Approvals, compensating actions & recovery
Last reviewed: 2026-07-16. See the freshness policy.
Learning objectives
After this chapter you will be able to:
- bind human approval to the exact action and current state;
- make tool execution idempotent across retries and crashes;
- distinguish retry, compensation, reconciliation, and escalation;
- design an agent run that can pause, resume, cancel, and recover safely.
The failure that matters
An agent submits a refund. The payment API succeeds, but the network response is lost. The orchestrator retries, and a second refund is issued. A human “approved a refund,” yet the system cannot prove which amount, account, or state they reviewed.
Reliable agentic systems assume this ambiguous boundary will occur: the external effect may commit while the caller believes it failed. Recovery must be designed before autonomy.
Durable action lifecycle
stateDiagram-v2
accTitle: Durable lifecycle for a consequential agent action
accDescr: A proposed action is validated and may wait for approval before execution. Execution can succeed, fail safely for retry, require reconciliation, or enter compensation and manual review.
[*] --> Proposed
Proposed --> Rejected: policy denies
Proposed --> AwaitingApproval: approval required
Proposed --> Authorized: auto-authorized
AwaitingApproval --> Authorized: matching approval
AwaitingApproval --> Expired: state or time changed
Authorized --> Executing
Executing --> Succeeded: effect confirmed
Executing --> Retryable: no effect confirmed
Executing --> Reconciling: outcome ambiguous
Retryable --> Executing: retry with same key
Reconciling --> Succeeded: effect found
Reconciling --> Executing: no effect found
Succeeded --> Compensating: later step fails
Compensating --> Compensated
Compensating --> ManualReview: compensation fails
Rejected --> [*]
Expired --> [*]
Compensated --> [*]
ManualReview --> [*]
Succeeded --> [*]
Persist each transition transactionally. A process restart should load the current state, not infer it from a model conversation.
Risk-tier the tools
| Tier | Example | Required control |
|---|---|---|
| 0: read | Search policy | Tenant scope, rate limit, audit |
| 1: reversible local write | Add draft note | Idempotency, validation, undo |
| 2: external or customer-visible | Send message | Preview, approval or policy gate, receipt |
| 3: financial/legal/security | Refund, entitlement, access grant | Strong authentication, separation of duties, exact approval binding, reconciliation |
| 4: irreversible or catastrophic | Destructive infrastructure or safety action | Keep outside general-purpose agent authority |
Risk depends on parameters and context. A $5 credit and a $500,000 transfer are not the same tool tier. The policy input must include amount, target, environment, data class, reversibility, and accumulated effects in the run.
Approval is a cryptographic-quality control decision
An approval record should bind:
{
"approval_id": "apr_01J...",
"run_id": "run_01J...",
"action_hash": "sha256:...",
"action_type": "issue_refund",
"resource_version": "order:18431:v17",
"principal": "approver-9",
"scope": {"max_amount": 85, "currency": "USD"},
"reason": "duplicate shipment",
"issued_at": "2026-07-16T17:02:00Z",
"expires_at": "2026-07-16T17:12:00Z",
"authentication_context": "phishing-resistant-mfa"
}
Before execution, recompute the canonical action hash and re-read the resource version. Any change in target, amount, recipient, state, authority, or expiry invalidates the approval. “Approve everything in this chat” is not an acceptable capability grant.
Design the review screen to show the proposed effect, source evidence, uncertainty, policy result, alternatives, and consequence of approval. Prevent model-generated text from obscuring fields or changing the review UI.
Idempotent execution
Generate the idempotency key from the logical action, not from an attempt:
idempotency_key = H(tenant, action_type, target_id, intent_id)
The tool adapter stores an action record before calling the external system. Every retry reuses the same key. The target service should atomically return the original result for duplicate keys. If it cannot, add a broker that records intent and reconciles target state.
Do not mark an action successful merely because the call returned HTTP 200. Validate the business receipt: resource ID, final state, amount, version, and correlation key.
Retry, reconcile, compensate, or escalate
flowchart TB
accTitle: Recovery decision after a tool failure
accDescr: The orchestrator classifies whether an external effect definitely did not occur, definitely occurred, or is ambiguous, then retries, accepts, reconciles, compensates, or escalates accordingly.
F["Tool call failed or timed out"] --> Q{"Effect status known?"}
Q -->|"Definitely absent"| I{"Request idempotent?"}
I -->|"Yes"| R["Retry with same key and budget"]
I -->|"No"| H["Manual resolution"]
Q -->|"Definitely committed"| V["Validate receipt and continue"]
Q -->|"Ambiguous"| C["Query by idempotency / correlation key"]
C -->|"Found"| V
C -->|"Absent"| R
C -->|"Still unknown"| H
V --> L{"Later invariant failed?"}
L -->|"No"| S["Complete"]
L -->|"Yes"| P["Compensate or contain"]
- Retry repeats the same intended effect after a transient failure.
- Reconciliation discovers what actually happened.
- Compensation creates a new business action that semantically offsets a committed action.
- Rollback is reserved for a true atomic transaction; most external effects cannot be rolled back.
- Escalation is correct when state is ambiguous or compensation would create more risk.
Sagas and compensating actions
A multi-system action is usually a saga: local transactions with forward recovery or compensation. For each step, define the idempotency key, receipt, retry policy, compensation, compensation preconditions, and owner.
| Forward step | Receipt | Compensation | Limitation |
|---|---|---|---|
| Reserve inventory | reservation ID | Release reservation | May expire independently |
| Capture payment | payment ID | Refund payment | Fees and timing may not reverse |
| Create shipment | shipment ID | Cancel shipment | Impossible after carrier pickup |
| Notify customer | message ID | Correction message | Original message remains visible |
Compensation is not erasure. Preserve both actions in the audit trail and explain residual effects. Where compensation is impossible, favor prevention, staged commitment, or human authority.
Cancellation and deadlines
Cancellation is a durable state transition. Stop starting new work, propagate cancellation to cancellable calls, and allow already-committed actions to reach reconciliation. Never abandon an ambiguous effect.
Use separate timeouts for queue wait, tool execution, approval, and whole run. An approval that arrives after the resource or policy changed must expire, even if the overall run remains open.
Recovery runbook
For any run in Reconciling or ManualReview:
- freeze new actions for the affected intent;
- load the durable action ledger and correlation IDs;
- query the target system through an authoritative read path;
- compare the observed state to business invariants;
- accept, retry, compensate, or contain using a documented decision;
- notify affected operators or users without overstating certainty;
- attach receipts and the decision to the run;
- convert the failure into an automated recovery test.
Operators need a safe console to search by run, resource, and idempotency key, see pending effects, and perform allowlisted recovery actions. Editing database rows is not a recovery interface.
Concurrency and stale state
Sagas lack transaction isolation across services. Use optimistic version checks, semantic locks for scarce resources, and precondition headers where available. Revalidate policy and resource state immediately before commit. If two agents act on the same case, one should observe a version conflict and replan or stop.
Observability and evidence
Record run ID, action ID, idempotency key, attempt, approval ID, policy version, tool schema version, target receipt, resource version, timestamps, and transition reason. Do not log secrets or full sensitive payloads. Metrics should distinguish tool errors, policy denials, retries, duplicate suppression, reconciliation duration, compensation success, expired approvals, and manual backlog.
Evaluation
Test crash points before and after every durable write and external call. Inject lost responses, duplicate delivery, delayed approval, stale resource versions, partial downstream outages, failed compensation, cancellation, and operator retry. The acceptance criterion is invariant preservation and recoverability—not merely a successful final response.
Northstar decision
Northstar’s support agent may propose a refund but cannot execute it. A workflow validates policy and creates an action hash. A strongly authenticated supervisor approves the exact order, currency, and maximum amount. The payment adapter uses a stable idempotency key, verifies the receipt, and enters reconciliation after an ambiguous timeout. Refunds above the delegated threshold require a second approver.
Architecture artifact
Produce an action-risk matrix and recovery runbook with tool tiers, approval policy, canonical action fields, state machine, idempotency strategy, receipts, retry classifications, compensation limits, cancellation behavior, operator procedures, SLOs, and fault tests.
Lab
Model a three-step Northstar replacement order. Simulate a crash after payment capture but before acknowledgement, a stale approval, a duplicate event, and a failed shipment cancellation. Demonstrate that the final state is explainable and no effect is duplicated.
Check yourself
- Why must an approval bind a resource version and action hash?
- What is the safe response to an ambiguous non-idempotent action?
- Why is compensation not rollback?
- Which state must survive a model or orchestrator restart?