Two-Person Approval for High-Risk Agent Actions
Some actions should never be unilateral. The approval flow, the queue, the timeout policy, and the change-management story you can show your auditor.
Which actions require two-person approval
Three classes of action need two-person approval. Irreversible actions (drop table, terminate instance, delete object, force push; anything that cannot be undone within the same run); high blast-radius actions (anything affecting more than one resource, anything affecting a critical-path service, anything affecting customer data); cost-impact actions (anything that could change the bill by more than $X, the threshold is policy and usually $1000-$10000).
- Irreversible. Drop table, terminate instance, delete object, force push; cannot be undone in the run.
- High blast-radius. Multi-resource, critical-path service, customer data.
- Cost-impact. Bill change above $X; threshold is policy ($1000-$10000 typical).
- Per-class documented threshold. The trigger threshold per class committed to policy.
The approval flow
The flow is structured. Agent proposes the action with structured output (what, why, blast radius, reversibility); proposal lands in an approval queue and two distinct identities (humans or other agents) must approve before execution; approval is logged with reason, rejection is logged with reason, both auditable.
- Structured proposal. What, why, blast radius, reversibility.
- Two distinct identities. Humans or other agents; same-person rejected.
- Logged approval and rejection. Both with reason; both auditable.
- Per-flow timeout. Pending proposals expire; supports correct queue management.
The queue
The queue is a simple table. proposal_id, action, status (pending/approved/rejected/expired), approvers, timestamps; the queue is read by the approval UI; pending proposals expire after a timeout (typically 30 minutes for triage, longer for non-urgent); expired proposals are not auto-retried but are reported to the team for review (expirations might mean understaffing or the proposal was wrong).
- Simple status table. proposal_id, action, status, approvers, timestamps.
- 30-minute timeout for triage. Longer for non-urgent; the queue is bounded.
- No auto-retry on expire. Reported to team; expirations indicate understaffing or wrong proposal.
- Per-queue health metric. Expiry rate tracked; supports continued tuning.
Audit story for the auditor
The audit story is straightforward. Every proposal, approval, rejection, and expiry is logged with timestamps, identities, and reasons; the audit trail is immutable (the approval system writes to an append-only log, modifications would themselves be audited); the auditor can reconstruct any past action’s approval chain in seconds, which is the test of a good audit story.
- Per-event log entry. Proposal, approval, rejection, expiry; timestamps, identities, reasons.
- Immutable trail. Append-only log; modifications would themselves be audited.
- Past-action reconstruction in seconds. The test of a good audit story.
- Per-action approval chain. Documented per action; supports compliance review.
Pitfalls to avoid
Three pitfalls deserve mitigation. Same-person approval (one identity approving in two roles): trivial to detect, the system rejects. Rubber-stamp approval (an approver who clicks yes on every proposal): track per-approver approval rates because high rates without rejections are a flag. Approval of bundles (“approve all 5 of these proposals at once”): each proposal is approved individually so the audit trail is meaningful.
- Same-person approval. Trivial to detect; system rejects.
- Rubber-stamp pattern. Per-approver rates tracked; high rates without rejections flagged.
- Bundle approval rejected. Each proposal individually; meaningful audit trail.
- Per-pitfall detection rule. Each pitfall has a documented detector; supports continued integrity.