Durable Agent Workflows
Persist decisions and effects as separate evidence, replay only deterministic orchestration, and reconcile ambiguous side effects before a retry can duplicate them.
- Authorship
- InterviewsVector
- Published / updated
- 2026-09-21 / 2026-09-21
- Review status
- Artifact tests passing · primary sources recorded
Original InterviewsVector teaching. Executable artifacts are deterministic illustrative audits with focused tests and recorded primary sources; they do not claim production durability, performance, penetration testing, or security certification.
The decision in one pass
Make the workflow history the durable source of orchestration truth, not the worker process or the model context. Record each typed intent before dispatch, bind its idempotency key to the exact workflow, step, tool, and input digest, and record a typed outcome after the external system acknowledges it. A crash between the effect and outcome record is an ambiguity, not permission to issue a fresh action: query an authoritative receipt or reconcile external state first, then reuse the same key if policy permits a retry. Replay deterministic decisions from history; keep network calls, clocks, randomness, and model sampling in recorded activities. Persist human approvals as expiring, payload-bound events. Version workflow code against existing histories, cap attempts and time, and end in an explicit completed, failed, cancelled, or intervention state. These controls improve recoverability; they do not manufacture exactly-once delivery across independent systems.
Why this matters
An agent can pause for minutes, wait for a person for days, and resume on a different worker after its context has disappeared. During that time a payment, message, deployment, or ticket update may have succeeded even though the caller never received the acknowledgement. If recovery means rerunning the prompt and hoping, the system can repeat side effects, skip approval, read changed state as if it were old evidence, or follow a different plan under new model output.
You will be able to
- Separate replayable orchestration decisions from non-deterministic model and tool activities.
- Bind every state-changing attempt to a stable idempotency key and exact intent digest.
- Classify failures as known failure, known success, or ambiguous outcome before choosing retry or reconciliation.
- Persist payload-bound human approvals, cancellation, deadlines, and terminal states.
- Version long-lived workflows and operate them with history, lag, retry, and intervention evidence.
Your Vector Loop for this lab
- 01
Model
Map workflow identity, immutable plan, history, external effects, approvals, deadlines, and terminal outcomes as different state owners.
- 02
Derive
Derive transition guards, idempotency scope, ambiguity states, retry budgets, reconciliation evidence, and version compatibility before choosing infrastructure.
- 03
Build
Build a deterministic recovery audit that reconstructs frozen plan, event, and receipt records and never executes a side effect.
- 04
Stress
Crash before dispatch, after dispatch, and after external success; repeat deliveries, alter inputs under one key, expire approvals, and bypass constructors.
- 05
Operate
Track open-run age, schedule-to-start lag, activity attempts, ambiguous effects, reconciliation latency, history growth, interventions, and terminal outcomes.
- 06
Defend
Defend the exact guarantee at each boundary and reject any claim of global exactly-once execution that the external systems do not jointly provide.
Durability is recorded progress, not a very patient process
A worker is disposable. A durable workflow can be reconstructed from persisted history after a crash, deployment, eviction, or long human pause. The record needs a stable workflow identity, a definition version, typed inputs, ordered commands, activity outcomes, signals, approvals, timers, cancellation, and terminal status. Model context is derived working material; it is neither the authoritative history nor a safe place for credentials.
| Layer | Durable evidence | Recovery rule |
|---|---|---|
| orchestrator | ordered decisions and awaited events | replay deterministically from history |
| model activity | request, model/config revision, typed result | reuse the recorded result; resample only as a new declared attempt |
| tool activity | intent digest, key, attempt, typed outcome | reconcile ambiguous outcomes before retry |
| human gate | actor, action digest, decision, expiry | resume only the exact approved action |
| external system | authoritative receipt or queried state | treat it as separate truth with its own consistency model |
Name the crash window around every side effect
For a remote write, the caller can atomically control neither the remote mutation nor its own outcome record. If the worker crashes after the remote service commits but before history records success, recovery sees a scheduled action with no terminal result. That is an ambiguous outcome. Blind retry can duplicate the effect; assuming success can skip work that never happened.
recovery(scheduled, no outcome) = reconcile(key, intent) → {succeeded, safe-to-retry, intervention}
A receipt lookup or state query must bind the same tenant, workflow, step, idempotency key, and input digest. Unknown is a real result; it must not be converted to success by optimism.
- 01Persist intentRecord the exact operation, authorization context, input digest, idempotency key, deadline, and attempt before dispatch.
- 02Execute through a narrow adapterPass the same caller-chosen key on every retry and require the service to reject key reuse with different parameters.
- 03Persist the typed outcomeRecord success, retryable failure, permanent failure, or cancellation with an external receipt when available.
- 04Reconcile ambiguityAfter a missing acknowledgement, query authoritative state using the original identity before issuing another mutation.
Idempotency preserves one intent; compensation creates a new one
An idempotency key is not a random retry label. Scope it to the authenticated caller and stable business intent, store the first accepted parameters, and return a semantically equivalent outcome for a duplicate when the service contract supports that behavior. The same key with a different amount, recipient, tenant, or payload must fail as an intent mismatch rather than being silently deduplicated.
| Situation | Correct next move | Unsafe shortcut |
|---|---|---|
| known transient failure before effect | retry same intent within budget | mint a new key per attempt |
| unknown outcome | reconcile receipt or state | blindly retry a non-idempotent write |
| known success | record or reconstruct success | execute again to be sure |
| later business reversal | issue an authorized compensating action | delete prior history or call it rollback |
| changed requested outcome | create a new intent and approval | reuse the old key with new parameters |
Audit a restart without executing a tool
1def audit_recovery(contract: WorkflowContract, snapshot: RecoverySnapshot) -> RecoveryReport:2 """Replay declarations and reconcile ambiguous effects without executing a tool."""3 contract = validate_record(contract, WorkflowContract)4 snapshot = validate_record(snapshot, RecoverySnapshot)5 if snapshot.scope != contract.scope or snapshot.contract_content_id != contract.content_id:6 raise ValueError("snapshot belongs to another workflow contract")7 states = _validate_history(snapshot.plan, snapshot.events, contract)Expected output
example=illustrative_only
status=RESUMABLE
completed=reserve-stock,charge-card
next=send-receipt
decision=reconcile-before-retry
claim=LOCAL_RECOVERY_AUDIT_NOT_EXACTLY_ONCE_GUARANTEEVerify: python3 -m unittest discover courses/ai-engineering/reference-impl/durable_agent_workflows
The fixture records a completed inventory reservation, an approved payment that was scheduled, and an authoritative payment receipt observed after the schedule event but before a local success event. Recovery counts the payment once and advances to the receipt step. The implementation reconstructs every frozen record, copies supplied collections, verifies digests and scope, rejects noncontiguous plan/history positions, enforces a sequential transition grammar, caps attempts, and rejects boolean integers, non-finite timeout values, string subclasses, and constructor bypass.
This result is illustrative only. The script does not run an activity, persist an event, authenticate a receipt, lock a resource, emulate a workflow service, or prove exactly-once delivery. Its receipt is declared authoritative by fixture input; a real adapter must establish that fact from authenticated external evidence.
Resume under current authority without rewriting history
A human pause is a persisted workflow state, not a blocked process. Store the proposed action and digest shown to the reviewer, authenticated reviewer, decision, policy version, time, and expiry. On resume, re-check tenant and resource authorization, approval freshness, and mutable preconditions. If the payload changes, request a new approval. If external state changed, branch through an explicit replan or intervention event rather than pretending the old plan still applies.
- Cancellation is cooperative for work already dispatched. Record the request and the observed cancellation outcome; reconcile any effect that may already have committed.
- Use logical workflow timers rather than worker sleep. Persist deadlines and define what late signals mean.
- Version workflow definitions with replay compatibility. A code deployment must still interpret open histories or migrate them through an explicit marker.
- Keep secrets and bulky model context out of history. Store bounded references with retention, encryption, and deletion policy appropriate to the data.
Operate histories, not just happy-path latency
| Signal | Question it answers | Likely response |
|---|---|---|
| open-run age by state | where are workflows stranded? | inspect waits, missing workers, or lost signals |
| attempts and retry delay | is a dependency transient or amplifying load? | cap, back off, shed, or intervene |
| ambiguous-effect count | which writes lack a known outcome? | reconcile before enabling retries |
| history size and replay time | will recovery remain bounded? | compact or continue with an explicit versioned boundary |
| approval age and rejection | are humans seeing current intent? | expire, reroute, or redesign the gate |
Test recovery by killing workers at every persistence boundary, not merely by throwing an exception inside an activity. Re-deliver events, reorder allowed signals, deploy old and new workflow code against captured histories, and inject timeouts after an external service has committed. A release gate should prove deterministic replay for representative histories and show which effect classes have genuine reconciliation support.
Operate at three altitudes
Production lens
- — Alert on ambiguous effects, repeated attempts, old open workflows, expired approvals, history growth, replay failures, and operator interventions by workflow and activity version.
- — Give every state-changing adapter a documented key scope, parameter-mismatch behavior, receipt/reconciliation path, retry budget, and compensation owner.
- — Retain representative histories and run replay compatibility before deploying workflow-code or schema changes; canary long-lived versions separately.
Staff lens
- — Own durability as an end-to-end guarantee map across history storage, workers, model calls, tool adapters, external systems, human authority, and incident recovery.
- — Require each product flow to name its irreducible ambiguity and intervention path instead of allowing infrastructure marketing to imply global exactly-once semantics.
Interview defense
A payment activity timed out, the worker restarted, and history contains only the scheduled event. What should the workflow do?
It has an ambiguous outcome. I would not mint a new key or blindly charge again. I would load the original workflow/step identity, input digest, authorization and approval evidence, then reconcile against an authenticated payment receipt or query using the same idempotency key. A matching committed payment becomes recorded success; an authoritative not-found may allow a retry with the same key and parameters within budget; unresolved state goes to intervention. The workflow decision is persisted, replayable, and explicit about what the payment provider—not the orchestrator—actually guarantees.
Expect the interviewer to press on
- — Why can replayed workflow code be deterministic while activities are not?
- — What should happen when the same idempotency key arrives with different parameters?
- — How do you deploy a workflow-code change while old histories remain open?
Misconceptions to remove
“A durable workflow engine makes every activity execute exactly once.”
An activity may execute or partially execute more than once. History can make completion observation durable, while external idempotency and reconciliation control duplicate effects.
“A unique key generated for every retry provides idempotency.”
Retries of one intent must reuse the same scoped key and parameters. A fresh key tells the service that the request may be a new intent.
“Compensation restores the world to its previous state.”
Compensation is a new forward action whose business effect may be incomplete and whose own execution can fail or require approval.
Check your model
1. Why is a scheduled activity with no result not simply a failure?
The external effect may have committed before the worker lost the acknowledgement or before history persisted success, so the outcome is unknown until reconciled.
2. What must a durable human approval bind?
Authenticated actor, exact action and payload digest, tenant/resource scope, policy version, decision time, and expiry; changed intent requires new approval.
3. What does deterministic workflow replay exclude?
Unrecorded clock reads, randomness, network calls, model sampling, and mutable external state; those observations belong in recorded activities or signals.
Prove the mechanism
Extend the harness with a cancellation-request event and an authenticated not-found reconciliation receipt. Define the only histories that permit retry, and prove that cancellation cannot convert an ambiguous write into a known non-effect.
Add a production constraint
Design a replay-safe migration from a three-step workflow to a conditional five-step workflow while old runs are open. Include version markers, schema compatibility, approval rebinding, activity rollback, history fixtures, and operator recovery.
Artifact: Durable workflow recovery harness
courses/ai-engineering/reference-impl/durable_agent_workflows/durable_workflow_audit.py
Download reference implementationPrimary references and next links
References
- 1. Temporal Workflow Execution overview
Temporal. Official documentation for event history, replay, recovery, workflow states, and recorded transitions.
- 2. Temporal Activity Definition
Temporal. Official documentation explaining activity retries, partial completion, and the need for idempotent external effects.
- 3. Making retries safe with idempotent APIs
Amazon Builders' Library. First-party engineering guidance on caller-provided request identifiers, semantic-equivalent retry responses, and changed intent.
Continue through the graph
- A Tool-Using Agent Is a Bounded State Machine →
Place recovery inside explicit state, budget, approval, and terminal-state guards.
- State, Memory, and Context Are Different Things →
Keep authoritative workflow state distinct from retrieved memory and model-visible context.
Glossary: durable execution · event history · deterministic replay · idempotency key · ambiguous outcome · reconciliation · compensation · workflow versioning