Analysis concluding that typed collection filters collapse the need for a
separate artifact address concept. The conclusion is incorporated into
hack/designs/modules-v2/artifacts.md on the modules-v2 branch of
dagger/dagger.
See: https://github.com/dagger/dagger/tree/modules-v2/hack/designs/modules-v2
The workspace-artifacts design introduces "artifact addresses" — a (TYPE, cross-cutting key value) identity for collection items whose key type belongs
to a blessed list (WorkspacePath, HTTPAddress, GitAddress, ContainerAddress).
Addresses make these items first-class nouns: things users can point at,
inspect, and target with verbs like ship.
Meanwhile, the standalone collections branch shipped typed collection
filters (--go-modules=./cmd/api, --go-tests=TestFoo) with a strong UX
that already covers most of the targeting use cases artifact addresses were
designed for.
This document asks: do we still need artifact addresses as a separate concept, or do typed collection filters already provide what we need?
-
A noun.
GoModule:./cmd/apiis a thing, not a verb modifier. You can list it, inspect it, target it. -
A flat cross-type inventory.
dagger artifact listshows every important thing in the workspace in one table, regardless of which collection or module surfaced it. -
Blessed key types. The engine maintains a fixed list of cross-cutting key types (WorkspacePath, HTTPAddress, etc.). Collections keyed by those types produce artifacts; others don't. This separates "important addressable things" from "internal keyed sets." These types also carry special parsing and rendering semantics — WorkspacePath values are resolved relative to the client's working directory, remote addresses render in absolute form, etc.
-
Bundle boundaries. An artifact address defines an entry object. Fields from that object define the artifact's internal structure. Include/exclude narrows which parts of the bundle participate in a verb.
-
Cross-artifact reference edges. If artifact A's fields reference artifact B, the engine can order verb execution (check B before A).
The noun. --go-modules=./cmd/api carries the same information as
GoModule:./cmd/api. It selects one item in one collection. For check and
generate, filter syntax is natural. For ship and up, "ship, filtered to this
thing" is slightly less direct than "ship this thing" — but it works. The
difference is syntactic preference, not capability.
The flat inventory. Without addresses, "what's in my workspace?" is
answered by walking all collections and listing their keys grouped by type.
The --list-* flags on the collections branch already do per-type listing. A
unified dagger list command could walk all collections and present a
combined view without needing an "artifact" concept.
Blessed key types. The artifact promotion role goes away, but the types themselves do not. WorkspacePath, HTTPAddress, etc. still need to exist as typed scalars with special parsing and rendering semantics — a WorkspacePath filter value must be resolved relative to the client's working directory, and the CLI must accept any valid path to the same location. What changes is that these types live in the collection filter plumbing (the CLI and engine know how to parse and normalize filter values of these types) rather than in an artifact promotion layer. The types carry rendering/parsing semantics, not identity semantics.
Bundle boundaries. The bundle is "collection item + everything reachable from its fields." This structural reality exists whether or not the entry object has a user-facing address. The engine walks this structure already (ModTree). Bundle boundaries can be an engine implementation detail rather than a user-facing concept.
Cross-artifact reference edges. The engine can identify reference edges using DAGQL object identity. It sees "this object's field points to that object" through structural analysis. User-facing addresses aren't needed for the engine to build an ordering graph.
The unified inventory framing. "Your workspace has these artifacts: GoModule:./cmd/api, NetlifySite:./docs" is a cleaner elevator pitch than "your workspace has these collections: GoModules (./cmd/api, ./cmd/worker), NetlifySites (./docs)." Same information, different packaging. The artifact framing is flatter; the collection framing is more structured.
The "ship this" directness. dagger ship GoModule:./cmd/api reads as
targeting a noun. dagger ship --go-modules=./cmd/api reads as filtering a
verb. For ship and up, the noun framing feels more intentional. But if
dagger ship with no filters is an error (you must specify what to ship),
the filter form works — you're just required to provide at least one.
The named bundle. Without addresses, the bundle concept has no user-facing handle. You can still narrow within a collection item's structure using filters and include/exclude, but you don't name the bundle itself.
Blessed types demoted from identity to rendering. The blessed scalar types (WorkspacePath, HTTPAddress, etc.) still exist — they carry parsing and rendering semantics that are essential for good UX. But their role shrinks from "gatekeeper for artifact promotion" to "hint for how the CLI handles filter values." No artifact promotion logic, no debates about which types qualify for the address namespace.
No artifact promotion logic. The engine doesn't walk collections, check key types, and build an artifact registry. Collections are the only keyed-set primitive.
One UX model across all verbs. Check, generate, ship, up all use the same typed filter syntax. No "addresses for some contexts, filters for others."
Cleaner separation of concerns. Typed filters select collection items.
Provenance filters (--path) select by workspace origin. No third selection
mechanism (addresses) to distinguish from both.
Simpler mental model. Users learn one concept (collections with typed filters) instead of two (collections for internal keyed sets, artifacts for important keyed sets promoted by blessed key types).
The strongest argument for artifact addresses is that ship and up are inherently noun-targeted: you ship a thing, you bring up a thing. Filters are verb modifiers, not nouns.
Counter-argument: dagger ship --netlify-sites=./docs is precise and
unambiguous. The typed filter names the collection and the key. If ship
requires at least one filter (no bare dagger ship that ships everything),
the UX is safe and clear. The "noun" quality is a syntactic preference that
typed filters can approximate.
The second strongest argument is discoverability. "Show me everything important in my workspace" is a natural first question. Artifact addresses give a flat answer. Without them, the answer is per-collection.
Counter-argument: a dagger list command (or equivalent) can walk all
collections, present a unified table grouped by type, and answer the same
question. It doesn't need an "artifact" abstraction to do this — it just
needs to enumerate collections and their keys. The collections branch already
has the machinery for this.
Typed collection filters collapse roughly 80% of what artifact addresses were designed for. The remaining 20% is:
- The noun quality for ship/up (real but possibly a syntax preference)
- The unified cross-type inventory (achievable without a dedicated concept)
- The bundle-as-named-concept (may be an engine internal)
The strongest version of the "no addresses" position: artifacts aren't a separate concept — they're collection items. Some collections happen to be keyed by types with special rendering/parsing semantics (WorkspacePath, etc.). The engine can reason about structure and ordering internally. Typed filters are the UX — and the blessed scalar types make those filters smart about path resolution, display, and equivalence. One primitive (collections with typed filters) instead of two (collections + artifact addresses).
Whether that last 20% justifies the added concept is an open design question. The fact that artifact address syntax is still open in the design doc suggests the concept hasn't yet proven its necessity through concrete use.
Open question. Not yet incorporated into the artifacts design.