Approval gates and human-in-the-loop
Scoped IAM (Chapter 36) and injection containment (Chapter 37) bound what an agent can do. Some actions are consequential enough that "bounded" is not sufficient and you want a human to say yes before they happen: spending real money, sending an external message, deleting production data, deploying code. The approval gate is that checkpoint, and this chapter is about building it so it actually protects, which is harder than it sounds, because a gate that fires too often trains people to approve without looking, and then you have the cost of the gate with none of the safety.
What to gate, and what not to
The instinct to gate everything is as wrong as gating nothing, and the production bar's gate 10 named the axis that sorts them: irreversibility crossed with reach. Gate the actions that are hard to undo and touch the world outside the platform; leave the rest ungated, because every unnecessary gate spends the one resource gates depend on, human attention.
| Action | Reversible? | Outward-facing? | Gate it? |
|---|---|---|---|
| Read a metric, search logs, analyze data | n/a | no | No: this is the free half of the effects-are-gated tenet |
| Write to the agent's own scratch state | yes | no | No |
| File an internal ticket | easily | barely | Usually no (log it, let a human dismiss it) |
| Open a pull request | yes (review before merge) | somewhat | Often no: the PR is the gate; a human reviews before merge |
| Send an external email or message | no (it is out) | yes | Yes |
| Spend money, place an order | hard | yes | Yes |
| Delete data, deploy to production | hard | yes | Yes, and often with a second reviewer |
The pattern the table encodes: prefer designs where the natural
workflow is the gate. An agent that opens a pull request rather than
pushing to main has a human review step built into the tool's
normal use, no separate approval machinery required. When you can
turn a gated action into a proposal that a existing human process
consumes, do that instead of building a gate; it is the same safety
with none of the fatigue.
The mechanism: pause, park, resume
When a genuine gate is needed, the engineering problem is that
approval can take minutes, hours, or a weekend, and you cannot hold a
running process open that long. The event-sourced
design solves this cleanly: the gate is an event, and the run's
state is durable across the wait. The agent reaches a gated action,
writes a approval.requested event with the proposed action and its
evidence, and the run parks, holding no compute. A human (via a UI,
a Slack message, an email link) approves or denies, which writes an
approval.granted or approval.denied event, and the run resumes
from exactly where it paused, the replay machinery
reconstructing its state.
On AWS the decoder ring already named the
primitive: Step Functions' waitForTaskToken. The workflow reaches
the gated step, emits a task token, and stops billing until
something calls back with that token, which is precisely a durable
pause waiting on a human. The approval UI (or a Lambda behind a Slack
button, or an email link) posts the token back with the decision, and
the workflow continues. Parked-in-a-queue variants work too; the
shape is identical: a durable request, an out-of-band decision, a
resume keyed to the request.
Two properties the mechanism must have, both non-negotiable. The gate is enforced by the platform, not the prompt. "Ask the human before spending money" in the system prompt is a suggestion an injection can override; a Step Functions state that cannot proceed without a token is a wall. This is rung four again: the gate lives outside the model's reach. And every gate decision is an event. Who approved, when, what exactly they approved, and the evidence they saw, all in the durable ledger, because the audit trail is half the reason the gate exists. A gate with no record is theater.
Gate fatigue is the real failure mode
Here is the thing most designs get wrong, and it is a human problem, not a technical one: a gate that fires constantly stops being a gate. Present someone with fifty approval requests an hour and they will approve all fifty in a rhythm, glancing at none, and the one malicious request rides through with the rest. You have built the machinery of safety and the reality of a rubber stamp, at the cost of everyone's afternoon.
The defenses are design, not exhortation:
- Gate rarely. Every ungated action you can safely allow (by scoping, by reversibility, by making the workflow the gate) is attention preserved for the gates that matter. A platform that gates ten things a day gets careful review; one that gates ten things a minute gets none.
- Make the decision cheap and complete. The approval request must carry everything the human needs to decide in seconds: what will happen, to what, why the agent proposed it, and the evidence, linked to event ids. A gate that requires the approver to go investigate is a gate they will learn to skip.
- Batch and risk-tier. Group low-risk approvals for periodic review; reserve interactive, one-at-a-time gates for genuinely high-stakes actions. Not every gate deserves an interrupt.
- Watch the approval rate as a metric. An approver saying yes to 99.8% of requests is not a careful reviewer; they are a rubber stamp with a pulse, and the gate is not working. Part 9 monitors approval rates precisely because a too-high one is a silent failure of the whole control.
Where gates sit in the machine
Approval gates compose with everything Part 6 built. A gated action is a work unit that, instead of executing, emits its request and parks; the scheduler tracks parked units the same way it tracks running ones (a lease, a budget hold); a decision resumes the unit. In a fleet, gates are per-unit and asynchronous, so five hundred agents can run while three of their proposed effects wait on a human, and the multi-tenant chapter will add that gates, like budgets, are per-tenant. The gate is not a special case bolted onto the platform; it is a work unit whose executor is a person.
Don't be confused: a gate vs a guardrail. A guardrail (Chapter 13) is an automated filter that runs on every relevant call and decides in milliseconds; a gate is a human checkpoint that runs on the few actions worth a person's judgment and may take a weekend. Guardrails scale and never tire but only catch what they were built to catch; gates catch anything a human would question but cost attention and cannot scale. You use guardrails to reduce how often you need gates, and gates for the residue too consequential to automate. Substituting one for the other, gating everything until humans rubber-stamp, or guardrailing a decision that truly needs judgment, breaks both.
👉 Next: multi-tenant isolation, the last trust chapter, where one platform serves many customers and every control so far, budgets, data, sandboxes, quotas, gains a per-tenant dimension.