Approval
An operator does not approve a vague feeling that "this plan looks fine." Treating human-in-the-loop as a rubber stamp on a chat summary is security theater: the model promises one thing, executes another, and the audit log records a human signing off on an uninspected blast radius.
Here, an operator approves an enumerable, closed claim: this exact set of authorities, over this byte-for-byte content.
const record: ApprovalRecord = {
planId: 'archive-q3',
digest: state.digest, // the content they were shown
authoritySet: [ // what it may do
{ capability: 'file', scope: '/reports/q3', verb: 'read' },
{ capability: 'file', scope: '/backup', verb: 'create' },
],
decidedBy: 'operator@example.com',
decidedAt: new Date().toISOString(),
disposition: 'approved',
}
const result = await orchestration.approvePlan('archive-q3', record)The authority set
computeAuthoritySet traverses the folded plan and computes the canonical union of what every reachable call claims. It does not care what an agent says it intends to do; it extracts what the IR will actually execute:
- De-duplicated and sorted, deterministically. Two plans whose authors listed identical claims in different orders yield the exact same set. If they did not, the operator UI would present cosmetic differences as separate sets, and set-equality checks would fail on array ordering.
- Reachable only. An unreachable node cannot execute. Deriving authority from dead code tricks an operator into approving ambient privileges the plan can never exercise. We prune unreachable claims before the review surface is built.
- Optionally filtered. Pass
alreadyLive, and any claim your backing authority layer reports as already granted is omitted from the prompt. The operator reviews only the net-new delta requiring a decision. That filtering governs the display; it never weakens the validation gate, which checks the full reachable set against the plan.
The verb set is closed — list, read, create, update, delete. Open-ended verb vocabularies make authority sets impossible to compare mechanically, which reduces policy enforcement back to string matching and hope.
Set equality, before the store is touched
approvePlan recomputes the authority set directly from the folded log and asserts strict set-equality against record.authoritySet. If the record misses a single claim or invents an extra one, the operator signed something other than what the plan executes. The gate rejects the operation before writing a single byte to the store:
// The plan grants file:read AND file:create; the record names only one.
const result = await orchestration.approvePlan('archive-q3', narrowRecord)
// result.ok === false — and the plan is still `reviewable`, untouched.Failure returns a value, not a thrown exception — a TransitionResult-shaped refusal that callers handle through normal control flow. Because TransitionResult carries no arbitrary message field, an authority mismatch reports as digest_mismatch. That is not a workaround: an altered authority set is, by definition, different content.
Activation is strictly all-or-nothing. A rejected approval leaves plan state, digest, and revision completely untouched.
We are about to state an opinion
Test the authority gate against approvePlan, never against transition. By contract, the store checks only lifecycle state and digest. It does not evaluate policy, and it cannot: deciding whether a tool is on a tier-C allowlist or whether a reference taints a call argument requires battery knowledge that a BYO storage adapter does not have. The battery validates; the store commits. A test asserting the authority rule against transition tests the wrong layer and passes for the wrong reason.
What a forged record can and cannot do
We are about to state an opinion
approvePlan is the only supported route to executable, and the reason is worth being blunt about rather than leaving implied.
A caller who skips it and invokes the store's transition directly — with a hand-built ApprovalRecord and a valid expectedDigest — will have that record persisted. transition proves the digest and the lifecycle state. It does not check authoritySet, and it cannot: recomputing that set means walking the graph and knowing what a call node is, which is battery knowledge a BYO store does not have. That is the same split as everywhere else here — the battery validates, the store commits.
What that forged record does not do is grant anything. The executor never reads authoritySet; what a run may invoke is bounded by the tier-C allowlist enforced at freeze. A plan naming a tool outside that allowlist is refused before it can be approved at all, and a plan cannot be edited after approval without an unfreeze that sends it back through the gate.
The damage is to the audit trail. readApproval hands back what was written, so a forged record tells anyone reading it that an operator approved something they never saw. If you display or audit an approval, recompute the set with computeAuthoritySet and compare — do not trust the stored copy to describe the plan.
The digest binds it
The record carries the digest of the content the operator inspected, and the transition passes it as expectedDigest. This closes the stale-approval race by construction:
- Actor A reads the plan at digest D1 and begins review.
- Actor B unfreezes, mutates the plan, and refreezes — now at digest D2.
- Actor A submits an approval carrying D1.
- The store rejects the write: the plan is no longer at D1.
Actor A never approves content nobody showed them. That is the entire reason approval binds a content digest rather than a mutable plan ID.