ADR-0012: Context Compilation, Gating, Delivery and Runner Routing as They Stand
Status
Accepted — 2026-09-26. Supersedes ADR-0001, ADR-0003, ADR-0004, ADR-0005 and ADR-0006. Decision 4 superseded by ADR-0013.
Context
Five Accepted records describe behaviour the tree no longer has, or never had. An Accepted body is
immutable (.agents/skills/adr-scaffold/SKILL.md, "Immutability"), so the only allowed correction
is a superseding record. This record restates each decision as the code implements it. The five
older records keep their text; only their Status line changes, to Superseded by ADR-0012.
Measured at eed57331 (#352, the Ubuntu 26.04 runner and image baseline) plus the fixes on the
same branch as this record (the gating stage's function-length resolution and the pre-commit
context classification). The operational fork was read at <owner>/praetor main 88a39dd9.
| Record | Claim | Measured | Evidence |
|---|---|---|---|
| ADR-0001 | Aider is one of the covered vendors | compile-context writes six vendor files, none of them for Aider; the Codex target the record omits is one of the six |
internal/agentcontext/render.go:47-54 |
| ADR-0003 | the engine keeps "100% test coverage" and a "100% zero-debt baseline" | CI enforces a 65% total statement-coverage floor; existing debt is recorded in .standards-baseline.json and only new or touched-file debt fails |
.github/workflows/ci.yml:217, internal/gating/pipeline.go (runHissStage), cmd/standardsctl/audit.go (auditBaselineAndInvariants) |
| ADR-0003 | verified releases and signed tags flow downstream | no v* tag exists, so the tag-triggered release workflow has never produced a release (#205) |
git tag -l 'v*' is empty; .github/workflows/release-binaries.yml:3-7 |
| ADR-0004 | a 4-stage pipeline scanning HISS-01 through HISS-16 at <= 60 lines per function | six stages; the HISS scan emits HISS-01, 02, 04, 07, 08 and 09; the function-length limit comes from the repository's resolved policy | internal/gating/pipeline.go (executeStages, hissScanOptions), internal/hiss/rules.go, internal/hiss/go_ast.go |
| ADR-0005 | GitOps delivery through ArgoCD at deploy/k8s/application.yaml |
deploy/ in this repository holds only the Helm chart; the ArgoCD Application is in the operational fork; no workflow builds or publishes an image |
git ls-files deploy, <owner>/praetor:deploy/k8s/application.yaml, .github/workflows/*.yml, .goreleaser.yaml (no dockers section) |
| ADR-0005 | Praetor's own image builds from docker/dev/Dockerfile |
that file is the development container; the production image is build/package/Dockerfile |
docker/dev/Dockerfile, build/package/Dockerfile:3,32,47 |
| ADR-0006 | Linux jobs run on ARC scale sets for amd64, arm64 and GPU | the routing policy still resolves those names, but no workflow routes through it: CI runs on GitHub-hosted ubuntu-26.04, with an ubuntu-26.04/macos-26/windows-2025 portability matrix, and a guard test rejects any -latest runner alias; the operational fork defines arc-runner-set-linux-amd64 and arc-runner-set-gpu-xpu and no arm64 set |
internal/config/hierarchy.go:58-69, .github/workflows/*.yml (runs-on), .github/workflows/portability.yml (matrix.include), internal/forge/runner_label_guard_test.go, <owner>/praetor:deploy/arc/runner-scale-set.yaml |
| ADR-0006 | Darwin targets route to GitHub-hosted macos-14 and macos-13 |
the defaults are macos-26 for darwin/arm64 and macos-26-intel for darwin/amd64; macos-14 carries a deprecated badge in actions/runner-images and macos-13 is no longer published |
internal/config/hierarchy.go:39-69 (the comment cites the runner-images table) |
Decision
1. Context compilation (supersedes ADR-0001)
AGENTS.md is the single source of agent instructions. praetorctl compile-context writes exactly
six vendor files, each within the 300-line budget (internal/agentcontext/render.go:8):
CLAUDE.md, .cursor/rules/hiss-invariants.mdc, .github/copilot-instructions.md,
.windsurfrules, .gemini/GEMINI.md and .codex/rules.md. It projects each persona in
.agents/agents/ into .claude/agents/, .codex/agents/, .github/agents/ and .gemini/agents/
(internal/compiler/agents.go), and into the plugin copy under .agents/plugins/praetor/ when the
plugin manifest exists, together with the skills in .agents/skills/.
compile-context --verify checks every one of those outputs and fails on drift. The pre-commit hook
runs it for any change to a path it reads or writes (CONTEXT and CONTEXT_PREFIXES in
.config/lefthook/scripts/checks.py), and praetorctl audit verifies the six vendor files as well.
A vendor with no entry in vendorTargets is not covered; covering one means adding that entry.
2. Engine and operational fork (supersedes ADR-0003)
The two-repository topology stands. cordanaLLM/praetor is the engine; <owner>/praetor is the
operational fork that runs on workstations, carries operator data and reports demand through
.needs.yaml. Paths only the fork may carry are engine schema in
internal/operationalsync/overlay.go (ownerOnlyPrefixes), and the engine's .gitignore ignores
each of them.
Engine changes pass the gating pipeline (decision 3) and the debt ratchet: debt recorded in
.standards-baseline.json is tolerated, new debt and debt in a touched file fail. CI enforces a
total statement-coverage floor of 65% (.github/workflows/ci.yml:217), raised as coverage grows and
never lowered. Releases are cut by pushing a v* tag, which runs
.github/workflows/release-binaries.yml; none has been cut yet (#205), so nothing flows downstream
as a release today.
ADR-0003's checkable clause engine-scope-has-no-exempt-tier stays in force. praetorctl adr verify
replays it from ADR-0003, whose body is unchanged.
3. Gating pipeline (supersedes ADR-0004)
praetorctl gate run executes six stages in order and stops at the first failure
(internal/gating/pipeline.go, executeStages):
- Prefetch & Lockfiles:
.standards.yamland.standards.lockmust exist and be non-empty;go mod verifyandgo mod downloadrun where there is ago.mod. - HISS Invariant Scan: the scanner reports HISS-01, 02, 04, 07, 08 and 09. The function-length
limit is the one
config.ResolveRepositoryComplexityresolves, the same limitpraetorctl auditenforces for a locked repository. The stage fails on violations beyond.standards-baseline.jsonand on an incomplete scan. - Security & SCA Scan:
govulncheckandgosec -conf .gosec.json; a missing scanner or configuration fails the stage. - Flavor Conformance:
flavor.AuditFlavor; skipped, with the reason printed, where the declared profile has no flavor. - Race-Detector Tests:
go test -racein an isolated worktree; skipped with a reason on a dry run, withoutgo.mod, or without a working cgo toolchain. - Declared Verification:
make verify-allin an isolated worktree, where the root holds nogo.modorCargo.lockand the makefile declares the target; a non-zero exit fails the stage. Not applicable otherwise; it signs no receipt (#1218). - Ed25519 Exit-0 Receipt: signs the concatenated stage output, skip reasons included, with the
key
lockdown.LoadSigningKeyresolves; a missing key fails the stage and a dry run mints none.
The main ruleset requires a pull request and the CI status checks
(.github/rulesets/main.json), and CI's forge validate-pr step verifies the receipt in the pull
request body.
4. Container delivery (supersedes ADR-0005)
The production image is defined by build/package/Dockerfile: a digest-pinned golang: builder at
the go.mod toolchain version compiling with CGO_ENABLED=0, and a digest-pinned
gcr.io/distroless/static-debian13:nonroot runtime running as 65532:65532. The Helm chart deploy/helm/praetor sets
readOnlyRootFilesystem: true, a memory-backed emptyDir for scratch space and probes on
/livez and /readyz; praetorctl serve answers /healthz, /livez and /readyz on :8080.
No workflow builds or publishes the image. The chart's default image reference
(deploy/helm/praetor/values.yaml:3-6) therefore points at a tag nothing has pushed; an operator
builds and pushes the image, or overrides the reference, until a publish workflow exists. GitOps
wiring such as an ArgoCD Application is operator data and lives in the operational fork under
deploy/k8s/, never in the engine.
5. Runner routing (supersedes ADR-0006)
Runner routing is policy resolution, not CI configuration. config.LoadCascadingRunnerConfigContext
starts from DefaultRunnerPolicy (internal/config/hierarchy.go:58-69) and applies, in order,
.config/fleet.yaml, .config/orgs/<org>.yaml and the runners: key of .standards.yaml.
runner.ResolveRunner maps a target to a runner and rejects Darwin on a self-hosted ARC runner, and
praetorctl audit checks that five targets resolve (auditRunnerMatrix). No workflow in this
repository consumes the result: CI runs on GitHub-hosted ubuntu-26.04, and the portability
matrix on ubuntu-26.04, macos-26 and windows-2025.
The default policy routes each target as follows; every route is ephemeral:
| Target | Runner type | Label |
|---|---|---|
darwin/arm64 |
GitHub-hosted | macos-26 |
darwin/amd64 |
GitHub-hosted | macos-26-intel |
linux/amd64, and any target without a route (Default) |
self-hosted ARC | arc-runner-set-linux-amd64 |
linux/arm64 |
self-hosted ARC | arc-runner-set-linux-arm64 |
linux/gpu |
self-hosted ARC | arc-runner-set-gpu-xpu |
The Darwin labels name an explicit macOS 26 image rather than macos-latest, which would move the
whole Darwin tier the day GitHub promotes the next image; internal/config/hierarchy.go cites the
actions/runner-images table this rests on. ADR-0006's macos-14 and macos-13 no longer apply:
the first carries a deprecated badge there and the second is no longer published. The same rule
holds for this repository's workflows, where internal/forge/runner_label_guard_test.go fails on
any ubuntu, macos or windows -latest alias.
ARC scale sets are operator data (deploy/arc/ is an owner-only prefix). The operational fork
defines arc-runner-set-linux-amd64 and arc-runner-set-gpu-xpu. The default policy's
arc-runner-set-linux-arm64 has no scale set there, so a job routed to it by the defaults waits for
a runner until an operator defines that set or overrides the route.
Consequences
- Positive: every current-state claim in these five areas points at a file, a command or a test, and the superseded records say so on their first line.
- Positive: "operator data stays in the fork" is already a replayed check:
TestOwnerOnlyPrefixesAreIgnoredByTheEngine(internal/operationalsync/owner_only_test.go) fails when anyownerOnlyPrefixesentry is not ignored by the engine's.gitignore. - Negative: the image and ARC gaps remain gaps. This record states them; it does not add a publish workflow or an arm64 scale set.
- Neutral: ADR-0001 through ADR-0006 bodies are unchanged; readers of an older record follow its Status line here.
Checkable clauses
This record declares no adr-constraint block, on purpose. A forbidden-path clause for the
owner-only paths would fail in the operational fork: the fork tracks deploy/arc/, deploy/k8s/,
.config/fleet.yaml and .config/orgs/ by design, it receives docs/adr/ from this repository
through internal/operationalsync, and its make verify-all runs praetorctl adr verify, which
replays every record against git ls-files (internal/adr/checks.go, trackedPaths) with no
per-repository exemption. TestShippedDecisionRecordsAllowOwnerOnlyPaths
(internal/operationalsync/owner_only_records_test.go) replays every shipped record in a tree
shaped like the fork and fails on such a clause. The engine side is covered by the .gitignore
replay named under Consequences, which reads ownerOnlyPrefixes directly and so cannot fall behind
that list.
References
- Issue #355 (items 1 and 2), #205 (first release), #222 (operator data is configuration).
- ADR-0001, ADR-0003, ADR-0004, ADR-0005, ADR-0006 (superseded by this record).
Redactions
Identifier-only redactions under ADR-0014 §7. No decision, rationale, status or evidence path changed; line numbers refer to this file.
- 2026-09-27, line 16: the operator fork's owner account became the placeholder
<owner>. - 2026-09-27, line 24: the same owner account, in an evidence reference, became
<owner>; the file path after the colon is unchanged. - 2026-09-27, line 26: the same owner account, in an evidence reference, became
<owner>; the file path after the colon is unchanged. - 2026-09-27, line 48: the same owner account became
<owner>.