Skip to content

Instantly share code, notes, and snippets.

@shykes
Created April 1, 2026 11:38
Show Gist options
  • Select an option

  • Save shykes/9c800d491ab62f7da6a83cfe46ce4a41 to your computer and use it in GitHub Desktop.

Select an option

Save shykes/9c800d491ab62f7da6a83cfe46ce4a41 to your computer and use it in GitHub Desktop.
ARCHIVE: Do We Need Artifact Addresses? (analysis)

ARCHIVE: Do We Need Artifact Addresses?

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


Do We Need Artifact Addresses?

Context

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?

What Artifact Addresses Give You

  1. A noun. GoModule:./cmd/api is a thing, not a verb modifier. You can list it, inspect it, target it.

  2. A flat cross-type inventory. dagger artifact list shows every important thing in the workspace in one table, regardless of which collection or module surfaced it.

  3. 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.

  4. 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.

  5. Cross-artifact reference edges. If artifact A's fields reference artifact B, the engine can order verb execution (check B before A).

Which of These Survive Without Addresses?

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.

What You'd Lose

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.

What You'd Gain

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 Ship/Up Question

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 Inventory Question

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.

Assessment

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.

Status

Open question. Not yet incorporated into the artifacts design.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment