From bb9391027fcf79b4ad4ef3ce33b93f6c43aac5b1 Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:17:00 +0200 Subject: [PATCH 1/9] e2e: resolve the fixture as-sdk paths against the fixture config The e2e fixtures declare their managed modules in .dagger/modules/e2e/fixtures/dagger.toml, and dagger/dagger b79115ac5 gave as-sdk paths the same resolver every other path in a dagger.toml already had: relative to the directory holding that config, with a leading "/" to anchor at the workspace root. The fixture still spelled them from the workspace root, so every entry resolved one fixture root deeper than the module it names and the managed-module list came back empty. Signed-off-by: Yves Brissaud --- .dagger/modules/e2e/fixtures/dagger.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.dagger/modules/e2e/fixtures/dagger.toml b/.dagger/modules/e2e/fixtures/dagger.toml index ae892f8..bc02768 100644 --- a/.dagger/modules/e2e/fixtures/dagger.toml +++ b/.dagger/modules/e2e/fixtures/dagger.toml @@ -6,16 +6,16 @@ check.skip = ["*"] name = "java" [[modules.java-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/generate/app" +path = "generate/app" [[modules.java-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/lookup/app" +path = "lookup/app" [[modules.java-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/deps/app" +path = "deps/app" [[modules.java-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/skip/app" +path = "skip/app" [[modules.java-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/managed-toml/app" +path = "managed-toml/app" From 19ee0c54ad0ce0155cfb1d521d920899a59b4ab6 Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:19:25 +0200 Subject: [PATCH 2/9] codegen: escape the javadoc comment terminator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A schema description that shows a glob example — Workspace.findRoots documents its exclude argument as ["**/target/**"] — carries a comment terminator, and javapoet copies the description into the generated javadoc verbatim. The comment ends early and the generated client no longer parses: Workspace.java:[1110,78] expected Escape it the way dagger/dagger's own copy of this codegen does. Signed-off-by: Yves Brissaud --- .../java/io/dagger/codegen/introspection/Helpers.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/Helpers.java b/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/Helpers.java index 91bd827..bab17a5 100644 --- a/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/Helpers.java +++ b/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/Helpers.java @@ -189,11 +189,17 @@ static MethodSpec withSetter(InputObject var, TypeName type, TypeName returnType return builder.build(); } - /** Fix using '$' char in javadoc */ + /** + * Escape characters that have a special meaning in javadoc. + * + *

'$' is escaped for JavaPoet's format strings and '&' as an HTML entity. The comment + * terminator is escaped so that a glob example such as {@code **/target/**} cannot end the + * generated javadoc early. + */ static String escapeJavadoc(String str) { if (str == null) { return ""; } - return str.replace("$", "$$").replace("&", "&"); + return str.replace("$", "$$").replace("&", "&").replace("*/", "*/"); } } From 6eb1660b88fa2a05b240e16c9413ac823dc70671 Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:19:25 +0200 Subject: [PATCH 3/9] codegen: model an id-bearing interface as IDAble MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A GraphQL interface that exposes an id field is IDAble exactly like an object, but only objects were generated as such — so an interface-typed argument (a Node, as LLM.withTools takes) had no overload to serialize through and was marshalled as a plain object instead of by ID. Reuse the providesId() test ObjectVisitor already applies, matching dagger/dagger's own copy of this codegen; in the current schema that covers Node, Exportable and Syncer. Signed-off-by: Yves Brissaud --- .../io/dagger/codegen/introspection/InterfaceVisitor.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/InterfaceVisitor.java b/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/InterfaceVisitor.java index db1ef07..721c980 100644 --- a/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/InterfaceVisitor.java +++ b/sdk/dagger-codegen-maven-plugin/src/main/java/io/dagger/codegen/introspection/InterfaceVisitor.java @@ -36,6 +36,14 @@ TypeSpec generateType(Type type) { .addJavadoc(Helpers.escapeJavadoc(type.getDescription())) .addModifiers(Modifier.PUBLIC); + // An interface exposing an id field is IDAble like any object, so a value + // typed by it (a Node argument, say) serializes by ID through the same + // Arguments.Builder overloads. + if (type.providesId()) { + interfaceBuilder.addSuperinterface( + ParameterizedTypeName.get(ClassName.bestGuess("IDAble"), ClassName.bestGuess("ID"))); + } + if (type.getFields() != null) { for (Field field : type.getFields()) { MethodSpec.Builder methodBuilder = From 12a7d5edb689e3a5725a57bf643decb5bb68e6d7 Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:19:25 +0200 Subject: [PATCH 4/9] prebuilt: refresh the committed codegen plugin Regenerated with `dagger generate packager`. Generation resolves the plugin from prebuilt/m2 and never compiles the vendored sources, so the two preceding codegen fixes only take effect once this jar carries them. Signed-off-by: Yves Brissaud --- .../dagger-codegen-maven-plugin-0.21.4.jar | Bin 67901 -> 68036 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/prebuilt/m2/io/dagger/dagger-codegen-maven-plugin/0.21.4/dagger-codegen-maven-plugin-0.21.4.jar b/prebuilt/m2/io/dagger/dagger-codegen-maven-plugin/0.21.4/dagger-codegen-maven-plugin-0.21.4.jar index e4c09a036e2ae6e7994879f652fc651716b912f1..bb7442b9c90663e96a30ae1e4ffffbdba80c6fe4 100644 GIT binary patch delta 9457 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 delta 9313 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 From 39fb49f9068f6cec63ba83b824f582fd69f4304c Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:19:47 +0200 Subject: [PATCH 5/9] java-sdk: select managed modules from the engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit currentModule.asSDK now answers the question this SDK was reconstructing by hand: it returns the modules registered to this SDK, already narrowed to the client's cwd — everything at or below it, plus the nearest enclosing module when the cwd is not itself one — with workspace-root-relative paths. That is exactly the polyfill's findConfigDirs walk intersected with the workspace's managed-module list, so drop the walk, the intersection, and the cwd-relative-to-root-relative mapping the walk needed. Signed-off-by: Yves Brissaud --- .dagger/modules/e2e/main.dang | 9 +++-- main.dang | 67 +++++++---------------------------- main.dang.tmpl | 67 +++++++---------------------------- 3 files changed, 28 insertions(+), 115 deletions(-) diff --git a/.dagger/modules/e2e/main.dang b/.dagger/modules/e2e/main.dang index 93b6528..1a6e517 100644 --- a/.dagger/modules/e2e/main.dang +++ b/.dagger/modules/e2e/main.dang @@ -151,11 +151,10 @@ type E2e { modules that live outside it. """ modulesCwdCheck(ws: Workspace!): Void @check { - # A stable snapshot of the workspace, re-anchorable at any cwd. Snapshot the - # whole workspace (not just config files) so the re-anchored workspace keeps - # its dagger.toml — modules() reads the managed-module list from it via - # ws.sdk — alongside every module config (dagger.json and dagger-module.toml, - # for discovery) and lookup/app/nested (a config-less subdirectory). + # A stable snapshot of the workspace, re-anchorable at any cwd. It has to + # carry the fixture dagger.toml, which is where currentModule.asSDK reads the + # managed-module list from, and lookup/app/nested, the config-less + # subdirectory the find-up case is anchored in. let root = testWS(ws).directory("/") # Walk-down: from fixtures/generate only generate/app is in the cone; the diff --git a/main.dang b/main.dang index 658c372..d24f764 100644 --- a/main.dang +++ b/main.dang @@ -19,13 +19,6 @@ type JavaSdk { """ skipGenerateFilename: String! = ".dagger-java-sdk-skip-generate" - """ - Config filenames that mark a Dagger module root: the CLI 1.0 - `dagger-module.toml` (workspace-managed modules) and the legacy `dagger.json`. - A managed module is discovered by whichever it uses. - """ - let moduleConfigFilenames: [String!]! = ["dagger-module.toml", "dagger.json"] - """ Commit the compiled Dagger Java SDK as a jar into each generated module so its runtime build compiles only the module's own code against the jar instead of @@ -98,62 +91,26 @@ type JavaSdk { Return every Java SDK module this workspace manages that is visible from the client's current location. - Discovery is anchored at the client's cwd (never the workspace root): the - nearest enclosing module plus every module at or below the cwd, intersected - with the SDK's engine-owned list of managed modules - (currentModule.asSDK.modules). So running from a subdirectory acts on the - project you're in — and the projects beneath it — not the whole workspace. - - Discovery is the polyfill's cwd-aware findConfigDirs (dagger/dagger#13688); - this maps its cwd-relative results to workspace-root-relative paths and keeps - the ones this SDK manages, whether they use dagger-module.toml or dagger.json. + The engine owns both the list and the cwd policy: currentModule.asSDK returns + the modules registered to this SDK that sit at or below the client's cwd, plus + the nearest enclosing one when the cwd is not itself a module. So running from + a subdirectory acts on the project you're in — and the projects beneath it — + not the whole workspace. Paths come back relative to the workspace root, the + currency Mod.rootPath uses. """ modules(ws: Workspace!): [Mod!]! { - let managed = ws.sdk(name: currentModule.name).modules.{{source}} - let cwd = normalizePath(ws.cwd) - polyfill - .workspace(ws) - .findConfigDirs(moduleConfigFilenames, exclude: ["**/target/**"]) - .map { dir => moduleRelPath(cwd, dir) } - .uniq - .filter { path => managed.filter { m => normalizePath(m.source) == path }.length > 0 } - .map { path => Mod( - rootPath: path, + currentModule + .asSDK(workspace: ws) + .modules + .{{path}} + .map { module => Mod( + rootPath: module.path, ws: ws, skipGenerateFilename: skipGenerateFilename, vendorSdkJar: vendorSdkJar, ) } } - """ - Normalize a workspace path: strip a leading "./" or "/" and any trailing "/", - and map the empty/root path to ".". - """ - let normalizePath(path: String!): String! { - let normalized = path.trimPrefix("./").trimPrefix("/").trimSuffix("/") - if (normalized == "") { "." } else { normalized } - } - - """ - Resolve a findConfigDirs result — a cwd-relative path, at or below the cwd - ("." , "sub/dir") or a strict ancestor (".." , "../..") — against the cwd into a - workspace-root-relative path, the format both managed module sources and - Mod.rootPath use. - """ - let moduleRelPath(cwd: String!, dir: String!): String! { - let base = if (cwd == "" or cwd == ".") { [] } else { cwd.split("/") } - let segs = dir.split("/").reduce(base) { acc, seg => - if (seg == "..") { - acc.dropLast(1) - } else if (seg == "." or seg == "") { - acc - } else { - acc + [seg] - } - } - if (segs.length == 0) { "." } else { segs.join("/") } - } - """ Generate every managed Java SDK module visible from the client's current location (runs at `dagger generate`). Discovery goes through modules(ws), so diff --git a/main.dang.tmpl b/main.dang.tmpl index a3bb400..5ea396f 100644 --- a/main.dang.tmpl +++ b/main.dang.tmpl @@ -19,13 +19,6 @@ type JavaSdk { """ skipGenerateFilename: String! = ".dagger-java-sdk-skip-generate" - """ - Config filenames that mark a Dagger module root: the CLI 1.0 - `dagger-module.toml` (workspace-managed modules) and the legacy `dagger.json`. - A managed module is discovered by whichever it uses. - """ - let moduleConfigFilenames: [String!]! = ["dagger-module.toml", "dagger.json"] - """ Commit the compiled Dagger Java SDK as a jar into each generated module so its runtime build compiles only the module's own code against the jar instead of @@ -98,62 +91,26 @@ type JavaSdk { Return every Java SDK module this workspace manages that is visible from the client's current location. - Discovery is anchored at the client's cwd (never the workspace root): the - nearest enclosing module plus every module at or below the cwd, intersected - with the SDK's engine-owned list of managed modules - (currentModule.asSDK.modules). So running from a subdirectory acts on the - project you're in — and the projects beneath it — not the whole workspace. - - Discovery is the polyfill's cwd-aware findConfigDirs (dagger/dagger#13688); - this maps its cwd-relative results to workspace-root-relative paths and keeps - the ones this SDK manages, whether they use dagger-module.toml or dagger.json. + The engine owns both the list and the cwd policy: currentModule.asSDK returns + the modules registered to this SDK that sit at or below the client's cwd, plus + the nearest enclosing one when the cwd is not itself a module. So running from + a subdirectory acts on the project you're in — and the projects beneath it — + not the whole workspace. Paths come back relative to the workspace root, the + currency Mod.rootPath uses. """ modules(ws: Workspace!): [Mod!]! { - let managed = ws.sdk(name: currentModule.name).modules.{{source}} - let cwd = normalizePath(ws.cwd) - polyfill - .workspace(ws) - .findConfigDirs(moduleConfigFilenames, exclude: ["**/target/**"]) - .map { dir => moduleRelPath(cwd, dir) } - .uniq - .filter { path => managed.filter { m => normalizePath(m.source) == path }.length > 0 } - .map { path => Mod( - rootPath: path, + currentModule + .asSDK(workspace: ws) + .modules + .{{path}} + .map { module => Mod( + rootPath: module.path, ws: ws, skipGenerateFilename: skipGenerateFilename, vendorSdkJar: vendorSdkJar, ) } } - """ - Normalize a workspace path: strip a leading "./" or "/" and any trailing "/", - and map the empty/root path to ".". - """ - let normalizePath(path: String!): String! { - let normalized = path.trimPrefix("./").trimPrefix("/").trimSuffix("/") - if (normalized == "") { "." } else { normalized } - } - - """ - Resolve a findConfigDirs result — a cwd-relative path, at or below the cwd - ("." , "sub/dir") or a strict ancestor (".." , "../..") — against the cwd into a - workspace-root-relative path, the format both managed module sources and - Mod.rootPath use. - """ - let moduleRelPath(cwd: String!, dir: String!): String! { - let base = if (cwd == "" or cwd == ".") { [] } else { cwd.split("/") } - let segs = dir.split("/").reduce(base) { acc, seg => - if (seg == "..") { - acc.dropLast(1) - } else if (seg == "." or seg == "") { - acc - } else { - acc + [seg] - } - } - if (segs.length == 0) { "." } else { segs.join("/") } - } - """ Generate every managed Java SDK module visible from the client's current location (runs at `dagger generate`). Discovery goes through modules(ws), so From baffd411bc9ad21ef1483bc2043492a8b744b582 Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:20:11 +0200 Subject: [PATCH 6/9] java-sdk: resolve module sources through the workspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workspace.moduleSource now resolves a module the way the polyfill's nested client did — through the workspace's own owning client, so SDK and user-defaults loading still reach the requester's host session — and ModuleSource.generateLocalDependencies is native too. Call both directly. The guard around the dependency staging goes with it. It was there because the polyfill call needed an owning client and so failed outright on a synthetic workspace (Directory.asWorkspace, which the e2e checks build) even for a module with no dependencies at all; the native call recognises a value workspace and resolves against its own contents. What is left short-circuits nothing: the dependency list it inspected cost the same module-source resolution the staging call does, and the engine already skips git dependencies and returns an empty changeset when there is nothing to stage. generateModule loses its path argument while it is being rewritten anyway. Its only caller passed rootPath, the field on the same object, and the two spellings had already drifted apart — the dependency staging said rootPath while every other line said modPathArg. Signed-off-by: Yves Brissaud --- mod.dang | 61 ++++++++++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/mod.dang b/mod.dang index 04a1966..37123f4 100644 --- a/mod.dang +++ b/mod.dang @@ -67,7 +67,7 @@ type Mod { if (skipGenerate(ws)) { changeset } else { - generateModule(ws, rootPath) + generateModule(ws) } } @@ -201,58 +201,41 @@ type Mod { The returned changeset is relative to the client's cwd, the form the engine applies a generator's result in. """ - let generateModule(ws: Workspace!, modPathArg: String!): Changeset! { - # Native module-source resolution (Directory.asModuleSource, Query.moduleSource) - # fails from module code: SDK and user-defaults loading need the requester's host - # session. The polyfill resolves the source from a nested client that has one. - # Anchor rootPath at "/" so a non-root workspace cwd is not prefixed again. - - # Create an overlay containing generated (local) dependencies so the generation - # can work. - # - # Only stage when there is a local dependency to stage: the call resolves - # module sources through the workspace's owning client, which a synthetic - # workspace (Directory.asWorkspace, as the e2e checks build) does not have, - # and an all-remote dependency list yields an empty changeset anyway. - # Remote dependencies are assumed committed, matching the engine. - let localDeps = ws - .moduleSource(workspaceRef(rootPath)) - .dependencies - .{{kind}} - .filter { dep => dep.kind != ModuleSourceKind.GIT_SOURCE } - let wsWithDeps = if (localDeps.length == 0) { - ws - } else { - ws.withChanges( - polyfill.workspace(ws).moduleSource(workspaceRef(rootPath)).core.generateLocalDependencies(ws), - ) - } - - let modSource = polyfill.workspace(wsWithDeps).moduleSource(workspaceRef(modPathArg)) - let name = modSource.core.moduleName - let introspectionJSON = modSource.core.introspectionSchemaJSON + let generateModule(ws: Workspace!): Changeset! { + # A module that depends on another by local path cannot resolve its own + # schema until that dependency has been generated, so overlay the generated + # closure onto the workspace first. The engine skips remote dependencies — + # assumed committed, as it does itself — and returns an empty changeset when + # there is nothing to stage. + let wsWithDeps = ws.withChanges( + ws.moduleSource(workspaceRef(rootPath)).generateLocalDependencies(ws), + ) + + let modSource = wsWithDeps.moduleSource(workspaceRef(rootPath)) + let name = modSource.moduleName + let introspectionJSON = modSource.introspectionSchemaJSON let vendored = vendoredSdk(introspectionJSON, name) # the module as committed, with the whole committed sdk/ dropped (source and # any previously vendored jar) and the freshly vendored SDK sources overlaid, # plus any stale generated entrypoint dropped so the processor regenerates it - let baseDir = moduleDir(wsWithDeps, modPathArg) + let baseDir = moduleDir(wsWithDeps, rootPath) .withoutDirectory("sdk") .withDirectory("sdk", vendored) .withoutDirectory("src/generated") let entrypoint = generatedEntrypoint(baseDir, name) - let before = if (modPathArg == ".") { + let before = if (rootPath == ".") { wsWithDeps.directory("/", include: ["**"]) } else { - wsWithDeps.directory("/", include: [modPathArg + "/**"]) + wsWithDeps.directory("/", include: [rootPath + "/**"]) } let staged = before - .withDirectory(joinPath(modPathArg, "sdk"), vendored) - .withDirectory(joinPath(modPathArg, "src/generated/java"), entrypoint) + .withDirectory(joinPath(rootPath, "sdk"), vendored) + .withDirectory(joinPath(rootPath, "src/generated/java"), entrypoint) let after = if (vendorSdkJar) { - staged.withDirectory(joinPath(modPathArg, "sdk/repo"), vendoredSdkJar( + staged.withDirectory(joinPath(rootPath, "sdk/repo"), vendoredSdkJar( introspectionJSON, name, )) @@ -268,13 +251,13 @@ type Mod { let cwd = ws.cwd.trimPrefix("/").trimSuffix("/") if (cwd == "") { after.changes(before) - } else if (modPathArg == cwd or modPathArg.trimPrefix(cwd + "/") != modPathArg) { + } else if (rootPath == cwd or rootPath.trimPrefix(cwd + "/") != rootPath) { after.directory(cwd).changes(before.directory(cwd)) } else { # A cwd-relative changeset cannot express paths outside the cwd, so a # module discovered above it (find-up) would be silently dropped and left # ungenerated. Fail loudly until there is a policy for such modules. - raise "module " + modPathArg + " lies outside the current directory " + cwd + ": its generated changes cannot be expressed relative to the cwd" + raise "module " + rootPath + " lies outside the current directory " + cwd + ": its generated changes cannot be expressed relative to the cwd" } } From f371373244defd45240210640fec2297386a17b5 Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:20:19 +0200 Subject: [PATCH 7/9] java-sdk: drop the polyfill dependency Nothing in this module reaches for the polyfill any more: module selection goes through currentModule.asSDK and module sources through the workspace. dagger.lock keeps its polyfill entry for now. That file locks the whole workspace, and the sdk-sdk check harness installed there still resolves the polyfill at this pin; the entry goes when that pin moves. Signed-off-by: Yves Brissaud --- dagger-module.toml | 5 ----- dagger.json | 9 +-------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/dagger-module.toml b/dagger-module.toml index 3d53cf8..4209411 100644 --- a/dagger-module.toml +++ b/dagger-module.toml @@ -3,8 +3,3 @@ engineVersion = "v1.0.0-0" [runtime] source = "dang" - -[[dependencies]] - name = "polyfill" - source = "github.com/dagger/polyfill@main" - pin = "e90bbfc4843258a877a3a95b8db1571e7981e65f" diff --git a/dagger.json b/dagger.json index e38c38e..249d65d 100644 --- a/dagger.json +++ b/dagger.json @@ -3,12 +3,5 @@ "engineVersion": "latest", "sdk": { "source": "dang" - }, - "dependencies": [ - { - "name": "polyfill", - "source": "github.com/dagger/polyfill@main", - "pin": "e90bbfc4843258a877a3a95b8db1571e7981e65f" - } - ] + } } From d6d4a68fa1a1d2b023b5358c38d9256850ecaf8a Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 13:20:30 +0200 Subject: [PATCH 8/9] java-sdk: let the engine measure generator changesets from the cwd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The engine applies a generator's changeset relative to the client's cwd, so a workspace-rooted changeset lands every path under the cwd a second time and leaves the module ungenerated. This SDK re-rooted its own result to compensate, and raised for a module found above the cwd, because the changesets came back workspace-rooted. dagger/dagger#13855 moved that into the engine: from v1.0.0-beta.10 a Workspace.changes result is measured from the workspace's own cwd, the caller supplies the baseline to compare against, and a change that falls outside the cwd fails loudly instead of being dropped. Declare the version and hand the work back — stage onto the workspace value and diff it against the baseline already in hand. The baseline is the dependency-staged workspace, not the one handed in: the dependencies' generated code belongs to their own SDKs and must stay out of this changeset, which is what the filtered before-directory used to express. Signed-off-by: Yves Brissaud --- dagger-module.toml | 2 +- dagger.json | 2 +- main.dang | 12 +++++------- main.dang.tmpl | 12 +++++------- mod.dang | 46 +++++++++++++++------------------------------- 5 files changed, 27 insertions(+), 47 deletions(-) diff --git a/dagger-module.toml b/dagger-module.toml index 4209411..2bf7646 100644 --- a/dagger-module.toml +++ b/dagger-module.toml @@ -1,5 +1,5 @@ name = "java-sdk" -engineVersion = "v1.0.0-0" +engineVersion = "v1.0.0-beta.10" [runtime] source = "dang" diff --git a/dagger.json b/dagger.json index 249d65d..95b85ed 100644 --- a/dagger.json +++ b/dagger.json @@ -1,6 +1,6 @@ { "name": "java-sdk", - "engineVersion": "latest", + "engineVersion": "v1.0.0-beta.10", "sdk": { "source": "dang" } diff --git a/main.dang b/main.dang index d24f764..0ecc12e 100644 --- a/main.dang +++ b/main.dang @@ -49,14 +49,12 @@ type JavaSdk { rawPath.trimSuffix("/") } - let before = if (modPath == ".") { - ws.directory("/", include: ["**"]) - } else { - ws.directory("/", include: [modPath + "/**"]) - } - let selectedTemplate = if (template == "") { "default" } else { template } - before.withDirectory(modPath, renderedTemplate(name, selectedTemplate)).changes(before) + # `path` is workspace-root-relative, so anchor it: relative workspace paths + # resolve from ws.cwd and would be prefixed again when initModule is called + # directly from a subdirectory instead of being driven by the engine. + let target = if (modPath == ".") { "/" } else { "/" + modPath } + ws.withNewDirectory(target, renderedTemplate(name, selectedTemplate)).changes(ws) } """ diff --git a/main.dang.tmpl b/main.dang.tmpl index 5ea396f..228a5e2 100644 --- a/main.dang.tmpl +++ b/main.dang.tmpl @@ -49,14 +49,12 @@ type JavaSdk { rawPath.trimSuffix("/") } - let before = if (modPath == ".") { - ws.directory("/", include: ["**"]) - } else { - ws.directory("/", include: [modPath + "/**"]) - } - let selectedTemplate = if (template == "") { "default" } else { template } - before.withDirectory(modPath, renderedTemplate(name, selectedTemplate)).changes(before) + # `path` is workspace-root-relative, so anchor it: relative workspace paths + # resolve from ws.cwd and would be prefixed again when initModule is called + # directly from a subdirectory instead of being driven by the engine. + let target = if (modPath == ".") { "/" } else { "/" + modPath } + ws.withNewDirectory(target, renderedTemplate(name, selectedTemplate)).changes(ws) } """ diff --git a/mod.dang b/mod.dang index 37123f4..c682800 100644 --- a/mod.dang +++ b/mod.dang @@ -199,7 +199,9 @@ type Mod { Stage the vendored SDK + generated entrypoint for one module. The returned changeset is relative to the client's cwd, the form the engine - applies a generator's result in. + applies a generator's result in: past v1.0.0-beta.10 every Workspace.changes + result is measured from the workspace cwd, so staging onto the workspace value + and diffing it against the baseline the caller already holds is all this needs. """ let generateModule(ws: Workspace!): Changeset! { # A module that depends on another by local path cannot resolve its own @@ -225,40 +227,22 @@ type Mod { .withoutDirectory("src/generated") let entrypoint = generatedEntrypoint(baseDir, name) - let before = if (rootPath == ".") { - wsWithDeps.directory("/", include: ["**"]) - } else { - wsWithDeps.directory("/", include: [rootPath + "/**"]) - } - let staged = before - .withDirectory(joinPath(rootPath, "sdk"), vendored) - .withDirectory(joinPath(rootPath, "src/generated/java"), entrypoint) + let staged = wsWithDeps + .withNewDirectory(workspaceRef(joinPath(rootPath, "sdk")), vendored) + .withNewDirectory(workspaceRef(joinPath(rootPath, "src/generated/java")), entrypoint) let after = if (vendorSdkJar) { - staged.withDirectory(joinPath(rootPath, "sdk/repo"), vendoredSdkJar( - introspectionJSON, - name, - )) + staged.withNewDirectory( + workspaceRef(joinPath(rootPath, "sdk/repo")), + vendoredSdkJar(introspectionJSON, name), + ) } else { staged } - # The staging above is workspace-rooted, but the engine applies a returned - # changeset relative to the caller's cwd — so without re-rooting here every - # path ends up nested under the cwd a second time and the module is never - # actually generated. Selecting a subdirectory is metadata only, so this - # costs nothing. - let cwd = ws.cwd.trimPrefix("/").trimSuffix("/") - if (cwd == "") { - after.changes(before) - } else if (rootPath == cwd or rootPath.trimPrefix(cwd + "/") != rootPath) { - after.directory(cwd).changes(before.directory(cwd)) - } else { - # A cwd-relative changeset cannot express paths outside the cwd, so a - # module discovered above it (find-up) would be silently dropped and left - # ungenerated. Fail loudly until there is a policy for such modules. - raise "module " + rootPath + " lies outside the current directory " + cwd + ": its generated changes cannot be expressed relative to the cwd" - } + # Baseline on the staged-dependency workspace, not the one handed in: the + # dependencies' generated code belongs to their own SDKs, not this changeset. + after.changes(wsWithDeps) } """ @@ -280,8 +264,8 @@ type Mod { } """ - A module path as a workspace-root-absolute ref, the form Workspace.moduleSource - resolves from the workspace root rather than from the client's cwd. + A module path as a workspace-root-absolute path, the form Workspace resolves + from the workspace root rather than from the client's cwd. """ let workspaceRef(modPathArg: String!): String! { if (modPathArg == ".") { "/" } else { "/" + modPathArg } From 92dc4a354ed66b6d8d5e73be076df4a57d29d15f Mon Sep 17 00:00:00 2001 From: Yves Brissaud Date: Thu, 20 Aug 2026 16:14:57 +0200 Subject: [PATCH 9/9] chore: update sdk-sdk to 00bb067 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picks up dagger/sdk-sdk#20, which threads daggerCliVersion into mod-test so the contract checks run on the configured CLI release rather than mod-test's own default, and moves that default to 1.0.0-beta.10 — new enough to build a flag for a Workspace argument. All four contract checks failed here without it: the harness silently skipped initModule, which takes a Workspace, and reported the caller's assertion message rather than the underlying failure. Also picks up the harness's own polyfill removal. Signed-off-by: Yves Brissaud --- dagger.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dagger.lock b/dagger.lock index 8949b1e..e7ebe70 100644 --- a/dagger.lock +++ b/dagger.lock @@ -1,4 +1,3 @@ -[["version","1"]] -["","git.head",["https://github.com/dagger/dang-sdk"],"c724eec4270870aae489daa9f3cb8cbacfb44680","float"] -["","git.head",["https://github.com/dagger/sdk-sdk"],"e1747f4b6221fa24da080701e027243e0cc5fa33","float"] -["","git.ref",["https://github.com/dagger/polyfill","main"],"ec3ea84a2351b4beb06ecece951f2e5ef66509ff","float"] \ No newline at end of file +[["version","2"]] +["","git.ref",["https://github.com/dagger/dang-sdk","HEAD"],{"ref":"refs/heads/main","sha":"c724eec4270870aae489daa9f3cb8cbacfb44680"}] +["","git.ref",["https://github.com/dagger/sdk-sdk","HEAD"],{"ref":"refs/heads/main","sha":"00bb06748bcf22d724ed467f2298d31f1fb49be0"}] \ No newline at end of file