ADLC
Toolkit

quartermaster

The operator-local supply layer. Decides which model runs which lifecycle job from a registry outside the repo, and routes build work by derived class rather than by the caller's label.

quartermaster

ADLC phase: P4 Build · Gate: every dispatch resolves its {adapter, model, transport} from an operator-local registry outside the candidate tree, or it does not dispatch at all.

The ADLC lifecycle, with P4 highlighted. Read the theory for P4

What it defends against

F8: Coherence loss (theory ↗)

model-router answers how much model a ticket deserves — a tier and a ladder mode. It deliberately does not answer which concrete harness, on which subscription, through which transport. Left unanswered, that question gets answered by whatever config happens to be nearest, and the nearest config is usually inside the repo being reviewed.

That is the hole. A candidate tree that can name its own reviewer, or repoint frontier at something cheap, can lower the bar it is about to be judged against — and nothing downstream would look wrong. So the registry lives entirely outside every candidate tree, and the repo carries no knob at all: not a path, not an override, not a symbolic hint. A registry-shaped file found in the tree is ignored with a notice; a configured path that is relative or inside the repo disables loading rather than reading it.

"Operator-local" means "not selected by the candidate", not "unreachable by it." Fleet's model-plane worker is deliberately not filesystem-sandboxed (it must authenticate with its provider), and the repo's own fleet.gate commands run inside that worker. Candidate-authored code therefore executes with the operator's filesystem privileges and could rewrite the registry itself. Moving supply out of the tree turns a declarative downgrade into a code-execution one; it does not close it. Closing it means isolating the model plane's filesystem while keeping network egress — a change to fleet's containment architecture, tracked in #395.

The second half is that a registry label proves nothing about what ran. The dispatcher forces the registry's model onto the harness command line explicitly (-m <model>) instead of trusting an ambient default, so the model that ran is the model the records claim.

transport does not yet get that treatment. It constrains validation and is reported in the plan, but dispatch does not use it to select credentials — so frontier (subscription:*) and frontier-metered (api:*), being the same adapter and model, execute identically today. Work routed to the metered channel may consume the subscription instead. Treat a declared transport as intent and a validation constraint, not as a guarantee about which credential paid. Tracked in #396.

The registry

An operator-local quartermaster.json$XDG_CONFIG_HOME/adlc/quartermaster.json by default, or wherever ADLC_QUARTERMASTER_REGISTRY points, provided that path is absolute and outside the repo under review. Channel names are a closed set fixed by the spec (frontier, frontier-metered, mid, cheap, plus the reviewer groups), so a registry cannot invent a route the routing contract never sanctioned. Adding a channel is a spec revision, not a config edit.

Seven load-time rules apply, all fail-closed and all reported at once: closed names, an adapter allowlist drawn from the fleet's adapter modules, no command/argv-shaped field at any depth, distinct fallback transports, a closed transport taxonomy (subscription: / api: / gateway:), concrete model IDs for reviewer seats, and a (adapter, model) → provider table that must carry every concrete model and agree with the seat's declared provider. A missing, disabled, or invalid registry aborts before dispatch. There are no defaults.

Channels must name concrete model IDs, not aliases like default. An alias is resolved by the harness from state the candidate can influence — Claude Code reads a project-committed .claude/settings.json model when no --model is passed — so §4c permits an alias only on an adapter that attests the model it actually ran. None do yet, so alias channels are rejected at load.

Alias resolutions cannot be checked that way — a harness alias tracking a floating "latest" is not enumerable at load time — so for an alias the table is only checked to be non-empty, and the schema reference says so plainly rather than implying more.

Full schema: docs/integrations/quartermaster-registry.md.

Routing

routeJob is a total function over a closed job enum that does not trust its caller. For build.* jobs the class is derivedcontract/spec/ architecture categories are spec-class, float === 0 is critical-path, everything else is ladder-start — and a caller-supplied label that disagrees with the derivation throws. A mislabeled dispatch is a bug, not a route.

Float is read from the assignment, never from the ticket: stored tickets have no float field, and defaulting an absent one would silently downgrade critical-path work to the cheap channel.

Usage

The registry is operator-local configuration, not a command. To see what it would dispatch — resolved per ticket, with the argv the adapter itself renders — run the fleet's dry run:

adlc fleet run --dry-run
  quartermaster registry: /Users/you/.config/adlc/quartermaster.json
  T900     job=build.critical-path channel=frontier adapter=opencode model=operator/frontier-model transport=subscription:anthropic-max
           argv: opencode ["run","-m","operator/frontier-model","<prompt>"]
  T901     job=build.ladder-start channel=mid adapter=opencode model=zai/glm-5.2 transport=gateway:opencode-go
           argv: opencode ["run","-m","zai/glm-5.2","<prompt>"]

Notices — an ignored in-repo registry, a disabled path — go to stderr. Adding --json returns the same seats and argv under a quartermaster key, through the identical validation path, so automation using it as a pre-dispatch check is never told a run is fine when the live run would fail closed. With no operator registry present the layer is simply not engaged, and the fleet keeps its --adapter / --model behavior.

Breaking change. fleet.model in .adlc/config.json is now ignored with a warning, like fleet.adapter before it. It was previously honoured as "non-executable data" — safe only by accident, because the claude-code, codex, and opencode adapters silently dropped it. Now that every adapter forces its model onto the command line, a repo-committed value would be a candidate tree choosing the model that builds and judges it. Use the operator-local --model flag.

Go deeper

packages/quartermaster · the operating-stack spec

On this page