Human-in-the-Loop Approval Gates That Survive Failure

Human-in-the-loop approval gates need durable state, not better prompts — here is how to pick the right level so your agent runs never strand a decision.

Drawing an approval gate on a whiteboard takes a minute. Operating one takes a checkpoint store, an expiry policy, and an owner for the resume path. The collision in human-in-the-loop AI agent architecture is never the prompt; it is state ownership, because whoever holds the paused run also holds the audit trail, the retry semantics, and the blast radius of a lost event. Three levels cover most production systems: in-process framework interrupts, external callback tokens, and durable execution in the orchestration layer. Each moves the boundary, and each fails differently.

How Does Human-in-the-Loop AI Agent Architecture Survive Failure?

Survival comes from placing the paused run’s state in a system that outlives the agent process, because a framework interrupt saves data while a durable execution engine also detects the crash and re-enters the correct step. Temporal’s engineering blog draws this distinction directly: a LangGraph checkpoint preserves data, but a process failure still requires another system to notice, pick a re-entry point, and restart the graph (Temporal’s LangGraph integration).

That distinction is the whole essay. Checkpointing answers “what did the agent know?” Durable execution answers “who notices the agent died, and where does it resume?” A gate that only answers the first question will hold state correctly and still strand approvals when the worker is rescheduled, the pod is evicted, or the queue consumer restarts. Temporal’s post argues the recovery system must be at least as reliable as the agent it supervises, which is a strong claim about where the boundary belongs. The practical consequence: pick the level based on how long the wait can last, how ambiguous the side effects are, and who is on call when the resume path breaks. The AI stack reference is a reasonable map of the surrounding layers, and the AI tools reference covers the harness side.

How This Was Researched

This essay is based on official vendor documentation, framework documentation, and published specifications — no hands-on testing was performed. Source families reviewed: OpenAI Agents SDK docs, LangGraph docs, Temporal docs and engineering blog, AWS Step Functions docs, the MCP specification, Kubernetes admission-control docs, GitHub Actions environments docs, Argo Workflows docs, Anthropic’s engineering guidance, and NIST’s Generative AI Profile.

What was not covered: no load testing, no benchmark runs, no cost modeling beyond what sources state, and no private incident data. No cited source publishes approval-latency or gate-throughput benchmarks, so none appear here. Last researched: September 2026.

The Approval Gate Is a Distributed Systems Boundary

An approval gate is a distributed systems boundary because it splits one logical run across two failure domains — the agent process and the human decision — and the split must be mediated by durable state. Kubernetes admission webhooks make this explicit with a failurePolicy field, where Fail is fail-closed and Ignore is fail-open, plus a timeoutSeconds bound (Kubernetes — Admission controllers).

Once you accept the boundary framing, the design questions stop being about prompts. Who owns the pending record? What happens when the reviewer never answers? Is the resume idempotent against ambiguous external effects? A gate that pauses a tool call but stores the pending record in process memory is a gate that disappears on deploy. The Kubernetes model is instructive because it forces an explicit choice rather than an implicit default: the platform team writes down what happens when the policy engine is unreachable. Approval gates deserve the same written answer, and the answer differs by level.

Level One: In-Process Framework Interrupts

Level one uses framework interrupts: LangGraph’s interrupt() pauses graph execution, saves state through its persistence layer, and waits indefinitely until resumed, requiring a persistent checkpointer and a thread_id in the run configuration (LangGraph Interrupts).

config = {"configurable": {"thread_id": "run-42"}}
result = graph.invoke(input, config)
# inside a node:
decision = interrupt({"action": "delete_records", "count": 120})
# later, from another process:
graph.invoke(Command(resume="approved"), config)

The supplied value becomes the return value of interrupt(), payloads must be JSON-serializable, and multiple pending interrupts are paired by interrupt ID. The OpenAI Agents SDK shows the same shape from the tool side: tools declare approval statically via @tool(needs_approval=True) or dynamically via a callable predicate such as needs_approval=requires_review, pending approvals surface as interruptions, and the RunState object can be serialized, paused, and later resumed (OpenAI Agents SDK — Human-in-the-loop guide (Python)). The approval surface is run-wide: interruptions from nested Agent.as_tool() calls or handoffs still surface on the outer run. That is convenient for least-privilege reasoning, because one gate can cover a subtree, but it also means the gate’s scope is coarser than the tool that triggered it.

Level Two: External Callback Tokens and Expiry

Level two hands the pause to an external system that owns a token and an expiry clock. AWS Step Functions’ callback pattern pauses a workflow until an external process returns a task token, selected with the .waitForTaskToken service-integration mode; Standard Workflows support callback waits up to the one-year service quota, and operators can configure a heartbeat timeout (AWS Step Functions — Callback pattern).

{
  "Resource": "arn:aws:states:::sqs:sendMessage.waitForTaskToken",
  "Parameters": {
    "QueueUrl": "https://sqs.example/approvals",
    "MessageBody": {"TaskToken.$": "$$.Task.Token"}
  }
}

Express Workflows support only Request Response integrations, so the callback pattern is not available there — a real constraint when teams scale the same workflow definition across both modes. GitHub’s environment protection sits in the same family: it can require reviewers, prevent self-review, and apply a wait timer, and required-reviewer behavior is “one of the required reviewers” (GitHub Actions — Environments). On Free, Pro, and Team plans, protection rules are limited to public repositories. Argo Workflows offers the bounded variant, suspending a workflow step for a configured duration (Argo Workflows — Suspending). Expiry semantics are the level-two tradeoff: a token that outlives the reviewer’s attention becomes an orphan, and the workflow must decide whether that is a failure or a default.

Level Three: Durable Execution in the Orchestration Layer

Level three moves the pause into the orchestrator itself, so the workflow engine owns pending state, tracks who is waiting, detects the approval, and re-enters the correct step. Temporal describes human review exactly this way and states the recovery system must be at least as reliable as the agent (Temporal’s LangGraph integration).

Temporal also promises automatic failure recovery, human-in-the-loop steps that can wait for days at no cost, and runs that survive crashes. Workflows can act as stateful web services receiving Queries, Signals, and Updates, with handler parameters and return values required to be serializable, data classes preferred (Temporal Workflow message passing). That is the strongest operational position of the three levels: the approval is a message to a running workflow, not a row that some other service must poll. The cost is that the agent’s graph logic now lives inside the orchestrator’s programming model, and the resume path inherits the orchestrator’s versioning and deployment discipline. For teams already running durable workflows, this is usually the correct home. Our own durable agent execution patterns cover the surrounding mechanics, and our postmortem on silent tool-call failures shows why ambiguous effects deserve explicit handling.

What Exactly Must a Durable Approval Record?

A durable approval record must capture enough provenance to reconstruct who approved what, under which policy version, and with which resume semantics, because the record is the audit artifact when an external effect is disputed. The MCP specification’s elicitation guidance is a useful reference for the surrounding controls, even though it addresses a different pattern (Model Context Protocol — Elicitation).

Minimum fields worth persisting: run identifier and thread or workflow ID; the exact tool call and serialized arguments; the requesting agent identity; the approver identity and decision; the policy or rule version evaluated; timestamps for request, decision, and resume; the interrupt or task token; and the outcome of the resumed step. MCP elicitation is a server-to-user information request, not an agent action-approval mechanism, but its recommendations — approval controls, decline and cancel actions, rate limiting, requester visibility, and review before submission — map cleanly onto the record’s control surface. Servers must not use elicitation to request sensitive information, which is a reminder that the gate itself should not become a credential-collection channel. Our LLM guardrails architecture treats these records as first-class inputs to policy evaluation rather than as logs.

Choosing Fail-Open or Fail-Closed

Fail-open or fail-closed is an explicit policy choice, not an emergent property, and Kubernetes names both options directly: failurePolicy: Fail is fail-closed, failurePolicy: Ignore is fail-open (Kubernetes — Admission controllers).

The OpenAI Agents SDK documents a related policy in the JavaScript guide: when a provider reports a request may already have been accepted, the SDK checkpoints that occurrence and fails closed rather than silently replaying (OpenAI Agents SDK — Human-in-the-loop guide (JavaScript)). That is a documented failure-mode policy, not a guarantee of exactly-once external effects. No cited framework guarantees exactly-once external effects after a resume, and Temporal’s cited page does not provide a universal exactly-once side-effect guarantee either. Choose fail-closed when the pending action is destructive or irreversible; choose fail-open when blocking the run is worse than proceeding, and write the choice down next to the gate.

Failure Modes: Lost Events, Replays, Expiry, and Rubber-Stamping

The recurring failure modes are lost resume events, replays against ambiguous effects, expiry that silently converts a pending approval into a default, and rubber-stamping when gate volume exceeds reviewer attention. None of the cited sources publish approval-latency or gate-throughput benchmarks, so the volume question is an operational judgment rather than a sourced number.

Lost events usually trace to a pending record living in a process that restarted. Replays trace to a resume path without idempotency keys or effect deduplication, which is exactly the ambiguity the OpenAI JS SDK checkpoints around. Expiry is a policy question: does a timed-out gate deny, allow, or escalate? Rubber-stamping is a design question: a run-wide approval surface reduces the number of gates but raises the stakes per decision, while tool-local gates increase review load. The MCP specification’s rate-limiting and requester-visibility recommendations are the closest thing to a control set here, and they apply to approval gates by analogy rather than by mandate.

A Decision Framework for Selecting the Right Level

The selection criteria are pause duration, crash boundaries, side-effect ambiguity, audit needs, operational ownership, and failure policy. Level one fits short pauses inside a single process that rarely restarts. Level two fits cross-team handoffs where an external system already owns the token lifecycle. Level three fits long waits, crash survival requirements, and audit trails that must outlive the agent.

Dimension Level one: framework interrupt Level two: callback token Level three: durable execution
State ownership Framework persistence layer External system holding the token Orchestrator owns pending state
Who resumes Caller invoking resume Token holder via callback Signal or Update to the workflow
Expiry semantics Waits indefinitely unless configured Service quota or heartbeat timeout Wait policy defined in workflow
Failure policy Depends on caller handling Explicit in the callback contract Explicit in workflow error handling
When it fits Short pauses, single process Cross-team or cross-system handoff Long waits, crash survival, audit

FAQ

When is a framework interrupt sufficient for an AI approval gate?

A framework interrupt is sufficient when the pause is short, the process is unlikely to restart mid-wait, and the caller owns the resume path. LangGraph’s interrupt() waits indefinitely and requires a persistent checkpointer and thread_id, so the gate is only as durable as that store (LangGraph Interrupts).

When should an agent hand approval to a workflow orchestrator?

Hand approval to an orchestrator when the wait can outlive the process, when the audit trail must survive deploys, or when the recovery system must be at least as reliable as the agent. Temporal’s argument for durable execution is precisely that a checkpoint alone does not detect failure or choose a re-entry point (Temporal’s LangGraph integration).

Should an approval gate fail open or fail closed?

Choose fail-closed for destructive or irreversible actions and fail-open when blocking the run costs more than proceeding. Kubernetes exposes both as failurePolicy: Fail and failurePolicy: Ignore, and the OpenAI JS SDK’s documented behavior is to checkpoint an ambiguous occurrence and fail closed rather than silently replay (Kubernetes — Admission controllers, OpenAI Agents SDK — Human-in-the-loop guide (JavaScript)).

Start by writing down, for each gate, which level owns the pending record and what happens when the reviewer never answers — then let that answer drive the implementation rather than the other way around.

  • NiteAgent — AI agent development, frameworks, and production patterns
  • ToolBrain — tool reviews, LLM comparisons, and AI workflow guides
  • NoCode Insider — AI workflow automation with no-code tools, agents, and APIs

Cross-links automatically generated from CodeIntel Log.