Reference/Safety
Human approval gates
Approve a digest of tool+args, not a bare id. Mutate never runs on missing/deny/tamper. Stop while waiting.
HUMAN_APPROVAL is a map the model cannot write.
Record
{action_id: {decision: "allow"|"deny", digest: sha256(canonical)}}
Canonical JSON: id, tool, args with sort_keys=True.
execute() outcomes
| Situation | Status | World |
|---|---|---|
| no record | needs_approval | unchanged |
| deny | denied | unchanged |
| digest mismatch | tamper | unchanged |
| allow + match | applied | changed |
| unknown tool | unknown_tool | unchanged |
Invariants
- No mutate without allow + matching digest
- No auto-allow on sev1
- Expiry (stale deploy_id / old step)
- Args still range-checked (replicas 1–10) even if approved
- Waiting is terminal for this run — write a report, do not spin
Who writes the map
Slack button, UI, or you in a Try it box. Never a tool set_approval.
Dual control
Two allow signatures. Same digest. all(decisions == allow).
Tip:Unit-test the gate with an empty map, a matching allow, and a tampered args object. Three tests, one invariant.