Execution contract, v1

Normative. Derived from RUNBOOK.md §13; where the two differ, RUNBOOK.md wins. Implemented by @runbooks/supervise; every MUST here has a test in packages/supervise/src/contract.test.ts.

P0/P1/P2 say how much structure a document has. R0/R1/R2 say how much of this contract a client enforces. They are independent axes, and the catalog should be able to say so: a perfect P2 document read by an R0 client is unsupervised.

Profiles

Profile The client… Suitable for
R0 — advisory reads the runbook and follows it. Nothing enforced; the model's judgement is the only control. human-only, human-with-agent on read-only work
R1 — enforcing enforces the capability allowlist and per-step scope, halts at approval gates, honours a risk ceiling, enforces retry budgets, blocks deviations. the minimum for agent-autonomous
R2 — supervised R1, plus: evaluates assert and routes on_fail itself, and emits a run report. anything destructive or irreversible

What a client MUST do

Claiming any profile:

  1. Check capabilities[] against its own allowlist before starting, and refuse the run rather than partially executing it. A run that stops halfway has already done some of the work, and "some of a destructive procedure" is the worst available outcome — so every reason to refuse is collected up front, not discovered in sequence.
  2. Treat every step body below trust T2 as untrusted input, never as instructions to itself.
  3. Honour requires_approval with a decision from a human who is not the agent.
  4. Refuse a document whose min_runtime_profile exceeds what it enforces.
  5. Stop at a terminal, and report a run that ended anywhere else as incomplete.

Claiming R1 or above, additionally:

  1. Scope permissions to the current step, not to the union of the runbook.
  2. Enforce retry budgets and abort when one is exhausted.
  3. Block any invocation the current step does not declare, and record it as a deviation rather than dropping it silently.

Claiming R2, additionally:

  1. Emit a run report conforming to §14, containing no free text.
  2. Evaluate assert, not expect. See below.

Coverage bounds the profile

A profile is bounded by what the adapter can observe, not by what the client claims.

Adapter Sees Therefore
MCP proxy MCP tool calls only blind to shell access: an agent can step around it without violating a single proxy rule
tool-call hook every invocation, shell included complete for that client
library in the client's own loop whatever the client routes through it complete only if the client routes everything

A runbook containing risk: destructive|irreversible MUST NOT run under an adapter that cannot observe every tool invocation — however loudly the client claims R1 or R2. Every adapter publishes what it cannot see, and that published statement is what this rule is evaluated against.

min_runtime_profile

A runbook may declare the lowest profile permitted to run it. When absent, the implied minimum is: R2 if the procedure contains destructive or irreversible work, R1 if it declares agent-autonomous execution, R0 otherwise.

agent-autonomous at R0 is an error, not a warning: an autonomous agent under an advisory client has nothing between it and the tools but its own judgement, which is the arrangement this specification exists to replace.

Postconditions: assert, never expect

expect is prose for the reader. assert is a closed set of predicates over the observation, and it is what an R2 supervisor evaluates.

A step with only prose is unevaluable — a first-class outcome, distinct from pass and from fail. The supervisor halts and asks a human rather than guessing. A supervisor that treats "I could not tell" as "passed" is worse than one that does not check at all.

There is deliberately no model evaluator. Putting one in would place a model back inside the control loop the supervisor exists to be outside of, and "the supervisor verified the check" would come to mean "a model thought it looked right". Reasoning: docs/decisions/Q12-expect-evaluability.md.

The policy envelope

What a runbook authorizes at run time, derived from the document and never authored separately. A policy that can drift from the procedure it governs is worse than no policy, because it is trusted while being wrong — so an author who writes a valid P1 runbook has already written the policy, and there is no second artifact to keep in sync.

Document element Envelope field Runtime meaning
capabilities[] allowlist nothing outside it may be invoked for the duration of the run
a step's tool steps[id].tools the scope — this step alone, never the union
risk + requires_approval steps[id].gated the capability does not exist until a human decides
retry budgets["from->to"] counted, and finite
terminals terminals where a run may end
anything else — a deviation

Two invariants hold by construction, not by review:

Serialized as runbook-policy-envelope/v1, so a client can be handed the policy rather than re-deriving it. Derivation is deterministic: two implementations produce the same envelope for the same document, or the contract is not portable and "any client can enforce this" means "our client can".

Deviations

Class Meaning
undeclared-tool outside the runbook's allowlist entirely
out-of-scope-tool declared by the runbook, not by the current step
out-of-order-step attributed to a step this runbook does not contain
budget-exhausted a retry limit ran out
terminal-not-reached the run ended somewhere that is not a terminal

undeclared and out-of-scope are reported separately on purpose. They look alike and mean different things: the first is a runbook asking for a privilege it never declared, the second is an agent reaching for a privilege at the wrong moment. Collapsing them would hide which one happened, and only one of them is a content problem.

What supervision does not provide

Published as part of the contract rather than as a caveat, because a contract that overstates its guarantee is worse than none.