hack/designs/workspace-foundation-compat.md
workspace-plumbing)This branch exists to spin out the fast-trackable part of workspace into a separate
PR.
The target is not "zero UI". The target is "no new primary workspace UX":
This document is the running source of truth for the branch. Update it after every substantive change so an interruption can be resumed from the filesystem alone.
The Workspace API path-contract rollout is complete. Its temporary tracker is archived
in
workspace-api-rollout-tracker.md.
The canonical contract itself currently lives in the workspace PR description.
When workspace is eventually rebased onto workspace-plumbing, preserve the
verified behavior, not the current patch shape or commit topology.
Local fix threads that must be reconciled explicitly:
cd4c63ff1 engine: preserve legacy toolchain customizations
defaultPath,
defaultAddress, and ignore through the same compat/runtime pathTestToolchain/TestToolchainsWithConfiguration/override_constructor_defaultPath_argumentfec23805b core: restore legacy blueprint caller env defaults
.env propagation for legacy
blueprints/toolchains before ModuleSource.asModule()TestUserDefaults/TestLocalBlueprint/inner_envfile6c93ddba9 core/schema: restore legacy generator include matching
generate-* matching on the workspace pathTestGenerators/TestGeneratorsDirectSDK/java/generate_multiple
and TestGenerators/TestGeneratorsAsBlueprint/java/generate1bd6065c7 engine: seed runtime module content cache
_contextDirectoryCommits that are bookkeeping, not behavior:
a35930cd5, f972163b3, 1025bf8d1
Commits likely to be partially or wholly superseded by the upstream entrypoint module rework:
defaultModule, call, functions, or shell routingMandatory reruns after the later workspace rebase:
TestToolchain/TestToolchainsWithConfiguration/override_constructor_defaultPath_argumentTestUserDefaults/TestLocalBlueprint/inner_envfileTestGenerators/TestGeneratorsDirectSDK/java/generate_multipleTestGenerators/TestGeneratorsAsBlueprint/java/generateworkspace-plumbing adopts the Workspace API path contract defined in workspace
PR #11812.
For this branch, that means:
. and relative paths are resolved from the workspace directory/ and absolute paths are resolved from the workspace boundaryws.path and ws.address are the intended public metadata surfacews.root should be reintroduced heredefaultPath behaviorCurrent implementation status:
workspace:
5e0b1e4a7 workspace: adopt path contractworkspace-plumbing:
bc8d8668e workspace: adopt path contractstrings import in
workspace.goworkspace-branch-only config-state references from
session.godagger generate -y ... functions and trimmed back to the
Workspace path-contract-related files only/ sandbox bug was fixed in bae60c5b2
(engine: fix sandboxing under root slash)defaultPath, defaultAddress, ignore) being dropped
during compat loading; the fix must stay inside the existing
workspace/session loader pathThe canonical entrypoint-module design now lives in the workspace PR
description for #11812.
Design target:
defaultModule logic, is the source
of truthImplications for workspace-plumbing:
Workspace.defaultModule and CLI-side prefixing are temporary workarounds,
not target behaviorcall, functions, shell, and related
defaultModule behavior) that are likely to be superseded by the upstream
entrypoint-module workThe earlier lockfile split candidate was technically possible only for boring substrate. The meaningful behavior was still workspace-bound, so it was not a good fast-track carveout.
This split is better because it tells one coherent story:
dagger.json projects.dagger call/functions/check/generate flows keep working from the current
directory.This is closer to the earlier workspace API carveout: land the runtime foundation first, then land the product UX on top.
The lockfile now has a clearer split too:
.dagger/lockmodules.resolvecontainer.fromKeep:
dagger.jsondagger.json pins during compat loading, without
creating .dagger/lockcall/functions/check/generate flows keep workingDefer:
.dagger/lock support, including modules.resolve read/write
behavior and migration-generated lockfiles--workdir repurposing/hiding and related targeting semanticsDefer:
container.fromworkspace-plumbing is the runtime model:
dagger.json is adapted into generic module-loading inputsworkspace-porcelain is the author-intent model:
Generic lockfile consumers remain follow-up work. They do not define the workspace product boundary.
The main seam for this split is connect-time module loading in
engine/server/session.go.
Legacy support in PR A is an adapter, not a parallel product model.
This keeps legacy compatibility localized instead of turning it into a second set of semantics spread across the codebase
For legacy dagger.json projects, the design target is:
That converted module set should include:
sdk / sourceImportant constraint:
Current design debt:
workspace and workspace-plumbing currently still use the split shortcutHistory note:
workspace briefly had the cleaner detect-time conversion shape in
4e92b04b0 and a7982a72bdab41cfbeThis shortcut is now considered temporary implementation debt, not target behavior.
dagger migrate, if kept, stays explicit..dagger/lock in a user's repo.dagger.json pins directly from compat parsing rather
than routing them through the workspace lockfile..dagger/config.toml runtime support and dagger migrate in the same PR.
Splitting those apart would make migration self-contradictory..dagger/config.toml and dagger migrate are fully hidden until the follow-up
PR, it is coherent to move both out of the plumbing PR together.dagger.json compat plus
current-CWD workspace bones, while the porcelain PR adds initialized-workspace
support (.dagger/config.toml), dagger migrate, and all explicit workspace
authoring/targeting UX.PR A is successful when:
call / functions / check / generate flows stay
coherent in standalone, workspace-shaped, and eligible legacy reposdagger.json pins are honored directly without introducing
repo-authored .dagger/lockThe highest-value coverage for this branch is:
.dagger/lockcall / functions / check / generate behaviorworkspaceThe branch still keeps the compat work already landed on workspace:
dagger.json projects load without a migration gate| Legacy feature | PR A load-time behavior | PR B migration behavior |
|---|---|---|
toolchains[] | Extract as workspace-level modules | Convert to .dagger/config.toml entries |
source != "." | Keep working through module loading | Relocate to .dagger/modules/ |
pin on blueprint/toolchain refs | Pass directly to module loading in compat mode | Convert to .dagger/lock modules.resolve entries |
The working rollback boundary is:
Mechanically, the first rollback pass is concentrated in cmd/dagger/:
main.gomodule.goworkspace.gocall.gofunctions.gochecks.gogenerators.goworkspace_target_args.gomodule_inspect.goRecovered context from terminal scrollback:
dagger migrate should stay in the spinout. Silent project rewriting should not.workspace-foundation-compat from the workspace tip and started mapping the CLI
rollback against main.At interruption time, the next concrete step was:
cmd/dagger/ against mainworkspace-foundation-compat89af48434Implemented the first CLI rollback pass to match the branch contract:
call, functions, check, and
generatedagger migrate as the explicit compat escape hatchinit/install/update semanticsConcrete code changes in this pass:
cmd/dagger/workspace.gocmd/dagger/migrate.gocmd/dagger/workspace_target_args.goinit, install, update, uninstall, develop, migrateIntentional limitation in this pass:
init/update without the old blueprint-specific behavior.
On the current workspace branch, the old ModuleSource.WithBlueprint and
WithUpdateBlueprint API is no longer available. Rather than re-inventing a new
blueprint authoring path in this fast-track branch, this pass keeps standalone
module authoring and defers blueprint-specific authoring UX.Verification after this pass:
go build ./cmd/dagger passesgo test -run 'TestOriginToPath|TestParseGit' ./cmd/dagger passesFollow-up fix in the same pass:
cmd/dagger/suite_test.go no longer imports internal/testutilcmd/dagger tests stop importing
core through internal/testutil/query.goParseTargetArgs explicit-target plumbing was removed from
functions.goCurrent verification caveat:
go test ./cmd/dagger no longer dies immediately at compile time, but it is a
slower-running CLI test target and was not used as the blocking verification signal
for this checkpointCurrent blocker / next step:
workspace, explicit workspace targets, and workspace-first authoring flowsRe-evaluated the .dagger/config.toml boundary under a stronger rollout assumption:
if PR A hides initialized-workspace support and dagger migrate from all users, then
there are no external repos in the wild depending on .dagger/config.toml between
PR A and PR B.
That changes the calculus:
workspace.Detect can return an uninitialized workspace, then
detectAndLoadWorkspaceWithRootfs extracts legacy toolchains/blueprints from nearby
dagger.json and still sets a default module for current-directory execution.dagger/config.toml parsing and
dagger migrate together into PR BIf we adopt that narrower split, this branch needs one more rollback pass:
.dagger/config.toml support from the user-facing plumbing storydagger migrate out of PR A with itdagger.json compat and current-directory workspace loadingConcrete rollback set for that pass:
cmd/dagger/main.go: stop registering migratecmd/dagger/migrate.go: remove from PR Acore/workspace/detect.go: stop treating .dagger/config.toml as an initialized
workspace marker; detect only current-CWD workspace bones needed for compat/runtimeengine/server/session.go: remove the ws.Config module-loading path and keep only
legacy toolchains, legacy blueprint, and the implicit CWD moduleengine/server/session.go: stop telling users to run dagger migrate in compat
warnings during PR Acore/workspace.go: drop config-facing workspace fields from the public object if
they are only meaningful for initialized workspacescore/schema/workspace.go: keep currentWorkspace, directory, file, findUp,
checks, and generators; defer config-backed/mutating methods
(init, install, moduleInit, configRead, configWrite, update)Yes, but only if the goal is review and landing parallelism for the runtime bones, not early exposure of initialized-workspace behavior.
Why it can still be worth it:
When it stops being worth it:
.dagger/config.toml and migrate is nearly as
large as just finishing the full workspace PRCurrent judgment:
To make sure nothing falls through the cracks, every workspace-branch change must be placed in exactly one bucket:
No unlabeled code paths, commands, tests, or docs.
Practical guardrails:
Initial deferred inventory for PR B:
.dagger/config.tomldagger migrate.dagger/lock substrate and serializationmodules.resolve lockfile read/write behaviorinit / install / update / config)Initial deferred inventory for PR C:
container.fromApplied the narrower PR A rollback to exclude initialized-workspace support and
dagger migrate, while keeping legacy dagger.json compat and current-CWD
workspace execution.
Concrete code changes in this pass:
core/workspace/detect.go no longer reads or parses .dagger/config.toml
during detection; it now detects workspace bones from .git or falls back to
the current directoryengine/server/session.go no longer loads modules from ws.Config; the
pending workspace module set now comes only from:
dagger.jsondagger.jsonengine/server/session.go no longer warns users to run dagger migratecmd/dagger/main.go no longer registers migratecore/schema/workspace.go no longer registers config-backed or mutating
workspace methods (init, install, moduleInit, configRead,
configWrite, update)core/workspace/config.go was removed from PR Acore/workspace/migrate.go and core/workspace/migrate_test.go were removed
from PR ASupporting cleanup:
core/workspace.go keeps initialized-workspace fields only as internal state
so deferred helpers still compile; they are no longer part of the public
GraphQL surface for this splitcore/workspace/detect_test.go now tests the no-config detection pathIntentional limitation of this pass:
Verification after this pass:
go test ./core/workspace passesgo test -run 'TestOriginToPath|TestParseGit|TestWorkspaceLoadLocation' ./cmd/dagger passesenv GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core/schema passesenv GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./engine/server passesenv GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core/integration passesVerification caveat:
go test ./core/schema / ./engine/server on this macOS host still
runs into the known Linux-only engine dependency/build-constraint mismatch, so
compile-only Linux-target checks were used therego test ./core/integration hits the same Linux-only engine
dependency/build-constraint mismatch on this macOS hostRemaining follow-up:
core/integration/workspace_test.go is now explicitly deferred from PR A via
a top-level t.Skip, because the suite is dominated by initialized-workspace
and migration scenariosdocs/current_docs/reference/cli/index.mdx, to remove workspace/migrate
surfaces that no longer exist in PR ANext doc step is to regenerate docs/current_docs/reference/cli/index.mdx from
the current branch command tree instead of hand-editing the stale generated file.
Confirmed generation path:
toolchains/cli-dev/main.go exposes reference(frontmatter, includeExperimental) and internally runs go run ./cmd/dagger genmigrate and the workspace management commandsRegenerated docs/current_docs/reference/cli/index.mdx from the current branch
command tree using the documented generator path:
dagger call -m ./toolchains/cli-dev reference --include-experimental --frontmatter=... export --path docs/current_docs/reference/cli/index.mdxConfirmed outcomes:
dagger workspace and dagger migrate are no longer documentedinit, install, and update now describe the restored
standalone/module behavior--workdir, and that matches the CLI:
installGlobalFlags still defines it in cmd/dagger/main.go but immediately
hides it with flags.MarkHidden("workdir")Result:
After the CLI reference was fixed, the next stale surface showed up in the generated API docs:
docs/docs-graphql/schema.graphqls still describes initialized-workspace
fields such as workspace init and config-backed state that PR A removedPlanned fix path:
toolchains/docs-dev as the narrowest supported generator entrypoint for
the generated GraphQL schema and static API referencereferences changeset before exporting it, so PR A only picks up
the generated doc updates that actually reflect the narrowed schemaRegenerated the generated GraphQL schema and static API reference to match the
post-rollback Workspace type:
toolchains/engine-dev graphql-schema directly to
docs/docs-graphql/schema.graphqls to confirm the exact schema delta firstdagger -s call -m ./toolchains/docs-dev references export --path .
so the generated API HTML caught up to the same schemaConfirmed outcomes:
docs/docs-graphql/schema.graphqls no longer documents
configPath, configRead, configWrite, hasConfig, init,
initialized, install, or moduleInit on Workspacedocs/static/api/reference/index.html dropped the same initialized-workspace
fields and now documents defaultModule insteadResult:
After fixing the generated GraphQL/API docs, the next consistency gap was in the generated clients:
Workspace methods such as configPath, configRead, configWrite,
hasConfig, initialized, and moduleInitdocs/static/reference/php still documented the
same stale methodsPlanned fix path:
Regenerated the schema-derived client surfaces after the Workspace rollback:
dagger -s call -m ./toolchains/go-sdk-dev generate export --path .dagger -s call -m ./toolchains/typescript-sdk-dev client-library export --path .dagger -s call -m ./toolchains/python-sdk-dev client-library export --path .dagger -s call -m ./toolchains/rust-sdk-dev apiclient export --path .dagger -s call -m ./toolchains/php-sdk-dev api export --path .Confirmed outcomes:
Workspace config/init/install mutation surface from PR BdefaultModule remains in the generated clients where expectedWorkspaceWorkspace methods in the
generated SDK or published reference surfaces; only unrelated uses of the word
"initialized" remainResult:
Post-regeneration verification:
go test ./... in sdk/go passedpython3 -m py_compile sdk/python/src/dagger/client/gen.py passedcargo check -p dagger-sdk --manifest-path sdk/rust/Cargo.toml passed
with one pre-existing dead-code warning in crates/dagger-sdk/src/core/session.rsyarn install and eslint --fix stepsVerification limitation:
php binary is available on this host, so there was no extra host-side
php -l lint step after the PHP toolchain runcmd/dagger Verification CleanupTightened two cmd/dagger tests so local PR-prep verification is not blocked by
host-environment drift unrelated to the workspace foundation changes.
Concrete fixes:
cmd/dagger/cloud_test.go now lets callers override HOME / XDG_CONFIG_HOME,
and TestCloudEngineUnauth uses a temp home so cached real cloud credentials do
not invalidate the unauthenticated expectationcmd/dagger/shell_completion_test.go now checks that the connected host
dagger engine exposes the schema features required by the embedded shell
introspection query (Function.sourceModuleName and
currentTypeDefs(includeCore:)); if the host install is older, the test skips
instead of failing as a false branch regressionVerification after these fixes:
go test ./cmd/dagger -run TestCloudEngineUnauth passesgo test ./cmd/dagger -run TestDaggerCMD/TestShellAutocomplete passesgo test ./cmd/dagger passesThe first Linux split-suite rerun on workspace-plumbing showed that the
workspace-focused bucket was already green, but the test-cli-engine and
test-call-and-shell buckets still contained pre-plumbing setup that invoked
dagger module init.
Concrete follow-up:
core/integration/engine_test.go now scaffolds version-compat and dagql-cache
scenarios with the branch's top-level dagger init commandcore/integration/module_call_test.go now uses dagger init consistently for
module setup, including host-side helpers and nested exec coverage, so the
TestCall bucket exercises argument handling instead of failing in setupVerification intent after this alignment:
dagger check test-split:test-cli-enginedagger check test-split:test-call-and-shellengine-dev test cases if either split still fails, to keep the
next checkpoint isolated and explainableAfter the init cleanup, the remaining red cases in the split-covered
integration suite were still using the old grouped dependency-management
commands.
Concrete follow-up:
core/integration/module_call_test.go now drives dependency setup through the
top-level dagger install command for local-path and remote-ref TestByName
coveragecore/integration/module_cli_test.go now uses top-level dagger install and
dagger update for the CLI scenarios that are part of the test-cli-engine
and test-call-and-shell bucketsVerification intent after this alignment:
TestCall/TestByNamedagger check test-split:test-call-and-shelldagger check test-split:test-cli-engine@ Path Init AlignmentThe last red case in the targeted TestCall/TestByName rerun was not a product
regression. It was one remaining pre-plumbing init call that still passed the
module name positionally instead of via --name.
Concrete follow-up:
core/integration/module_call_test.go now initializes the local ref with @
fixture with dagger init --name=mod-a test@test, which matches the current
top-level init [path] contract while preserving coverage for local module
paths that contain @Verification intent after this alignment:
TestCall/TestByName/local_ref_with_@dagger check test-split:test-call-and-shelldagger check test-split:test-cli-engineOnce the split definitions were checked directly, it was clear that
test-call-and-shell still exercised shared shell/module setup that had not
been updated by the earlier targeted TestCall fixes.
Concrete follow-up:
core/integration/module_test.go now builds generic module fixtures with the
current top-level dagger init --name=... [path] contract instead of the old
grouped dagger module init name path formcore/integration/module_shell_test.go now uses top-level dagger install
and dagger init --name=... in the direct TestShell setup that was still
bypassing the shared helperVerification intent after this alignment:
dagger check test-split:test-call-and-shellTestShell cases if the split still reports shell-specific
failurestest-cli-engine includes the full TestCLI suite, and that file was still
encoding the old grouped dagger module init surface across its init, develop,
install, and update coverage.
Concrete follow-up:
core/integration/module_cli_test.go now uses the branch's top-level
dagger init command consistently, while preserving the existing test
semantics around source-root inference, absolute paths, nested modules, and
develop/install flowsVerification intent after this alignment:
TestCLI under engine-dev testdagger check test-split:test-cli-enginedagger check test-split:test-call-and-shellOnce the stale setup churn was reduced, TestCall/TestErrNoModule was still
failing for a real CLI reason rather than an outdated test fixture. With no
default module selected, dagger call was reaching the synthetic Query
constructor and then attempting to subselect id, which produced a GraphQL
validation error instead of a user-facing CLI response.
Concrete follow-up:
cmd/dagger/functions.go now intercepts dagger call at the workspace root
when no function was selected and the current main object is Querymodule not found error again, which preserves the current integration
expectation for empty workdirsVerification intent after this guard:
TestCall/TestErrNoModuledagger check test-split:test-call-and-shellThe remaining TestDaggerDevelop failures were a real CLI regression rather
than stale split-suite expectations. dagger develop --sdk=... --source=...
was loading the module, applying WithSDK, and only then validating the
requested source path. After the workspace plumbing changes, WithSDK now
defaults an unset source path to the module root, so the later validation saw a
false conflict against "." and rejected legitimate first-time source
selection.
Concrete follow-up:
cmd/dagger/module.go now reads the module's configured source path before
applying WithSDKdevelop now validates --source against that configured value rather than
the post-WithSDK implicit defaultVerification intent after this fix:
TestCLI/TestDaggerDevelopdagger check test-split:test-cli-engineAfter the command-surface alignment and root-query guard, the remaining split red cases were all narrow expectation mismatches.
Concrete follow-up:
core/integration/module_cli_test.go no longer pre-sets the root module's
source path in the develop --source ... coverage, so those tests exercise
the engine's source-root rewriting instead of failing the preconditionfailed to install dependency)dagger functions coverage now asserts the current workspace
UX explicitly: an empty function table rather than a module not found error,
while normalizing the CLI's ANSI-styled header output firstcore/integration/module_call_test.go now treats dagger call conflict --help as a help-rendering path instead of expecting a flag-registration
failureVerification intent after this cleanup:
TestCLI/TestDaggerDevelopTestCLI/TestDaggerInstall/install_with_eager-runtimeTestCLI/TestCLIFunctionsTestCall/TestHelpdagger check test-split:test-cli-enginedagger check test-split:test-call-and-shellTo keep PR A scoped to workspace plumbing rather than deferred workspace porcelain, the branch now drops the skipped full-workspace integration suite instead of carrying it forward as dead weight.
Concrete follow-up:
core/integration/workspace_test.go from this branchcmd/dagger/module_inspect_test.go for workspace load-location
selectionDeferred coverage ledger for PR B restore:
Workspace and access workspace-root filesdagger migrate coverage for local and non-local sources, lock pins, and
summary outputfunctions/call behaviorVerification intent after this cleanup:
Verification after this cleanup:
go test ./cmd/dagger -run TestWorkspaceLoadLocation -count=1 passesdagger check test-split:test-cli-engine remains the
relevant broad integration check for the retained CLI surfaceRechecked the original workspace branch against the actual production
lockfile call sites instead of the generic API shape.
Concrete findings:
modules.resolvecontainer.fromdagger.json compat does not need .dagger/lock to honor pins
correctly; the compat path already parses blueprint/toolchain pins directly
from dagger.json and passes them into module loadingScope decision from that audit:
.dagger/lockmodules.resolve, and migrate-generated lockfilescontainer.fromImplemented the agreed PR A lockfile removal so the branch contract now matches the tree.
Concrete code changes in this pass:
--lock flag, client metadata propagation, and the old lock-mode testscore/workspace/lock.go, core/schema/lockfile.go, and util/lockfile/container.from behavior from PR ArefPin during session loading instead of routing through
.dagger/lock--lockResulting scope ledger:
dagger.json pin compat and no .dagger/lockcontainer.fromVerification after this pass:
go test ./cmd/dagger ./core/workspace passesenv GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core/schema ./engine/server ./core/integration passesdagger call engine-dev test --pkg=./engine/server --run='TestPendingLegacyModule|TestIsSameModuleReference|TestEnsureWorkspaceLoadedInheritsParentWorkspace|TestEnsureWorkspaceLoadedKeepsExistingWorkspaceBinding|TestWorkspaceBindingMode|TestParseWorkspaceRemoteRef|TestGatherModuleLoadRequests|TestModuleLoadErr' passesdagger check test-split:test-cli-engine passesdagger check test-split:test-call-and-shell passesRe-audited the preserved test changes with a stricter branch contract:
workspace-plumbing is supposed to preserve main behavior via workspace-aware
runtime loading, so test churn should default back to main unless a test is
specifically covering a new plumbing behavior.
Concrete follow-up in this pass:
core/integration/ suites to their main versions
instead of preserving workspace-branch command-surface rewritescore/integration/toolchain_test.go and
core/integration/workspace_test.go, which had been dropped from this branchScope rule after this audit:
main
implementation by defaultVerification intent after this pass:
cmd/dagger Test CleanupApplied the same audit rule to the remaining generic cmd/dagger test churn.
Concrete follow-up in this pass:
cmd/dagger/cloud_test.go,
cmd/dagger/shell_completion_test.go, and
cmd/dagger/suite_test.go to their main versionsVerification after this pass:
go test ./cmd/dagger ... on macOS still hits the pre-existing
engine/buildkit platform compile failureenv GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./cmd/dagger
passesAudited the original workspace test delta against the narrower
workspace-plumbing scope after restoring the generic suites to a main
baseline.
Concrete findings from that audit:
dagger migrate,
workspace config/update flows, and module-init behavior that depends on an
initialized workspaceworkspace diff:
workspace-aware function initialization, workspace load-location selection,
host find-up behavior, no-config workspace detection, legacy pin parsing,
workspace include matching, and session-side workspace binding/module loadingworkspace-only integration suite:
TestBlueprintFunctionsIncludesOtherModules in
core/integration/workspace_test.go, which exercises sibling workspace
module entrypoints under existing dagger functions / dagger call
commandsTestNestedModuleBeneathWorkspace from the same file is only a partial fit:
it covers nested standalone-module precedence and multi-module
functions/call behavior, but that area is still listed in the deferred
coverage ledger for the follow-up branchCurrent transplant stance:
While rerunning the retained plumbing unit tests, ./core was blocked by a
stale test mock rather than a runtime compat failure.
Concrete fix in this pass:
core/telemetry_test.go so its mockServer matches the current
core.Server interface after CurrentServedDeps switched from *ModDeps
to *ServedModsCurrentWorkspace stub required by the newer interfaceVerification after this pass:
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core
passes again./core plumbing test target can now proceed past package
build instead of failing in core/telemetry_test.goWhile sweeping non-integration tests, the Linux-backed ./core package exposed
one real runtime bug in the new changeset helper tests rather than a workspace
plumbing regression.
Root cause:
compareDirectories and directoriesAreIdentical shell out to
git diff --no-index.git file
for a worktree whose target is not mounted inside the container128
before honoring the --no-index comparison of the temp directoriesConcrete fix in this pass:
cmd.Dir to one of the compared directories so the
no-index diff no longer depends on the caller's cwdTestCompareDirectories_Integration so it explicitly runs from a
fake broken-worktree cwd; this keeps the regression covered instead of
relying on the repo checkout layout in the test containerVerification after this pass:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core --run='^TestCompareDirectories_Integration$' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./coreAfter the targeted fixes above, the non-integration sweep is down to one real remaining package failure plus a separate generation prerequisite bucket.
Green in this sweep:
auth, codegen templates, dagql, dagql/dagui,
engine, engine/client/drivers, engine/client/pathutil,
engine/clientdb, engine/filesync, engine/session/git,
engine/telemetry, engine/vcs, internal/buildkit/frontend/gateway/container,
internal/cloud/auth, util/*, and the other previously restored
root-module test dirs)cmd/dagger, cmd/engine, core,
core/schema, core/sdk, engine/buildkit, engine/serversdk/go, sdk/typescript/runtime/tsutils, toolchains/cli-dev/utilStill failing:
dagql/idtui
load workspace: ., load extra module: ..., and
ModuleSource.moduleName: String! where the existing goldens expected the
older standalone-module load pathGeneration prerequisite bucket, not counted as branch regressions:
sdk/python/runtimesdk/typescript/runtime root packagetoolchains/cli-dev root packageThose module roots import generated internal/dagger bindings that are
intentionally untracked until dagger develop -m <module> runs.
Verification after this sweep:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./cmd/daggerdagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./coredagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/schemadagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/sdkdagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./engine/servertar -cf - . | docker run --rm -i --entrypoint sh golang:1.26-alpine -lc 'mkdir /src && tar -xf - -C /src && cd /src && /usr/local/go/bin/go test ./cmd/engine ./engine/buildkit -count=1'The first concrete failure in dagger --progress=logs check test-split:test-base
is a test-only worktree isolation bug in util/gitutil.
Root cause:
util/gitutil/glob_test.go shells out to git ls-remote file://<tmpDir> as
a sanity check against Git's own globbing behavior.git
file that points to a gitdir path not mounted in the containerfatal: not a git repository: .../.git/worktrees/dagger_workspaceConcrete fix in this pass:
cmd.Dir = tmpDir for the sanity-check git ls-remote call so it runs
from the temporary repo it is validating instead of the outer worktreeVerification target for this pass:
env GOCACHE=/tmp/go-build go test ./util/gitutil -count=1test-base Remaining Failures and Fix PolicyAfter fixing the initial util/gitutil worktree leak, the remaining
test-base failures fall into two different buckets:
main tests that fail because this branch no longer exposes a
legacy authoring command or flag (init --blueprint, toolchain install)main tests that reach the current workspace-plumbing runtime path
and compute the wrong resultThis doc update records the current evidence only. No implementation changes are made in this pass.
Guardrails for the follow-up fixes:
main tests as the source of truth unless they are
demonstrably flaky or harness-onlyModuleSource.asModule()check / generate back to the old standalone-module pathmain does: as engine-backed ModuleSource
authoring operations whose generated context writes dagger.jsondagger.json editing and no CLI-owned
blueprint/toolchain resolution logicEvidence baseline for this section:
toolchains/cli-dev, so the detailed reproductions below were captured in
the main worktree after the worktree-specific git bug was fixedCurrent failure ledger:
cmd/codegen/generator/typescript/templates
test-base sliceenv GOCACHE=/tmp/go-build go test ./cmd/codegen/generator/typescript/templates -count=1env GOCACHE=/tmp/go-build go test ./cmd/codegen/generator/typescript/templates -count=5pendingcore/integration/TestBlueprint
TestBlueprint/TestBlueprintUseLocal/use_local_blueprintdagger init --blueprint=../helloError: unknown flag: --blueprintTestBlueprint/TestBlueprintInit/init_with_python_blueprint, which uses
the same restored main command shapeinit --blueprint
authoring surface that main exposes through engine-backed
ModuleSource.WithBlueprint(...)main:
ModuleSource.withBlueprint plus the generated-context export flowimplementation, engine-backed authoring onlycore/integration/TestChecks
TestChecks/TestChecksAsToolchain/typescriptdagger toolchain install ../hello-with-checks-tsError: unknown command or file "toolchain" for "dagger"TestToolchaintest-base slice also reported
TestChecks/TestChecksDirectSDK/javatoolchain ... authoring surface that main exposes through
engine-backed ModuleSource toolchain mutators and queriesmain:
withToolchains, withUpdateToolchains, withoutToolchains, and the
read-side toolchain query used by toolchain listtoolchain ... commands only as thin wrappers over that
engine surfaceTestChecksDirectSDK/java
before deciding whether there is also a separate runtime regression in
the direct-SDK pathTestChecksAsToolchain/*: implementation, engine-backed authoring onlyTestChecksDirectSDK/java: pending isolated reprocore/integration/TestToolchain
test-base reported at least:
TestToolchain/TestMultipleToolchains/install_multiple_toolchainsTestToolchain/TestToolchainsWithSDK/use_checks_with_sdk_that_have_a_constructor/gomain tests execute dagger toolchain install ...toolchain ... command surface
confirmed in TestChecks/TestChecksAsToolchain/typescriptimplementation, engine-backed authoring onlyFor the missing-command failures (init --blueprint, toolchain ...), the
implementation strategy is now locked to the same architectural shape used on
main.
Concrete plan:
ModuleSource authoring/query surface in
core/schema/modulesource.go rather than teaching cmd/dagger to edit
dagger.jsonsdk/gotoolchain command family in cmd/dagger only as
thin wrappers over those engine operationscheck / generate and all runtime loading on the existing
workspace/session pathGuardrails for this implementation:
dagger.json edits in the CLIModuleSource.asModule()check / generate to standalone-module loadingmain, prefer that
over inventing a branch-specific adaptationReview checkpoint:
git diff origin/main -- on the
touched files and confirm the delta shrank rather than grewCategory 1 is now implemented without changing the runtime design.
What changed:
core/schema/modulesource.go
withBlueprint, withUpdateBlueprint, withoutBlueprintwithToolchains, withUpdateToolchains,
withoutToolchainsloadModuleSourceConfig(...)ModuleSource data model fields and digest inputs
in core/modulesource.gosdk/gocmd/dagger only as thin wrappers over those
engine operations
dagger init --blueprintdagger toolchain install|update|uninstall|listGuardrails honored in this batch:
dagger.json edits in the CLIModuleSource.asModule()check / generate away from workspace traversalTargeted verification after the restore:
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./cmd/daggerenv GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core/schemadagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestChecks/TestChecksAsToolchain/typescript' --test-verbose
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestBlueprint/TestBlueprintUseLocal/use_local_blueprint' --test-verbose
init --blueprint succeeds; failure moved to runtime compatdagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestToolchain/TestMultipleToolchains/install_multiple_toolchains' --test-verbose
toolchain install succeeds; failure moved to runtime compatFailure reclassification after the restore:
core/integration/TestChecks/TestChecksAsToolchain/typescript
toolchain commandcore/integration/TestBlueprint/TestBlueprintUseLocal/use_local_blueprint
--blueprint flagload contextual arg "config": load legacy default file "./app-config.txt": workspace file "./app-config.txt": path "app" resolves outside root "/"core/integration/TestToolchain/TestMultipleToolchains/install_multiple_toolchains
toolchain install commandload contextual arg "config": load legacy default file "./app-config.txt": workspace file "./app-config.txt": path "app" resolves outside root "/"Diff-to-main checkpoint for the restored authoring files:
5 files changed, 327 insertions(+), 1617 deletions(-)5 files changed, 367 insertions(+), 853 deletions(-)Conclusion:
category 1 now converges toward main instead of diverging from it
the remaining blueprint/toolchain failures are no longer missing-command failures; they have moved into the runtime-compat bucket below
core/integration/TestUserDefaults
TestUserDefaults/TestLocalBlueprint/inner_envfile, is now fixed locally;
see the later ledger update belowTestUserDefaults/TestLocalBlueprint/outer_envfile_outer_workdirTestUserDefaults/TestLocalToolchain/outer_envfile_outer_workdirunknown command "message" for "dagger call"unknown command "defaults" for "dagger call"dagger -m ./app call ... command-resolution failures.env compat fix in the existing
workspace/session/legacy loading pathModuleSource.asModule()partial implementationcore/integration/TestGenerators
TestGenerators/TestGeneratorsDirectSDK/java/generate_multipleno changes to applyno changes to applycurrentWorkspaceWorkspace.generators(include: ["generate-*"]) compares the include glob
against workspace-qualified names such as
hello-with-generators-java/generate-files"hello-with-generators-java/generate-files".Glob("generate-*") -> no match[generate-*].Equals([hello-with-generators-java]): "generate" != "helloWithGeneratorsJava" -> NOT EQUALtest-base also reported TestGenerators/TestGeneratorsAsToolchain/gomain
tests expectgenerate on the workspace pathmain patterns such as generate-* still match in the
single-module casecore/schema/workspace.gogenerate-* without
broadening true multi-module workspace matchingTestGenerators/TestGeneratorsAsToolchain/go: passesdagger --progress=plain call -m ./toolchains/go test --pkgs=./core/schema --run='TestMatchWorkspaceInclude|TestFilterGeneratorsByInclude|TestResolveWorkspacePath|TestWorkspaceAPIPath'dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsDirectSDK/java/generate_multiple' --test-verbosedagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsAsBlueprint/java/generate' --test-verboseimplemented + verifiedVerification used to build this ledger:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestBlueprint/TestBlueprintUseLocal/use_local_blueprint' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestChecks/TestChecksAsToolchain/typescript' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalBlueprint/inner_envfile' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalBlueprint' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalToolchain/inner_envfile' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalToolchain/outer_envfile_outer_workdir' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsDirectSDK/java/generate_multiple' --test-verbosedagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsAsToolchain/go' --test-verbosedagger --progress=plain call -m ./toolchains/go test --pkgs=./core/schema --run='TestMatchWorkspaceInclude|TestFilterGeneratorsByInclude|TestResolveWorkspacePath|TestWorkspaceAPIPath'dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsDirectSDK/java/generate_multiple' --test-verbosedagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsAsBlueprint/java/generate' --test-verboseenv GOCACHE=/tmp/go-build go test ./cmd/codegen/generator/typescript/templates -count=1env GOCACHE=/tmp/go-build go test ./cmd/codegen/generator/typescript/templates -count=5After the root-/ sandbox fix in bae60c5b2, the next remaining targeted
toolchain failure narrowed to:
core/integration/TestToolchain/TestToolchainsWithConfiguration/override constructor defaultPath argumentKnown cause:
ConfigDefaults, but not the broader legacy toolchain customizations needed
to restore defaultPath, defaultAddress, and ignoreCleanup done before touching runtime:
legacy.go now reuses the authoritative modules.ModuleConfig schema instead
of maintaining a private dagger.json JSON shape
8126a7a94Current implementation for the runtime fix:
pendingModule carries ArgCustomizationspendingLegacyModule(...)resolveModule(...) applies them onto the already-loaded module typedefscore/module.go as
ApplyLegacyCustomizationsToTypeDefs(customizations)core.Module; they are applied during
load and discardedGuardrails preserved in this batch:
ModuleSource.asModule()check / generate away from workspace traversaldagger.json mutationcore.ModuleSupporting test-only cleanup:
core/integration/workspace_test.go had stale helper usage from the earlier
command/path-contract resetsd6a7b60fb (test: fix workspace helper drift)Focused verification for the in-flight runtime patch:
env GOCACHE=/tmp/go-build go test ./core/workspace -run TestParseLegacyPins -count=1
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core/workspace ./engine/server ./core ./core/integration
core/module_legacy_test.go for:
defaultPath + ignoreValidation gap still open at this checkpoint:
go test ./core and go test ./engine/server remains
blocked on this Darwin host by the existing Linux-only engine/buildkit and
overlay snapshotter filesengine-dev harness remains too opaque/slow to treat as a quick
validation loop for this one targeted fixCurrent stance:
The targeted rerun for the legacy toolchain customization patch is now green:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestToolchain/TestToolchainsWithConfiguration/override_constructor_defaultPath_argument' --test-verbose
defaultPath loading correctly through the existing workspace/session pathScope consequence after that rerun:
.env /
default-propagation failure in legacy blueprint/toolchain compat loadingParallel-work coordination rule:
workspace branch is still landing the upstream schema-level entrypoint
module changecall, functions, shell, and related defaultModule expectationsworkspace-plumbing.env / default propagation through legacy compat loadingThe previously active blueprint default-file path failure is no longer reproducing.
Focused rerun:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestBlueprint/TestBlueprintUseLocal/use_local_blueprint' --test-verbose
What this means:
Workspace.file("./app-config.txt") failure under the legacy
blueprint path is no longer an active runtime issue on this branch.env /
user-default propagation and generator include matching.env Compat RestoredImplemented a focused compat fix in engine/server/session.go:
ModuleSource.asModule(), if that caller module has its own .env,
load it as an env file and merge it over the legacy blueprint module-source
defaultsFocused verification:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalBlueprint/inner_envfile' --test-verbose
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalBlueprint' --test-verbose
inner_envfile: passesouter_envfile_inner_workdir: passesouter_envfile_outer_workdir: still fails, but now as
unknown command "message" for "dagger call"dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalToolchain/inner_envfile' --test-verbose
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/TestLocalToolchain/outer_envfile_outer_workdir' --test-verbose
unknown command "defaults" for "dagger call"Scope consequence:
.env / user-default propagation gap is
fixed for the legacy blueprint inner_envfile pathdagger -m ./app call ... command-resolution failures, so treat them as
entrypoint-sensitive hold items pending the upstream schema-level
entrypoint-module cherry-pickImplemented a focused workspace-side compat fix in core/schema/workspace.go:
check and other workspace
grouping behavior stay unchangedFocused verification:
dagger --progress=logs call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsAsToolchain/go' --test-verbosedagger --progress=plain call -m ./toolchains/go test --pkgs=./core/schema --run='TestMatchWorkspaceInclude|TestFilterGeneratorsByInclude|TestResolveWorkspacePath|TestWorkspaceAPIPath'dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsDirectSDK/java/generate_multiple' --test-verbosedagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsAsBlueprint/java/generate' --test-verboseScope consequence:
generate-* without reverting generate to the standalone
module path.env propagation fixed and generator matching implemented, the
remaining previously reproduced failures on the list are entrypoint-sensitive
hold items rather than active non-entrypoint runtime regressionsThe generator rerun block turned out not to be a generator-matching problem
after all. It was a runtime client cache gap in engine/server/session.go:
+defaultPath directories resolve through
_contextDirectory_contextDirectory rehydrates the originating module from the
content-digest cache before calling LoadContextDirEncodedModuleID were loading client.mod, but
not seeding that same content-digest cache in the new client serverengineDev.source.entries work, while later
nested dependency calls such as dag.Go(...).Binary("./cmd/codegen") failed
trying to reload the same contextual directoryImplemented fix:
client.mod from EncodedModuleID, immediately call
core.CacheModuleByContentDigest(...) for the current runtime clientFocused verification:
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsDirectSDK/java/generate_multiple' --test-verbose
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestGenerators/TestGeneratorsAsBlueprint/java/generate' --test-verbose
Scope consequence:
toolchains/engine-dev self-context failure loading
bin/codegen is gone for the reproduced generator pathsCherry-picked the upstream schema-level entrypoint change from workspace:
bbdfa797e workspace: move entrypoints to Query rootBranch-specific conflict resolution kept the workspace-plumbing scope
intact:
call / functionscmd/dagger/call.go, cmd/dagger/functions.go, and
cmd/dagger/module_inspect.gocore.Module.InstallOpts now carries Entrypointcore/served_mods.go preserves entrypoint install policy through module
dedupe/promotioncore/object.go installs non-conflicting Query-root proxies for the
workspace entrypoint module's main-object methodsengine/server/session.go threads entrypoint install policy through the
existing workspace/session loader path.env defaultsworkspace_test.go tail back to PR A scope instead of
re-importing the deferred initialized-workspace/config/migrate suite
TestBlueprintFunctionsIncludesOtherModulesTestEntrypointProxySkipsRootFieldConflictsTestEntrypointProxySkipsConstructorArgConflictsTestNestedModuleBeneathWorkspace deferred per the earlier transplant
auditFocused verification for this cherry-pick:
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./cmd/dagger
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./engine/server
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./core/integration
Validation caveats:
env GOCACHE=/tmp/go-build go test ./cmd/dagger -count=1 on this
Darwin host still fails in engine/buildkit on Linux-only unix.* symbols;
this is the same host/build-constraint class seen elsewhere on the branch,
not a targeted entrypoint regression signaltoolchains/engine-dev rerun for the three retained entrypoint
integration tests was started, but did not finish in a reasonable local
validation window; rerun that slice again before calling the entrypoint hold
bucket fully cleared:
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)' --test-verboseCurrent stance after this landing:
workspace-plumbingTakeover checkpoint before the next validation pass:
workspace-plumbing is at origin/workspace-plumbingcmd/dagger/module_inspect.gocmd/dagger/mcp.gocore/integration/user_defaults_test.gomodule_inspect.go and mcp.go are an unverified attempt to repair
explicit -m CLI focus after the Query-root entrypoint cherry-pickuser_defaults_test.go only adds nested stdout/stderr logging for the
existing user-default repros; it is debugging instrumentation, not a fixReran the exact combined hold-bucket command from the prior checkpoint:
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)' --test-verboseObserved local behavior:
EngineDev.test(run: "TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)", pkg: "./core/integration", testVerbose: true): VoidEngineDev.test(...), no further test output or completion
signal arrived within a normal local validation windowWorktree hygiene cleanup performed before continuing:
dagger ... engine-dev test runs:
TestWorkspace/(...) rerun aboveTestUserDefaults/TestLocalBlueprint/outer_envfile_outer_workdir
rerun from takeover auditTestUserDefaults/TestLocalToolchain/outer_envfile_outer_workdir
rerun from takeover auditLocked next step:
TestWorkspace/*
reruns under the same harness so the branch gets attributable results instead
of another opaque long-running aggregate slice-m or Query-root focus regression is still activeStarted the first split rerun under the same toolchains/engine-dev harness:
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestWorkspace/TestBlueprintFunctionsIncludesOtherModules' --test-verboseObserved behavior:
load module: ./toolchains/engine-dev: completeEngineDev.test(run: "TestWorkspace/TestBlueprintFunctionsIncludesOtherModules", pkg: "./core/integration", testVerbose: true): VoidEngineDev.test(...), there was still no attributable test
output before the run was stoppedConclusion from this pass:
toolchains/engine-dev test path is still too opaque here when left on its
default 30m Go test timeoutLocked next step:
TestWorkspace/* case individually with an explicit
shorter timeout argument to the same EngineDev.test(...) surface so a
hang turns into a stack-dumped failure instead of another silent long waitEngineDev.test Rerun Still Failed To Surface OutputRetried the first retained entrypoint test through the same harness with an explicit shorter timeout:
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestWorkspace/TestBlueprintFunctionsIncludesOtherModules' --timeout=3m --test-verboseObserved behavior:
EngineDev.test(run: "TestWorkspace/TestBlueprintFunctionsIncludesOtherModules", pkg: "./core/integration", timeout: "3m", testVerbose: true): Voiddagger call stayed alive beyond the expected 3m
test window without surfacing a pass, a timeout failure, or any test outputConclusion from this pass:
toolchains/engine-dev test is currently too opaque
to use as the first-line validation surface for the retained entrypoint
bucket, even after threading a shorter Go test timeoutLocked next step:
TestWorkspace/* cases with native go test from the
repo worktree, using a short explicit timeout, to get attributable output
before deciding whether any of the current local CLI WIP is warrantedtoolchains/engine-dev surface again if native
go test proves non-viable on this hostgo test Blocked On Darwin Host Build ConstraintsTried the first retained entrypoint test natively from the repo worktree:
env GOCACHE=/tmp/go-build go test ./core/integration -run 'TestWorkspace/TestBlueprintFunctionsIncludesOtherModules' -count=1 -v -timeout=3mObserved result:
engine/buildkit on Linux-only unix.* symbols:
undefined: unix.OpenTreeundefined: unix.OPEN_TREE_CLONEundefined: unix.OPEN_TREE_CLOEXECundefined: unix.AT_RECURSIVEundefined: unix.Unshareundefined: unix.CLONE_FSundefined: unix.SetnsTestWorkspace/* slice therefore cannot be validated natively on
this Darwin hostScope consequence:
go test is not viable here./core/integration tooLocked next step:
EngineDev.test
wrapper onto a more direct local-playground/container execution path that can
run go test against this branch's source while surfacing the inner test
output directlyExploration after the native-host failure:
engine-dev as a root module dependency
(dagger.json includes "name": "engine-dev", "source": "toolchains/engine-dev")engine-dev playground returns a plain Container, with the
expected container mutation surface available from the CLI:
with-directorywith-mounted-directorywith-execstdout / stderr / combined-outputengine-dev-testing skill still recommends
playground as the manual validation layer, with a wrapper script that adds:
What did not work well enough directly:
dagger call -m ./toolchains/engine-dev playground ... probes remained
too silent/long-running to use directly as a reliable handoff-safe validation
surfaceplayground primitive,
the missing pieceLocked next step:
engine-dev-testing/with-playground.sh
but pointed at this branch checkout for src/dagger instead of the remote
sample repoTestWorkspace/* case at a time
with direct go test output and an outer watchdog timeoutConcrete command selected for the first retained test:
dagger --progress=logs call -m ./toolchains/engine-dev playground --shared-cache with-mounted-directory --path=/src/dagger --source=. with-workdir --path=/src/dagger with-exec --args=sh --args=-lc --args='env GOCACHE=/tmp/go-build go test ./core/integration -run "^TestWorkspace/TestBlueprintFunctionsIncludesOtherModules$" -count=1 -v -timeout=3m' combined-outputWhy this is the chosen next harness:
toolchains/engine-dev/test.go runs the inner test with
WithExec(args).Sync(ctx), so it does not expose the inner go test
stdout/stderr directlyplayground returns a plain Container, so ending the chain with
combined-output should expose the actual inner go test output from the
same Linux-native dev-engine environmentRan the first retained test with direct playground + combined-output:
dagger --progress=logs call -m ./toolchains/engine-dev playground --shared-cache with-mounted-directory --path=/src/dagger --source=. with-workdir --path=/src/dagger with-exec --args=sh --args=-lc --args='env GOCACHE=/tmp/go-build go test ./core/integration -run "^TestWorkspace/TestBlueprintFunctionsIncludesOtherModules$" -count=1 -v -timeout=3m' combined-outputObserved behavior:
dagger call remained alive beyond the inner go test -timeout=3m
windowgo test stdout/stderr had been surfaced yet when the process-state
check was takenEngineDev.test(...) to raw playground was not
sufficient by itself to make the validation pass handoff-safeScope consequence:
engine-dev-testing/with-playground.sh:
playground path still appears to be the right execution
layer, but not as a naked synchronous dagger callLocked next step:
engine-dev-testing/with-playground.sh
that:
engine-dev playground from this branchsrc/daggergo test command to /tmp/inner.shTestWorkspace/TestBlueprintFunctionsIncludesOtherModulesRan the wrapped local-playground harness for the first retained test. The wrapper behaved as intended operationally:
Concrete result:
go test output was reachedsource=. directory argument for the mounted repo:
✘ parsing command line arguments 5m31s ERRORfailed to get value for argument "source": Post "http://dagger/query": read tcp 172.20.0.217:64869->64.6.38.39:444: read: operation timed outScope consequence:
Directory argument for the local
playground wrapper times out before the test even starts"Locked next step:
source=.toolchains/engine-dev source filter as the baseline
include/exclude set, then add only the extra repo paths needed for
go test ./core/integrationRetried the wrapped local-playground harness with a narrowed repo payload and a
trivial inner command (ls core/integration plus source-ok).
Observed result:
420s watchdog windowsource=. mount, it did not fail quickly during
argument parsing with a failed to get value for argument "source" timeout=== TIMEOUT: killed after 420s ===Scope consequence:
Directory-argument resolution failure--shared-cache, the next attempt has a reasonable
chance of benefiting from the warmed intermediate state even though this
probe itself timed outLocked next step:
TestWorkspace/* case on the same wrapped harness
with a longer outer watchdog windowgo test output, treat the
local-playground path itself as too expensive for this handoff window and
record that explicitly before considering any further validation-path changeReran the first retained test on the reduced-source wrapped harness with a
longer 900s outer watchdog.
Observed result:
go test stdout/stderr was surfaced before the watchdog fired=== TIMEOUT: killed after 900s ===Scope consequence:
playground path is too
expensive to serve as the primary immediate rerun loop for the retained
entrypoint bucketLocked next step:
Directory upload costs while still matching the current committed branch
behavior:
origin/workspace-plumbing or the equivalent GitHub ref as the source
payload for src/daggerRan the wrapped playground harness against remote workspace-plumbing branch
content for both:
-m github.com/dagger/dagger@workspace-plumbing)src/dagger sourceThis changed the validation picture materially:
Directory upload bottlenecksrc/dagger, and execute the inner scriptenv: can't execute 'go': No such file or directorywithExec sh /tmp/inner.sh exited 127What this means:
playground container does not include the Go toolchain needed to run
go test directly from src/daggerLocked next step:
workspace-plumbing playground pathTestWorkspace/TestBlueprintFunctionsIncludesOtherModules on that
adjusted remote harness before changing any branch behavior or classifying
the uncommitted local CLI WIPtoolchains/go Reached Real Test ExecutionInstead of trying to install go into the playground image directly, reran the
remote playground path with the inner script delegating to the repo's
toolchains/go module:
/src/dagger:
dagger --progress=plain call -m ./toolchains/go env with-exec --args=go --args=test --args=-v --args=-timeout=3m --args=-count=1 --args=-run=^TestWorkspace/TestBlueprintFunctionsIncludesOtherModules$ --args=./core/integration combined-outputThis is the closest the rerun has gotten to the actual retained test:
src/dagger mount stayed cachedtoolchains/go invocation ran for about 1m43s1, but now the remaining blocker is narrowed to
surfacing the actual failure text rather than getting to test execution at
allScope consequence:
toolchains/go path is now the best validation lane
for the retained entrypoint bucketLocked next step:
toolchains/go path with inner
dagger --progress=logs (or equivalent stderr capture) so the actual test or
build failure is visibletoolchains/go Exposed The First Real Test FailureReran the same remote playground + toolchains/go path with inner
dagger --progress=logs, which finally surfaced the actual failing test output.
Concrete test result:
TestWorkspace/TestBlueprintFunctionsIncludesOtherModules still did not reach
its entrypoint assertionsconnect(...):
failed to read session params: EOFstart engine: driver for scheme "image" was not availablecore/integration/suite_test.go:71core/integration/workspace_test.go:812What this means:
core/integration failure text obtained during the
hold-bucket rerun effortTestBlueprintFunctionsIncludesOtherModulestoolchains/go container changes the environment enough that the test
cannot start the engine it expectsScope consequence:
toolchains/go path is now proven useful for
observability, but not yet equivalent enough to the intended integration
environment for this bucketLocked next step:
playground base container so go test runs inside
the actual playground environment, orCurrent committed branch state:
bbdfa797e workspace: move entrypoints to Query rootCurrent uncommitted local WIP:
cmd/dagger/module_inspect.gocmd/dagger/mcp.gocore/integration/user_defaults_test.goCurrent classification of that WIP:
-m CLI focus to
the Query-root entrypoint designQueryCurrent blocker summary:
dagger to build and test this repodagger check -l test-splitdagger call engine-dev test --run=TestSomethingSpecificHeredagger playground for ad-hoc QA/manual commands, not for primary
integration-suite rerunsdagger --progress=plain call engine-dev test --pkg=./core/integration --run='TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)'https://dagger.cloud/dagger/traces/ba68601dbd7ba2f4fae511f47ac121c7engine-dev test note:
pkg to ./...--pkg=./core/integrationcmd/codegen/generator/typescript/templates panicked while loading a legacy
bare dagger dependency versionTestWorkspace/TestEntrypointProxySkipsRootFieldConflicts passesTestWorkspace/TestBlueprintFunctionsIncludesOtherModules still fails in:
dagger_functions_shows_all_modulesdagger_call_blueprint_functiondagger_call_sibling_module_functionquery_root_exposes_blueprint_entrypoint_methodsTestWorkspace/TestEntrypointProxySkipsConstructorArgConflicts still fails in:
namespaced_method_still_accepts_both_argscinew(prefix: String! = "ctor")echo(prefix: String! = "method")dagger call echo --prefix ... --prefix ... does not say which --prefix
belongs to the constructor vs the methoddagger call ci echodagger call ci --prefix ctor echo --prefix methoddagger call --prefix ctor ci echo --prefix methodQuery-root CLI parsing sees the leading
--prefix before it has descended into ci, so the flag has no ownercmd/dagger/functions.go traversal/flag ownership for the
namespaced constructor pathcore/object.go's proxy-skipping
behavior should be revertedcmd/dagger/module.go now treats repeated bare dagger init as a no-op
when the module already exists and no init-mutating flags were explicitly
requested; this removed an earlier workspace bootstrap blocker from the
direct rerun pathcmd/dagger/functions.go now rewrites leading constructor flags onto the
namespaced module constructor path when the workspace CLI root is Querydagger call --prefix ctor ci echo --prefix methoddagger call ci --prefix ctor echo --prefix methodctor:methoddagger --progress=plain call engine-dev test --pkg=./core/integration --run='TestWorkspace/TestEntrypointProxySkipsConstructorArgConflicts/namespaced_method_still_accepts_both_args' --test-verbose
https://dagger.cloud/dagger/traces/fdcce7fff92d111102e4a248f2a4b5f3dagger --progress=plain call engine-dev test --pkg=./core/integration --run='TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)' --test-verbose
https://dagger.cloud/dagger/traces/9bb1f6aa6a8acf126b0639ee675bf16fExplicit -m follow-up outcome under the same direct engine-dev test harness:
cmd/dagger/mcp.go / cmd/dagger/module_inspect.go WIP was not
sufficient by itself
TestUserDefaults/TestLocalBlueprintTestUserDefaults/TestLocalToolchaindagger -m ./app call messagedagger -m ./app call defaults message-m Query root did
not expose the wrapper app's related blueprint/toolchain entrypoints-m still arrives as connect-time module loading via
ExtraModules; that part remains correct-m path, engine/server/session.go was serving the
selected module plus type-only deps, but wrapper-app related modules live on
ModuleSource.Blueprint / ModuleSource.Toolchains, not in Deps./app could be loaded explicitly while still
omitting the related modules that actually contribute the visible Query-root
entrypoints-m as connect-time loading via ExtraModules; do not reintroduce a
second client-side targeting modelengine/server/session.go now resolves and serves the selected module's
related blueprint/toolchain modules on the explicit -m path before
serving the primary modulecmd/dagger/module_inspect.go now resolves the selected module name plus
visible related module names before CLI introspectioncmd/dagger/mcp.go now presents explicit -m as a filtered real
Query-root view based on those visible module names, instead of
refocusing MainObject to the module object or falling back to raw
unfiltered QueryQuery functions and the
selected module constructor proxy, while preserving namespaced constructor
and default behavior for explicit module accessuser_defaults_test.go logging was dropped before landingVerification after the explicit -m follow-up:
dagger --progress=plain call engine-dev test --pkg=./cmd/dagger --run='Test(WorkspaceLoadLocation|FocusRootModuleFunctions|RewriteQueryRootConstructorArgs)$' --test-verbose
https://dagger.cloud/dagger/traces/3da702adcba3a138eec8fea8bb65da7fdagger --progress=plain call engine-dev test --pkg=./core/integration --run='TestUserDefaults/(TestLocalBlueprint|TestLocalToolchain)' --test-verbose
https://dagger.cloud/dagger/traces/692e8d3ebb818920a1f72a6b0c7a90fa-m cases now succeed:
dagger -m ./app call messagedagger -m ./app call defaults messagedagger --progress=plain call engine-dev test --pkg=./core/integration --run='TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)' --test-verbose
https://dagger.cloud/dagger/traces/91c8c327c0fbef408564cee8c7521963-m engine changeReread the authoritative design text in:
Important design restatement:
QueryWorkspace.defaultModule and CLI-side prefixing are temporary workarounds,
not the target contractworkspace-plumbing) should not keep adding new client-specific
root-entrypoint repair logic on top of the schema-level designWhat had happened locally after the Query-root entrypoint cherry-pick:
2cc7e4573 added rewriteQueryRootConstructorArgs in
cmd/dagger/functions.go
a3ed6cb14 added an explicit--m focused-Query projection in
cmd/dagger/module_inspect.go and cmd/dagger/mcp.go
Query view client-side0fad6e504 added one more fallback on top of that synthetic view when no
proxied methods survived filteringWhy that local layer was wrong:
workspace-plumbing diverge from the schema-first design instead of
converging toward it-m
Query rootsWhat is retained from the explicit -m follow-up:
engine/server/session.go
-m still loads through ExtraModulesModuleSource.Blueprint and
ModuleSource.Toolchains, not only in DepsWhat is removed now:
cmd/dagger/module_inspect.go
initializeWorkspace no longer special-cases explicit -mcmd/dagger/functions.go
rewriteQueryRootConstructorArgsexecutecmd/dagger/mcp.go
focusRootModuleFunctionsworkspaceBehavioral consequence of this cleanup:
-m now follows the real schema againScope note:
workspace-plumbingworkspace itself, for example:
functions / help filtering on Query-root ownership metadatamcp fallback to CurrentWorkspace().DefaultModule() / auto-aliasworkspace itselfSupersession note:
-m follow-up" ledger entry above remains historically
useful for the engine-side diagnosis and retained engine/server/session.go
fixVerification after this cleanup:
env GOCACHE=/tmp/go-build GOOS=linux GOARCH=amd64 go test -c ./cmd/dagger
dagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestUserDefaults/(TestLocalBlueprint|TestLocalToolchain)' --test-verbose
https://dagger.cloud/dagger/traces/0db4655ed4a108a6943ab65d7bffc35eEngineDev.test(...), then stopped surfacing attributable output within the
local validation windowdagger --progress=plain call -m ./toolchains/engine-dev test --pkg=./core/integration --run='TestWorkspace/(TestBlueprintFunctionsIncludesOtherModules|TestEntrypointProxySkipsRootFieldConflicts|TestEntrypointProxySkipsConstructorArgConflicts)' --test-verbose
https://dagger.cloud/dagger/traces/6dcc985ce2da8db25f7a2fc61fe2945b-m engine fix still has the earlier historical pass
evidence recorded aboveEngineDev.test(...) surface, so do not overstate it as a rerun passFound one more harmless but misleading leftover from the original
workspace -> workspace-plumbing spinout:
cmd/dagger/module_inspect.go still exposed
initializeWorkspace(ctx, dag, workspaceRef *string)workspaceLoadLocation(...) still existed only to pretty-print that old
CLI-targeted workspace ref in tracingworkspace-plumbing caller already passed nilWhy remove it:
workspace-plumbing intentionally removed the user-facing explicit workspace
target UX from call / functionsworkspaceRef parameter in the CLI loader makes the branch
look like it still supports that old targeting path when it no longer doesworkspace rebase and accidentally reintroduce the wrong client contractWhat changed:
initializeWorkspace now just takes (ctx, dag)call.go, functions.go, and mcp.go use the simplified
signatureworkspaceLoadLocation(...) was deletedRebase guidance:
workspace onto workspace-plumbing, do not treat this
removed parameter as behavior to preserve hereworkspace branch still needs explicit user-facing workspace
targeting, that should come back as part of the real deferred workspace UX,
not as an inert leftover in PR A's CLI module-loading pathFound one more workspace-plumbing-only CLI behavior that muddies the design
boundary after entrypoint methods were projected onto Query:
cmd/dagger/functions.go hoisted constructor args from Query-root module
constructors onto the parent dagger call commandselectFuncdagger call --prefix ctor ci echo --prefix methoddagger call ci --prefix ctor echo --prefix methodWhy remove it:
What changed:
cmd/dagger/functions.goBehavioral consequence:
dagger call ci --prefix ctor echo --prefix methodinitializeWorkspace Errors In mcpFound one more workspace-plumbing CLI divergence from main that did not
carry its own design value:
cmd/dagger/mcp.go called initializeWorkspace(...) and discarded the
returned errorWhy remove it:
main already propagates real module-load errors in this pathmainWhat changed:
mcpStart now returns initializeWorkspace(...) errors directlyImportCallerHostDirAddressed the review note on engine/buildkit/filesync.go by deleting the
unused ImportCallerHostDir helper.
Why remove it:
main
and makes future review/rebase work noisierWhat changed:
ImportCallerHostDir from engine/buildkit/filesync.goThese are the expected user-visible breakages even without the follow-up porcelain.
call / functions / check / generate commands keep
the same names, but they now resolve through workspace loading instead of the old
standalone-module path.dagger functions can show sibling workspace module entrypoints in addition to the
focused module's functions.dagger check and dagger generate run against CurrentWorkspace() groups, so the
effective check/generator set can become broader than the old single-module view.dagger.json projects stop failing fast for migration and instead run in
compat mode with warnings.