Decision record
AD-0012 Composition as a maintainer step with committed output and a drift test
Status: accepted. Date: 2026-08-27.
Context
The router needs an execution configuration composed from the three
subgraph schemas. wgc router compose -i graph.yaml -o config.json
produces one locally, and the vendor’s page says the command “does not
interact with the control plane and completely runs locally”. The router
loads the file through EXECUTION_CONFIG_FILE_PATH and takes the
static-config branch before any poller is consulted. The vendor’s
compose page says “it is recommended to not use this for production”, its
CLI source directs production users to router fetch, and the router logs
“Not recommended for Production” at every start without a token. The demo
has no control plane to
fetch from, and the README presents the static path as what makes the
stack air-gappable, so the vendor’s wording is quoted wherever the public
text describes it.
The question is where and when composition runs. The Go composition
library was removed from the Cosmo repository on 2026-05-06, so composition
means wgc or the TypeScript package, both of which need Node. The
packaging research had suggested composing at container start or in a Go
build step, and both suggestions died with that removal.
CI runs go build and go test with read-only permissions and
image publishing on version tags, and no Node toolchain (SC-07). wgc sends
usage events unless DO_NOT_TRACK=1 or COSMO_TELEMETRY_DISABLED=true is
set, it declares no Node range, and whether it needs no network at all with
schema files only was not established.
The output carries a compatibility version that the router checks at start, wgc hard-codes it at 1, and the router 0.313.0 subscription overhaul renamed configuration keys, so the router tag and the wgc version are pinned and bumped together. The README’s third condition for a projection, that the contract between producer and consumer is checked mechanically before deployment, is met by the repository rather than by the demo, through a test that fails when a subgraph schema and the composed configuration drift apart.
Decision
We will run composition as a maintainer step on a connected machine with
the two telemetry variables set, from the compose input at
examples/pipeline/graph.yaml to examples/pipeline/config.json, commit
both, copy the configuration into the image at /app/config.json, and
guard the committed output with a Go test that parses it and compares each
embedded subgraph schema with the schema file it came from (SR-42). The
compose input names the three subgraphs with
loopback routing URLs on ports 3011 to 3013, a schema file for each, and
ws with subprotocol auto for the two that carry subscriptions.
Alternatives considered
Composing at container start, so the supervisor could template the routing
URLs itself. The packaging research raised it with composition-go, which
no longer exists, and the remaining composers need Node, which the
distroless image does not have.
Composing in the build, as a Node stage in the Dockerfile running
npx wgc router compose. The packaging research listed it as one of two
places composition could run in CI. It would put Node into every image
build, with a tool that declares no Node range and sends usage events
unless the two variables are set in CI. A maintainer step
on a connected machine is what makes wgc’s unverified offline behaviour
and Node range not load-bearing, which is why no spike is opened for them.
Fetching the configuration from a control plane with router fetch, the
vendor’s production recommendation. There is no control plane in
the demo, and a fetch at start would be a network dependency in a stack
presented as air-gappable.
Consequences
CI stays Go only. The drift test is an ordinary unit test, so a schema change without a recompose fails on the pull request without Node in CI (SC-07), and the maintainer’s recompose is a demonstration rather than a test (SR-42). The committed configuration is what the image ships, so what the router runs is reviewable in a pull request as a diff.
The maintainer’s machine becomes the one place composition happens, with
Node, a pinned wgc and the two telemetry variables. A contributor who edits
a schema and cannot run wgc sees the drift test fail and has to ask the
maintainer to recompose, which is a cost the demo accepts. The two file
locations are fixed by the allowlist: examples/*/*.yaml and
examples/*/*.json reach one level below examples/, so the files sit at
examples/pipeline/ rather than in a router/ subdirectory (SC-04).
The router tag and the wgc version move together, so a router bump is a recompose and a rebuild, not a tag change alone. The configuration bakes in the loopback routing URLs of the single-container layout, so any other layout needs its own compose input, which is one reason the compose file among AD-0011’s alternatives was not taken.
Whether Cosmo composition accepts the capacity service’s nested @requires
is the first spike of the implementation phase, and the compose
step is where it is exercised. The public text carries the vendor’s
production caveat beside its description of static composition and says
what the demo does about it: no control plane exists to fetch from, and
the configuration is committed and tested for drift.
Requirements affected
SR-42, SC-07
Sources
The vendor’s pages on wgc router compose and on the router’s static execution configuration, and the commit that removed composition-go from wundergraph/cosmo. Five spikes before the first line for the composition runs and where the embedded schema sits in the output, and Five views and twenty-six decisions for the composition view.