ADR-063: Autonomy is declared in system.yaml; an engine is bound by a contract and only signals can act before a merge
Status: Accepted (2026-10-27)
Builds on: ADR-053: A domain model based on Backstage’s system model, with environments (the domain model), ADR-054: The 5.0 interface: system.yaml checked by the renderer, noun plugins with tombstones, and a migration that proves itself (the renderer is the only validator of system.yaml), ADR-032 — Every run is recorded in a private run store (run records), ADR-055: The local console is a stdlib web server over cplat JSON; it acts only through Claude Code (the console acts only through slash commands), spec 124
Context
Section titled “Context”Spec 124 lets an always-on engine fix the running system without a person. In 5.x that engine is ai-ops, a separate repository built from service-go. Before this, its AutonomyPolicy lived in a cluster resource, which could disagree with the promotion rules in system.yaml and could not be reviewed. Four choices in the spec outlive it:
- Where the policy lives and who validates it.
- How an engine reads it. An engine in Go cannot run
cplat, and recomputing defaults and effective levels in the engine gives two definitions that drift apart. - How a person stops it in an incident. Every other change on the platform waits for a merged pull request (AC-122.18).
- Where an engine’s history lives. The engine has its own tamper-evident store, but the console and
cplatmust still work when the cluster is down.
Decision
Section titled “Decision”- The policy is part of
system.yamland the renderer validates it.environments.<env>.autonomy(level, guardrails,engineaddress), a top-levelautonomy: {paused: true}, and a per-instance capcomponents.<c>.instances.<env>.autonomy: {max}that can only lower the level. Prod is capped atact.promote(spec 093) still decides every promotion, at any level. An engine never promotes forward. Its only pin change is the rollback of a failed dev smoke to a tag dev ran before. - The renderer writes the effective policy as one JSON file,
applications/<system>/autonomy.json, when a system declares autonomy. It holds the defaults filled in, the effective level of each environment and instance, and the promotion rule. It equals the autonomy part ofcplat model show --json, and the engine reads it atmainwith that commit’s SHA. A missing file meansobserveeverywhere, so systems without autonomy render as before.cplat’sautonomy.pyholds the same table, and a test keeps the two equal. - Signals act at once, but only downward, and a pull request records each one.
pause,resumeandlowergo over HTTP to the environment’s engine. A signal can only set an environment at or below the merged policy:lowercarries the tighter value of each field, the engine refuses one that loosens anything, andresumeonly lifts a pause back to the merged policy. Every signal is followed by a pull request, so git stays the record. These are the only changes that act before their pull request merges. - The engine writes its runs and audit events into the platform’s private run store. Runs go to
runs/(kindfix,actor= the engine) and audit events toaudit/, each with the policy level, the guardrails and the commit that allowed it, plus a reference into the engine’s own tamper-evident store.cplatreads history only from the run store. Only the heartbeat and the signal answer come from the engine directly.
Consequences
Section titled “Consequences”- A person reviews every change to autonomy as a
system.yamldiff, and every tool reads the same effective values. A gitops repo needs the renderer of this release before it can declare autonomy. - A second engine (another vendor) can serve a system by implementing the same contract:
autonomy.json, the signal and status endpoints, the issue format, and the run and audit records. Nothing in the platform names ai-ops except as the example. - Raising autonomy always waits for a merge, while lowering and pausing never do. When an engine cannot be reached, the pull request still records the person’s intent, and the console shows the environment as not confirmed until the engine catches up.
- The run store gains a rewritten record kind (engine runs are rewritten per phase) and a second prefix (
audit/).cplat platform runs reportcounts engine runs like any other run. - The engine’s tamper-evident store stays the authority for its own integrity. The run store is the readable copy the platform depends on.
Not included
Section titled “Not included”- How an engine detects, triages and fixes. That is specified in the engine’s repository against this contract.
improvebeyond acting asact, which waits for a later spec.- Promotion to prod by an engine, at any level.