Repository navigation
Conversation
TomChv
force-pushed
the
fix/legacy-client-rm-prunes
branch
from
October 13, 2026 15:54
0af3eb3 to
08ad7ba
Compare
On the `--legacy-runtime` layout `dagger module client rm` removed the record from dagger.toml and nothing else. withClientDependencies upserted and never pruned, so the module kept its `[[dependencies]]` entry — which kept the target in the module's schema, so the bindings, the tsconfig alias and the compat shim's `dag.<target>()` all regenerated from it and the call went on working with nothing in the workspace config to say it should. A manifest may carry entries nobody here wrote, which is why pruning everything missing from `clients` was never an option. What separates the two was already on disk: codegen renders a serve hook into a client it is handed as a target and never into one for a manifest dependency the engine already serves, so a `sdk/<name>.gen.ts` carrying one is the last run's record of a client. Absent from `clients` now, it has been removed, and its dependency comes off the manifest — everything derived from it then goes with the existing pruning. Fixes #92 Signed-off-by: Tom Chauveau <tom@dagger.io>
TomChv
force-pushed
the
fix/legacy-client-rm-prunes
branch
from
October 14, 2026 16:10
08ad7ba to
35f4a1e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On the
--legacy-runtimelayout,dagger module client rmremoved the record fromdagger.tomland nothing else. Afterclient rm ./dep:app/dagger-module.tomlkept[[dependencies]] name = "dep"app/tsconfig.jsonkept the@dagger.io/deppath aliasapp/sdk/compat.gen.tsstill installeddag.dep()app/sdk/dep.gen.tswas not deleted — it was rewritten with the serve wiring strippedA following
dagger generatereportedno changes to apply, so the state was permanent: the user kept shipping a dependency they had removed, with nothing in the workspace config to show it. The entrypoint layout prunes all of this correctly, because it declares no[[dependencies]]at all.Worse than the code claimed.
typescript-sdk.dangsaid dropping a client "leaves its dependency behind, serving a module whose bindings are gone" — the bindings were not gone either, so there was no symptom to notice.Root cause
typescript-sdk.dang:765(withClientDependencies) upserted the scope's clients into the manifest and never pruned, on the stated grounds that a manifest may carry entries nobody here wrote — by hand, or migrated from a pre-1.0dagger.json— and that those are indistinguishable from the SDK's own.Everything else followed from the entry that stayed: the manifest is what a module's session serves, so the target stayed in the module's schema, and the bindings, the tsconfig alias and the compat shim are all rendered from that schema. Codegen emits
<module>.gen.tsper manifest dependency as well as per recorded target (helpers/codegen/generator/typescript/generator.go:146), which is why the client file was rewritten rather than pruned — the post-rmtree is the correct rendering of a module that declaresdepas a dependency.Fix
Option 1 of the three on the table: tell the two apart, and prune only ours. The distinction the docstring said was impossible is already recorded on disk, by codegen:
A client the scope records as a target is handed to codegen in
meta.jsonand gets an__serveModulehook; a plain manifest dependency, which the engine already serves into the session, never does. So asdk/<name>.gen.tscarrying that hook is the previous run's record that<name>was a client. Absent fromclientsnow, it has been removed, and its[[dependencies]]entry is the SDK's to take (departedClients).Reading the previous run's output is also what bounds the claim: the run that acts on the hook is the run that rewrites the file without it, so an entry can only ever look like a departed client once. Every later run sees an ordinary manifest dependency and leaves it alone — the same as one the module never had a client for.
Removing the manifest entry is the whole fix: the module's schema then loses the target, so its bindings, its tsconfig alias and its compat entry are no longer rendered and the existing pruning (
prunedBindings,syncModuleAliases) takes them on the same run. No new pruning paths.withoutLegacyRuntimeDependencyis a no-op for a name no[[dependencies]]declares (verified againstsdk-helpers@v1.0.6), so the module's own client — which carries a hook and is never inclients— needs no special case.Verification
Engine
v1.0.0-beta.17, through a scratch workspace built from.dagger/modules/engine-e2e.Red without the fix. The same branch with
typescript-sdk.dangrestored frommain:Green with it —
client:*plus the three neighbours asked for:and, in a run of their own, the new check beside the counterpart it mirrors:
The two checks are the two halves of the decision:
client:remove-module-client-prunes-dependency-checkrecords a client for a[runtime]module, regenerates with the complete desired set no longer holding it, and asserts the manifest entry, the bindings, the tsconfig alias and the compat shim'sdag.<target>()all go.generate:generate-dependency-bindings-checkgains the counterpart that must not break: thegenerate-deps/appfixture's[[dependencies]] gendepwas written by hand and never recorded as a client, so a generation that sees its client file on disk must leave the entry alone. The planted file stands in for a previous run's, the way the neighbouring stale-bindings check plants one — without one on disk the run is not deciding anything. It also pins the invariant the fix reads: a manifest dependency's client carries no__serveModule.Not covered
sdk/is gitignored or otherwise absent has no record to read, so its departed client's dependency stays — today's behaviour, unchanged. The next generation with the bindings present does not recover it either: the file it would read is written without the hook. Graceful, but it is a limit.generateScopeboundary the engine calls, not throughdagger module client rm; the issue's own repro established the CLI half on beta.16.[[dependencies]], andwithClientDependenciesis skipped there.Fixes #92