Visitar URL original
core: settle how a module appears in its own dependencies by sipsma · Pull Request #15119 · dagger/dagger · GitHub
Skip to content

core: settle how a module appears in its own dependencies - #15119

Open
sipsma wants to merge 3 commits into
mainfrom
sipsma/refuse-core-type-module-names
Open

sipsma wants to merge 3 commits into
mainfrom
sipsma/refuse-core-type-module-names

Conversation

@sipsma

@sipsma sipsma commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

This PR settles how a module appears in its own dependencies, in three commits.

1. Refuse a module whose main object is named after a core type

A module's main object takes the module's name and isn't namespaced, so a module named directory, container or file declares a type that core already has. Such a module should be refused when it loads, with type "Directory" is already defined by module "daggercore", and modules of SDKs without self calls (TypeScript, Python, Java, PHP) are. But for modules with self calls, which Go and Dang modules always have, the check only logged the clash and carried on (since #10584). The module's object type was then never installed, because the schema kept core's type of that name, while its constructor still replaced the core root field:

  • Every call through the main object failed: in Dang with convert return value: expected string, got map[string]interface {}, in Go with cannot instantiate dagql.Class[*core.Directory].
  • A Dang module that called the core root field it had replaced (a module directory calling Dagger.directory) hung on its first call.

Self calls add a module to its own dependencies, so its own types are found there. The check now accepts only that match and refuses any other clash, as for modules without self calls. This reverses the Go behaviour that #13381 described as "no longer fails the load".

That relies on a module's self dependency being the module itself. A module cloned into a new result (implementation scoping, SDK operation scoping) kept the self entry of the module it was cloned from beside its own, and would then have refused itself. The self entry is now marked, and attaching a module replaces it instead of appending a second one.

2. An entrypoint's schema never holds the module it builds

A version 2 manifest lists its dependencies as its entrypoint's own, and the entrypoint runs with a schema of the core and those dependencies. The module itself was added to that schema too. A module with the same name as one of its entrypoint's dependencies, such as a Go module named go, whose generated entrypoint builds it with dagger/go under the name go, then put two modules of one name into one schema: the entrypoint's go resolved to the module's own constructor, and the module failed to build. An entrypoint's schema is now the core and its own dependencies, never the module, and an entrypoint with no dependencies of its own runs with the core alone. The module's process keeps its self calls through the module's own schema.

3. A process an entrypoint starts runs as the client's own module

With the module no longer in its entrypoint's schema, a process the entrypoint starts couldn't find the client's module among the ones it serves, and fell back to a copy that keeps the source of whichever client loaded it first. The client's own module is now among the candidates.

What users see

  • A Go or Dang module named after a core type now fails to load with the error above, instead of loading and then failing on every call (or hanging). As far as the tests and the code show, no such module works today.
  • A module with the same name as one of its entrypoint's dependencies (such as a Go module named go) builds and runs.
  • Speed: not measured; none expected. The refusal adds one identity comparison per module type at install, an entrypoint's schema holds one module fewer, and a process looking up its module has one more candidate.

Tests

  • TestDang/TestMainObjectNamedAfterCoreType: a module container (failed with convert return value) and a module directory that calls Dagger.directory (hung; the test bounds it at 5 minutes).
  • TestGo/TestMainObjectNamedAfterCoreType: a module directory that calls dag.Directory() (failed with cannot instantiate).
  • TestModuleCloneRebindsSelfDependency: a self-calling module cloned into a new result has only itself as its self dependency, and its schema dispatches to it.
  • TestModule/TestDangModuleEntrypointDependencySharesModuleName: a module named like its entrypoint's dependency; the entrypoint's call reaches the dependency.

Each fails without its fix. Without the third commit, four existing TestModuleLoading/TestServeModuleCallerTree cases fail.

A module's main object takes the module's name and isn't namespaced, so a
module named `directory`, `container` or `file` declares a type that core
already has. Installing such a module is meant to fail with "type is
already defined by module", but for modules with self calls (always on for
Go and Dang) the check only logged and continued. The module's object type
was then never installed, as the schema kept core's type of that name,
while its constructor still replaced the core root field. Every call
through the main object failed (in Dang with "convert return value", in Go
with "cannot instantiate dagql.Class[*core.Directory]"), and a Dang module
that called the core root field it had replaced hung.

Self calls add the module to its own dependencies, so its own types are
found there. Accept only that match, and refuse a clash with core or with
another dependency again, as for modules without self calls.

This relies on a module's self dependency being the module itself. A module
cloned into a new result, as implementation scoping does, kept the self
entry of the module it was cloned from beside its own. Mark the self entry
and replace it on attachment instead.

Signed-off-by: Erik Sipsma <erik@sipsma.dev>
@sipsma
sipsma force-pushed the sipsma/refuse-core-type-module-names branch from 121e8a1 to 3d876cd Compare November 11, 2026 01:29
@sipsma sipsma changed the title core: refuse a module whose main object is named after a core type core: settle how a module appears in its own dependencies Nov 11, 2026
A version 2 manifest lists its dependencies as its entrypoint's own, and
the entrypoint runs with a schema of the core and those dependencies. The
module itself was added to that schema too: when its dependencies were
attached, and again when the engine served the entrypoint's client. A
module with the same name as one of its entrypoint's dependencies then put
two modules of one name into one schema. A Go module named go, whose
generated entrypoint builds it with dagger/go under the name go, is one.
The last root field of a name wins and the first type of a name wins, so
the entrypoint's go resolved to the module's own constructor, while the
Go types stayed dagger/go's, and the module failed to build.

An entrypoint's schema is now the core and its own dependencies, never the
module, and the engine serves its client exactly that schema. That is also
the schema SDKs check a generated entrypoint against. The module's process
keeps its self-calls through the module's own schema, as before. An
entrypoint with no dependencies of its own runs with the core alone, so
the rule holds whether or not it has any.

Signed-off-by: Erik Sipsma <erik@sipsma.dev>
A process an exec starts runs as the current client's own copy of its
module, found among the modules the client installed: before, only those
it served. A module entrypoint's client now serves only the entrypoint's
schema, which never holds the module, so the module wasn't among them. The
process fell back to the implementation-scoped copy, which keeps the
source of whichever client loaded it first, and served a client module
from another checkout's tree, or from a path that doesn't exist on this
host.

The client's own module is now among the candidates, beside the modules
it serves. For any other module client it is already one of those.

Signed-off-by: Erik Sipsma <erik@sipsma.dev>
@sipsma
sipsma force-pushed the sipsma/refuse-core-type-module-names branch from 3d876cd to 59be068 Compare November 11, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant