Interrupt and resume

Lesson 4 of 5 in Human-in-the-Loop: Designing the Human Back In.

In a demo, the approval gate is easy: the human is sitting at the terminal, the agent loop blocks on stdin, thirty seconds pass, work continues. In production, the approver is in a meeting, the approval arrives four hours later over Slack, and the process that was running the loop has been redeployed twice since. An approve-before gate is a promise to pause a run mid-loop and resume it later, intact — and keeping that promise is an engineering problem, not a prompt.

Pausing means the run’s full state gets written somewhere durable before the process lets go: the conversation so far, the pending tool call (the exact one shown to the approver), and an approval token that ties the eventual decision back to this run. Resuming means a fresh process can pick that state up hours later and continue as if nothing happened — this is durable execution, and it is the same plumbing that survives crashes and timeouts. The agentops reliability module builds it out; here you need the shape.

The life of a gated tool call

  1. Agent proposes tool call
  2. Action gated?
  3. Execute tool
  4. Persist run state + pending call

    Conversation, the literal pending call, and an approval token — written durably before the request goes out, so a process restart loses nothing.

  5. Deliver approval request

    Slack, email, a review queue — with the exact parameters rendered by the runtime.

  6. Decision before TTL?
  7. Execute exactly the approved call
  8. Inject denial as tool result

    The denial (ideally with the approver’s reason) returns to the model as information, so the loop can re-plan instead of dying.

  9. Expire: cancel or escalate

    Silence is not consent. An unanswered request cancels the action or escalates the task — it never auto-approves.

  10. Resume the loop

What if the process crashes while waiting for the approval?

Nothing is lost, if you persisted before notifying. That ordering is the whole trick: state first, request second. A fresh worker rehydrates the run from the store when the decision arrives. If you notify first and crash before persisting, the approver’s click references a run that no longer exists.

What should a denial do to the run?

Return to the model as a tool result — { status: 'denied', reason: 'wrong customer segment' } — not as an exception that kills the run. A denial is high-value information: the agent can re-plan, propose an alternative, or escalate. Runs that crash on “no” teach teams to stop saying no.

Why can’t an unanswered approval just auto-approve after a while?

Because that converts every reviewer vacation into an autonomy upgrade. Silence cancels or escalates — never consents. If an action is safe enough to fire without an answer, it didn’t need an approve-before gate; move it to notify-after honestly.

What stops a resumed run from re-firing a call that already executed?

Idempotency keys: the runtime records each executed call’s ID durably, and resume-time replay checks the record before firing. Approve-then-crash-then-resume must not become approve-then-execute-twice — one wire transfer approved is one wire transfer sent.

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.