Pattern · last reviewed 2026-07-13

Human-in-the-loop approval boundaries: which agent actions need a human sign-off

What this pattern is

Human-in-the-loop approval boundaries are the rule that decides, per action, whether an agent may act alone or must wait for a person. You classify every tool the agent can call by two axes — how reversible the action is and how large its blast radius — and assign each a boundary: act automatically, act and notify, require approval before acting, or forbid entirely. The point is not to review everything; it is to spend human attention only where an action is both hard to undo and wide-reaching, and to design the queue so that attention stays sharp rather than degrading into rubber-stamping.

This pattern is layer 7 of the AI quality stack: human-in-the-loop. Guardrails filter content the model produces; HITL is the layer that catches the well-formed, unflagged action — the correctly-phrased request to issue a refund or delete a record — that no content filter was ever built to judge.

Part of the AI quality stack — the layered gate chain for knowing your LLM is delivering quality.

Context and problem

An agent that can only read is safe and nearly useless. The value shows up the moment it can act — issue the refund, merge the PR, send the email, delete the stale bucket, apply the Terraform plan. Each new tool you mount widens what the agent can accomplish and, in exactly equal measure, what it can accomplish wrongly off a hallucinated argument, a prompt-injected instruction, or an ambiguous request it resolved the wrong way.

The naive responses both fail. Gate everything behind a human and you have rebuilt a form with extra steps — the agent's autonomy is gone and reviewers learn to click "approve" without reading, because the ninety-ninth low-stakes prompt this week has trained them that approval is a formality. Gate nothing and you are one bad tool-call away from a refund to the wrong account or a DROP against production. This is precisely OWASP's LLM06:2025 Excessive Agency: damaging actions performed in response to unexpected, ambiguous, or manipulated model output, whatever caused the model to misfire. The pattern exists to place the human at the few points where their judgment actually changes the outcome, and nowhere else.

Forces

  • Autonomy vs. safety. Every approval gate is latency and a human in the critical path; every ungated action is trust you cannot claw back once it executes.
  • Reversibility vs. blast radius. These are independent. A reversible action with enormous reach (paging every on-call engineer) and an irreversible action with tiny reach (deleting one throwaway file) are different risks and want different gates.
  • Coverage vs. fatigue. The more you route to humans, the less each review means. Attention is finite; a queue that cries wolf gets rubber-stamped, which is worse than no queue because it looks like control.
  • Speed of the loop vs. auditability. Synchronous approval blocks the agent mid-task; asynchronous approval keeps it moving but demands durable state, a record of who decided what, and a way to resume.

The pattern

Score each action the agent can take on two axes and route it to one of four boundaries. Reversibility asks: if this is wrong, can we undo it, and how cheaply? (A draft is free to undo; a sent email or a processed payment is not.) Blast radius asks: how many users, records, or systems does one call touch? The two multiply into a risk tier, and the tier picks the boundary:

  • Auto — reversible and narrow. The agent acts with no human involvement. Reads, drafts, idempotent writes to scratch state. Most calls should land here or the pattern has failed its own cost test.
  • Notify — reversible but wide, or narrow but worth a trail. The agent acts, then posts what it did to a channel a human watches. No one blocks; someone can intervene or roll back after the fact.
  • Approve — irreversible or wide enough that undo is expensive. The action is proposed, not taken; it waits in a queue until a person confirms or rejects. This is the narrow band the whole pattern is built to protect.
  • Forbid — no legitimate task needs this action from this agent. It is not a tool the agent has. Removing the capability is stronger than gating it, because a gate is code that can be bypassed and a missing tool cannot be called at all (least privilege — OWASP's first mitigation for LLM06).

The classification is a design-time decision baked into the tool layer, not something the model decides at runtime — the model is exactly the component you do not trust to grade its own actions. Confirmation and audit logging live outside the model, in the tool wrapper or the orchestration layer, so a jailbroken prompt cannot talk its way past the gate.

An agent action is scored by reversibility and blast radius, then routed to one of four approval boundaries: auto, notify, approve, or forbid. Agent proposes an action Classifier reversibility × blast radius Auto act, no human Notify act, then tell a human Approve queue; wait for sign-off Forbid tool not mounted every branch writes an audit record

Reference implementation notes

The classifier is yours to design; the confirmation and audit mechanics differ by platform. Only the parts that differ are worth writing down.

Anthropic

Claude Code ships this pattern as a first-class permission system, which makes it a usable worked example even if you are not building on it. Its permission modes set the baseline. There are six, and the two usually left out of summaries are the interesting ones: default asks before each edit, shell command, or network call; acceptEdits auto-approves file writes and common filesystem commands but still gates other Bash; plan never auto-approves a file edit, routing writes to your callback while read-only tools run as in default; dontAsk converts every prompt into a denial, so anything not pre-approved is refused rather than escalated; bypassPermissions approves everything that reaches it (isolated sandboxes only); and auto hands the prompt to a model classifier that approves or denies. That last one is worth sitting with on a page about human-in-the-loop: it is the vendor shipping a model in the seat the human used to occupy, which makes the tier boundaries below a design decision rather than a default. On top of the mode you layer allow / ask / deny rules per tool — which is exactly the auto/approve/forbid classification, expressed as configuration rather than model judgment. The property worth stealing is the layering: explicit deny rules are the hard forbid boundary, while the mode only sets the default posture — and bypassPermissions approves everything the deny rules don't catch, which is why it belongs only in isolated, throwaway sandboxes. When you build your own agent on the Claude Agent SDK, a canUseTool callback returns your boundary decision for calls the rules haven't already resolved — an auto-approved call never reaches it, so canUseTool is your ask-tier classifier, not your forbid boundary; mandatory enforcement stays in deny rules (or a PreToolUse hook that runs on every call).

# settings.json — the classification as config
{
  "permissions": {
    "allow": ["Read", "Grep", "Bash(npm test:*)"],
    "ask":   ["Bash(git push:*)", "WebFetch"],
    "deny":  ["Bash(rm -rf:*)", "Read(./.env)"]
  }
}

AWS

Amazon Bedrock Agents expose two distinct mechanisms, and the distinction matters. User confirmation is the approve boundary built in: enable it on an action-group function and the agent, instead of invoking, returns a confirmation question for the end user to confirm or deny before the function runs — enabled in the console, CLI, or SDK. Return of control is the more general escape hatch: rather than executing the action itself, the agent hands the collected parameters back to your application so you run whatever validation, queueing, or human review you want before executing. Use user confirmation for a simple inline yes/no; use return of control when the approval is asynchronous or needs to route through your own queue and identity system. Both keep the irreversible act outside the agent's autonomous loop.

Cloudflare

The approve boundary needs somewhere durable to hold a proposed action while a human is away — a synchronous prompt does not survive a page reload or a five-hour delay. On Cloudflare, a Durable Object per agent (or per pending decision) is the natural home: it holds the proposed action, the state to resume from, and the decision, with strong single-object consistency so two reviewers cannot both act on one item. Queues carry the notify-boundary side-effects — post-hoc alerts, audit fan-out — without blocking the agent, and Workflows give you durable, resumable execution for the approve-then-continue case. The agent parks the action, the human approves through a separate Worker route, and the object resumes the task from where it stopped.

Trade-offs

  • The classification is judgment you own forever. Reversibility and blast radius are not machine-derivable in general; a human sorts each tool into a tier at design time, and the mapping needs revisiting whenever tools or their downstream permissions change.
  • Approve gates add latency and a human dependency. An agent that stalls waiting for sign-off is only as fast as its slowest reviewer. If most of your value is in that band, the agent may not be the right tool at all.
  • Asynchronous approval is real infrastructure. Durable state, resumption, idempotency (the human approves once; the action must execute exactly once), and identity on the approver. This is more than a boolean.
  • Fatigue is a slow leak, not a crash. A queue that routes too much trains reviewers to approve reflexively, and you will not see it in any metric until an obviously-wrong action sails through. Guarding against it is ongoing tuning, not a one-time setting.

When not to use this

Skip approval boundaries entirely when the agent has no irreversible or wide-reaching action in its whole tool set — a read-only research or summarization agent needs auditing at most, not gating, and adding approval prompts there is pure fatigue-generation that devalues the gates that matter. Skip them, too, when the right answer is forbid rather than approve: if an action is dangerous enough that you would review every single invocation, and the agent has no legitimate autonomous use for it, remove the tool instead of gating it — a capability the agent does not have cannot be jailbroken into using, and an unremovable human bottleneck disguised as automation is worse than an honest manual process. And do not reach for a human queue where a deterministic guardrail is stronger: a hard spending cap, a row-count limit on a delete, or an allow-listed set of recipients enforces the boundary without a person and without the injection surface that a natural-language confirmation step reintroduces. Human-in-the-loop is for genuine judgment calls, not for rules a validator can encode.

As-built evidence

aiArch runs a modest, partial version of this pattern. Agent-drafted editorial content does not publish itself: a draft lands in a review queue (the review_queue table), and the owner is the one who approves or rejects it from the admin dashboard before anything goes live. Approval is what is wired to trigger the side effects — publish-on-approve is coded to purge the page's edge cache and re-index the guide corpus into Vectorize (src/lib/publish.ts), each best-effort behind its own credentials and bindings, so a missing one degrades to a reported skip rather than a broken approval.

Where that instance actually stands, because the gap between wired and exercised is the whole point of an as-built section: the approve route is wired end to end and has never run in production — no draft has yet been decided through it — and the cache-purge credentials are deliberately not minted, so that effect is a permanent reported skip rather than a pending task. Neither costs us anything today: the pages in question already serve Cache-Control: max-age=0, must-revalidate, which makes the purge an optimisation, not a correctness requirement. What does hold is the boundary's shape — one owner-only reviewer, and a claim-before-act guard that transitions only a row still in draft, so a double-approve touches zero rows and cannot fire the side effects twice. Email is gated on the same principle but not uniformly: a marketing send (the weekly Brief, which has never been sent to a real list) is gated on explicit owner sign-off for that specific send, while transactional service mail — the subscribe welcome, opt-in review-due nudges — dispatches automatically, and claiming otherwise would overstate the gate.

The boundary that has paid for itself most often, though, is not on a tool the agent calls at runtime — it is on the agent's ability to edit the instructions the next agent will read. Our retrospective skill analyses a working session and proposes changes to the repo's own agent-instruction files. On one run it produced a finding that was simply false: that a production database was unreachable and the documented capability was stale, built on a single transient error and a truncated diagnostic that had cut off the answer. The finding survived analysis, survived the write-up, and arrived as a proposed edit to the instruction file. The step it could not pass was the one that says propose, do not apply. The owner read it, said the capability had been working, a retest inverted the finding, and the report was rewritten. Nothing else in that chain would have caught it, because every earlier stage was the same agent agreeing with itself.

Two things in that generalise past our setup. First, the approve boundary earns its latency exactly where the human holds information the agent cannot check — here, a memory of the tool working last week, against which no amount of re-reasoning would have helped. That is a sharper test for the approve tier than "important": can the reviewer contribute evidence the agent structurally lacks? If not, you are buying a rubber stamp. Second, a doc edit scores as reversible on the classifier's first axis and therefore looks like auto or notify — you can revert a file in seconds. That reading is wrong, because the blast radius of a wrong instruction is not the file; it is every future run that reads the file and acts confidently on it, and those runs do not revert. Weigh reversibility of the effects, not of the write. Writes to durable instructions, prompts, policies and configuration belong in the approve tier for that reason, however cheap the undo looks.

The complementary move is to take the whole question off the table for agents that never need to act. Our Monday status roll-up is strictly read-only by construction: it reads the four desks, writes one report, and holds no write path to the roadmap, the task files, or anything else. The alternative — a roll-up that also drives the Monday sequence it reports on — was considered and rejected, and the design was given a success criterion that makes the boundary checkable rather than aspirational: no run ever leaves a diff beyond its own report file. Splitting inform from act this way is upstream of the four boundaries in this pattern. An agent with no write path needs no classifier, no queue, and no reviewer attention, so the cheapest approval boundary available is usually the decision not to mount the tool — the same reasoning as forbid, applied to an entire role instead of a single action.

Changelog

  • 2026-07-13 — Initial publication. Verified against OWASP LLM06:2025, Claude Code permission modes, and Amazon Bedrock user confirmation / return of control.
  • 2026-07-26 — As-built section corrected against production. The approve route is wired but has never executed; cache purge is a permanent credential-gated skip; the re-index target is Vectorize, not AI Search; and the email gate is per-send only for marketing mail.
  • 2026-08-10 — Added the approval boundary we actually exercise: propose-don't-apply on agent edits to our own instruction files, and the false finding it caught. Two refinements follow — the approve tier is worth its latency where the reviewer holds evidence the agent structurally lacks, and reversibility should be scored on an action's effects rather than on the write, which moves durable instructions and configuration into the approve tier. Added the inform/act split as the boundary upstream of all four: our read-only status roll-up, with the rejected alternative and the zero-diff success criterion that makes it checkable. Also corrected the Claude Code permission-mode enumeration, which named four modes when the vendor documents six — auto (a model classifier in the approval seat) and dontAsk (deny instead of prompt) were both missing — and sharpened the plan description, which said it forbids all execution when read-only tools in fact run as in default mode. Re-verified against the permission-modes and Agent SDK permissions docs on 2026-08-10.
Sources and provenance
  • Excessive Agency — the three root causes (excessive functionality/permissions/autonomy), least-privilege as the first mitigation: OWASP Top 10 for LLM Applications (2025), entry LLM06:2025 Excessive Agency, checked 2026-07-13.
  • Claude Code permission modes — all six (default/acceptEdits/plan/auto/dontAsk/bypassPermissions), deny/ask precedence in every mode, protected paths: Claude Code permission modes docs; the six-step evaluation order and the canUseTool equivalent for custom agents: Agent SDK permissions. Both re-verified 2026-08-10.
  • Bedrock user confirmation before invoking an action-group function: AWS Bedrock user-confirmation docs; return of control: AWS Bedrock return-of-control docs; HITL patterns write-up: AWS ML Blog, human-in-the-loop confirmation (Apr 2025).
  • Durable Objects and Queues as approval infrastructure: Cloudflare Durable Objects and Cloudflare Queues docs.
  • As-built: aiArch's editorial review queue and publish-on-approve are first-person, with the limits stated above — the review_queue table and src/lib/publish.ts in this platform's own codebase, verified against production state on 2026-07-26.
  • As-built: the propose-don't-apply boundary and the false finding it stopped are recorded in our own 2026-07-16 post-mortem of the retrospective skill, including the truncated diagnostic that produced the claim and the owner challenge that inverted it. The read-only status roll-up, its rejected orchestrating alternative, and its zero-diff success criterion are a 2026-07-14 entry in this platform's decision record. Both stated as first-person practice.

OWASP numbering and Bedrock feature names are the facts here most likely to drift — re-verify LLM06's designation and the Bedrock confirmation APIs before citing anything newer. Corrections: hello@aiarch.dev.

Learn to threat-model an agent, not just wire one.

aiArch teaches agent safety, least-privilege tool design, and human-in-the-loop gating by building — the same discipline this platform runs on its own content pipeline.

Free sample — no signup · every claim cited · full curriculum is waitlist-only