GenAI Consulting

Designing Stateful Retry Boundaries in Production GenAI Workflows

GenAI Consulting25 min read
Designing Stateful Retry Boundaries in Production GenAI Workflows

The failure usually doesn’t look dramatic at first. A customer asks an operations copilot to “rebook the delayed shipment, notify the warehouse, and update the customer ETA.” The workflow plans correctly, retrieves the right order, drafts a customer message, and calls an internal logistics tool. Then the LLM times out while generating the final explanation back to the user.

Your orchestrator does the obvious thing: retry.

On the second attempt, the model again decides it needs the logistics tool. The tool call goes through. The warehouse receives a second notification. The shipment is rebooked twice. A human approver, who only sees the latest execution trace, approves what looks like a single action. Meanwhile the user’s browser reconnects to the streaming endpoint and receives a partial duplicate response that doesn’t match the committed workflow state. Later, finance asks why some orders show duplicate carrier fees and why retries spike only for requests with long context windows.

This is the kind of production GenAI failure that teams discover after launch, not in the demo environment. The root problem is not “the model failed.” The root problem is that the system has no explicit retry boundaries tied to durable state. In a toy app, a retry is just “run it again.” In a production GenAI workflow, retry semantics are architecture. They decide whether replay is safe, whether side effects are duplicated, whether humans approve the right thing, and whether observability tells the truth.

This article is about designing stateful retry boundaries for production GenAI systems. Not generic distributed-systems retries in the abstract, and not just “make your tools idempotent.” Specifically, we’ll look at workflows that combine LLM calls, retrieval, validation steps, human approvals, and external tools that mutate state. We’ll cover where to resume versus restart, how to checkpoint, how to deduplicate tool usage and outputs, how retries interact with streaming and human-in-the-loop gates, and which telemetry signals reveal hidden replay bugs before customers do.

The key idea is simple:

Retries should target the smallest deterministic or recoverable unit of work, and every boundary between units should have durable state, replay rules, and deduplication semantics.

Once you take that seriously, your workflow design changes.

The pattern behind most GenAI retry bugs

Across production systems, the same pattern shows up repeatedly:

  1. The workflow is logically multi-step, but operationally treated as one request.
  2. Some steps are pure or reproducible; others have side effects or time-sensitive dependencies.
  3. A failure happens after one or more irreversible actions have already occurred.
  4. The orchestrator retries from too far upstream.
  5. The model re-plans, re-calls tools, or re-emits outputs in a way that is no longer equivalent to the original attempt.

In standard backend systems, we’ve learned to separate compute from commit, isolate transactions, and require idempotency on mutation paths. GenAI systems often regress on these lessons because the orchestration layer feels “soft”: prompts, context assembly, tool selection, validation, streaming, agent loops. Teams focus on model quality and forget that the workflow is a distributed system with non-deterministic decision-making at the center.

The difficult part is that retries are not just about network failures. In GenAI systems, you may retry because of:

  • provider timeouts
  • rate limits
  • context overflow
  • malformed structured output
  • validator rejection
  • retrieval backend transient errors
  • human approval timeouts
  • client disconnects during streaming
  • downstream tool uncertainty, where the tool may have succeeded but the acknowledgement was lost

Those failure modes are not equivalent. They should not all retry from the same place.

Why the naive approach fails

The naive design usually has one of two shapes.

Naive design A: one monolithic agent run

The system treats a user request as a single agent session. The full message history, retrieved context, intermediate tool outputs, and partial reasoning are held in memory or an ephemeral trace object. If anything fails, the entire run is repeated.

That fails because:

  • LLM decisions are not stable across retries. Even with temperature 0, many systems see differences due to prompt changes, retrieval timing, tool availability, provider-side updates, or hidden non-determinism.
  • Tool effects are not rolled back. If the retry repeats the plan, external actions can execute again.
  • Retrieval is time-variant. A second retrieval may fetch a different top-k set because indexes updated or scoring changed.
  • Validators can change downstream behavior. One retry may fail schema validation; the next may choose a different wording or tool sequence.
  • Streaming confuses logical completion with transport completion. If the client disconnects after tokens were generated but before the workflow committed its final state, you may have user-visible output with no durable record or durable state with no confirmed delivery.

Naive design B: retry every failed step independently

Teams sometimes swing to the other extreme: wrap every API call in exponential backoff and call it a day.

That also fails because:

  • Not every step is safe to retry in place. If a call is side-effecting, you need idempotency and a durable operation record.
  • Step-local retries can violate workflow-level intent. Re-running a validator, re-ranking retrieval results, or asking the model to repair output may change semantics relative to previous steps.
  • Dependencies between steps are ignored. If a retry changes intermediate state without invalidating dependent outputs, later stages operate on stale assumptions.
  • It hides uncertainty. “Tool timed out” is different from “tool may have succeeded but acknowledgement was lost.” Treating both as generic retriable exceptions causes duplicates.

The result is a class of bugs that are subtle in test and expensive in production:

  • duplicate external actions
  • diverging user-visible output and durable state
  • replaying human approvals against different artifacts than were reviewed
  • inflated token spend from full reruns
  • hidden quality regressions because retries silently alter retrieval or tool plans

The better approach: design retry boundaries around workflow state

A production-safe GenAI workflow should be modeled as a sequence of state transitions over durable workflow state, with explicit replay boundaries between transitions.

Think less like “an agent run” and more like “a state machine with non-deterministic compute stages.”

A practical workflow state model includes:

  • workflow_id: stable ID for the user request or business process
  • attempt_id: identifier for a run attempt at a particular stage
  • step_id / stage_id: current unit of work
  • checkpoint state: durable artifacts needed to resume safely
  • input snapshot: exact inputs used for a step, or a reference to immutable versions
  • output artifact: structured result of the step
  • dedupe keys / idempotency keys: for side-effecting operations
  • step status: pending, running, succeeded, failed_retriable, failed_terminal, awaiting_human, committed
  • causal metadata: retrieval version, prompt version, tool version, model version

The essential design question for each stage is:

If this stage fails after partially executing, should the workflow restart from an earlier checkpoint, resume this stage, or continue from the last committed boundary?

That answer depends on the type of stage.

A useful taxonomy of workflow stages

In practice, classifying stages into four buckets makes retry design much easier.

1. Pure deterministic stages

Examples:

  • rule-based normalization
  • schema transformation
  • deterministic ranking over a fixed input set
  • deterministic validator execution

For these, retrying in place is usually safe. You may not even need heavy checkpointing beyond inputs and outputs. If inputs are immutable and the code version is stable, you can recompute.

2. Pure but non-deterministic or time-sensitive stages

Examples:

  • LLM generation
  • retrieval against a changing index
  • web search
  • classifier calls to a hosted model

These stages have no direct side effects but can produce different outputs on replay. For them, you need to decide whether replay is acceptable or whether you must preserve the original output as the authoritative artifact once downstream steps depend on it.

This is where many bugs begin. Teams assume “no side effects” means “safe to rerun.” It may be computationally safe, but semantically unsafe if later actions were based on the original output.

3. Side-effecting stages with idempotent endpoints

Examples:

  • sending a message via an API with idempotency support
  • creating a ticket with a caller-supplied dedupe key
  • updating an order status using compare-and-swap semantics

These are manageable if you actually use the idempotency mechanism and persist the key before the call.

4. Side-effecting stages without trustworthy idempotency

Examples:

  • legacy internal tools
  • robotic browser automation
  • email or chat integrations lacking dedupe support
  • vendor APIs that claim idempotency but implement it poorly

These are the most dangerous. They require a stronger boundary, often involving a local operation log, reconciliation, compensating workflows, or a mandatory human checkpoint.

Where to resume versus restart

A simple production rule works well:

  • Resume from the last committed checkpoint when all prior stages are durable and their outputs are still valid.
  • Restart a stage only if it is self-contained and replay-safe under its recorded inputs.
  • Never restart upstream of a committed side effect unless the side effect is idempotent or you are intentionally running a compensating workflow.

Let’s make this concrete with a common workflow:

  1. classify intent
  2. retrieve account/order data
  3. retrieve policy docs from vector store
  4. draft action plan with LLM
  5. validate plan against policy
  6. request human approval if high-risk
  7. execute tool actions
  8. draft final user message
  9. stream response to client
  10. mark workflow complete

Recommended boundaries:

Boundary A: after retrieval snapshots

Once retrieval results are assembled, store references to the exact artifacts used:

  • document IDs and versions
  • retrieval scores
  • account snapshot version
  • query rewrite used
  • top-k result list

If the workflow later fails in execution or response generation, you typically want to resume from this retrieval checkpoint, not re-retrieve, unless freshness requirements explicitly demand it.

Why? Because if the plan or approval used one set of documents and the retry uses another, you are no longer continuing the same decision process.

Boundary B: after plan generation and validation

Persist the structured plan artifact, including:

  • model and prompt version
  • tool arguments proposed
  • validator outcomes
  • policy references used
  • risk classification

If validation fails due to format issues, you can retry the generation/repair loop within this stage. Once a plan is validated and perhaps shown for approval, it becomes a durable artifact. Do not silently regenerate it later unless you explicitly invalidate and re-approve.

Boundary C: before each side-effecting tool call

Create a durable operation record before the call:

  • operation_id
  • business action type
  • idempotency key
  • exact tool arguments
  • preconditions
  • status: prepared

Then execute the tool call using the same idempotency key. After receiving confirmation, mark the operation committed and persist the returned external reference.

If the tool times out and you don’t know whether it succeeded, the operation record should move to unknown_outcome, triggering reconciliation, not blind retry.

Boundary D: after side effects, before user-visible completion

Separate business action completion from response generation and delivery. If the tool action is committed but the final LLM response fails, do not re-run the tool path. Resume at response generation from the committed action results.

Boundary E: streaming delivery

Treat streaming as a delivery channel, not the source of truth. The canonical result should be a durable response artifact or final workflow state. If the stream drops, the client should be able to reconnect and fetch the committed result or the current resumable stage.

The architecture pattern that works in production

The most reliable pattern is a workflow engine with durable state + artifact store + operation log + idempotent tool adapter layer.

At a high level:

  1. Workflow orchestrator manages the state machine.
  2. Checkpoint store persists state transitions and stage artifacts.
  3. Artifact store holds large immutable inputs/outputs: retrieval snapshots, plan JSON, validation reports, streamed response transcript.
  4. Operation log records every side-effecting intent before execution.
  5. Tool adapter layer translates workflow operations into external API calls with idempotency, retries, and reconciliation.
  6. Event bus / audit trail emits lifecycle events for observability.
  7. Human review service binds approvals to immutable artifact versions.

A representative state transition sequence might look like this:

text
REQUESTED -> CONTEXT_SNAPSHOTTED -> PLAN_GENERATED -> PLAN_VALIDATED -> AWAITING_APPROVAL -> APPROVED(plan_version=12) -> OPERATION_PREPARED(op_id=abc) -> OPERATION_COMMITTED(external_ref=carrier_789) -> RESPONSE_GENERATED(response_version=4) -> RESPONSE_DELIVERED(channel=session_456) -> COMPLETED

The important point is that retries happen inside stages where safe, and between stages by resuming from durable transitions. You do not reconstruct the world from scratch unless the workflow design says to.

Checkpointing strategy: what to persist

Teams often either checkpoint too little or everything indiscriminately.

Checkpoint too little, and you can’t replay safely. Checkpoint too much, and your costs, privacy surface, and storage complexity explode.

In practice, persist these categories.

1. Semantic inputs

These are the inputs whose meaning must remain stable across retries:

  • user request text and metadata
  • normalized business entity IDs
  • prompt template version
  • model selection policy version
  • retrieval query or query rewrite
  • retrieved document IDs and versions
  • tool argument candidates

2. Decision artifacts

Artifacts produced by stages that influence downstream behavior:

  • structured plans
  • extracted slots/entities
  • validation reports
  • risk labels
  • approval payloads
  • selected tool sequence

3. Side-effect records

For every mutating operation:

  • operation ID
  • idempotency key
  • exact request payload
  • endpoint/version
  • timestamps
  • returned external identifier
  • final known status
  • reconciliation notes

4. Delivery artifacts

For user-facing output:

  • final response text or structured response object
  • citations used
  • stream transcript with sequence numbers if needed
  • channel delivery status

What not to persist by default:

  • every token from hidden chain-of-thought
  • every transient scratchpad step
  • massive unfiltered context blobs if references suffice

Persist enough to replay safely, audit decisions, and resume cheaply.

Deduplication patterns that actually prevent duplicate actions

“Use idempotency keys” is true but incomplete. The failure is often not in the downstream API; it is in the caller not having a stable definition of sameness.

You need deduplication at multiple layers.

Layer 1: Workflow-level dedupe

If clients retry the same user action, the system should detect whether this is:

  • the same logical request being resumed
  • a user-initiated new request
  • a network retry from the frontend

Common pattern:

  • client sends request_id
  • backend maps request_id to workflow_id
  • repeated submissions with same request_id return the existing workflow state

Layer 2: Stage-level dedupe

For expensive pure stages like retrieval or plan generation, cache by stable input hash when appropriate:

  • prompt version
  • model name
  • semantic input snapshot ID
  • retrieval corpus version

Be careful: caching LLM outputs across requests is safe only if business semantics allow it. Often this is useful internally within one workflow, not globally.

Layer 3: Operation-level dedupe

Every side-effecting action should have a dedupe key derived from business intent, not from the raw HTTP request.

Bad dedupe key:

  • random UUID generated at send time

Better dedupe key:

  • workflow_id + operation_type + canonicalized_business_target + action_semantics_version

For example:

  • wf_123|rebook_shipment|order_456|v2
  • wf_123|notify_warehouse|shipment_789|template_3

This ensures that replay of the same intent maps to the same operation, while intentionally changed plans produce different keys.

Layer 4: Output dedupe for streaming and notifications

If the final response or notification can be emitted multiple times because of reconnects or retry races, tag chunks or messages with monotonic sequence IDs and a response_version.

Clients should treat delivery as at-least-once unless your transport truly guarantees otherwise.

Streaming complicates retries more than most teams expect

Streaming gives the illusion of progress, but it introduces a second state machine: token delivery to the client.

There are three distinct events that teams often conflate:

  1. the model generated tokens
  2. the workflow accepted and committed a response artifact
  3. the client received the full stream

These can diverge.

A safe pattern is:

  • Generate response into a buffer or durable append log.
  • Stream chunks with sequence numbers.
  • Commit the final response artifact when generation completes and validators pass.
  • Mark delivery status separately from generation status.

If the client disconnects mid-stream:

  • do not restart upstream business actions
  • do not necessarily regenerate the response
  • allow the client to resume from the last sequence number or fetch the committed final response

For long-running workflows, consider emitting structured progress events instead of only raw token streams:

  • retrieval_complete
  • approval_required
  • action_executed
  • final_response_ready

This reduces pressure to keep a fragile token stream alive as the sole communication mechanism.

Human approvals must bind to immutable artifacts

A common hidden replay bug: the human approves “the plan,” but the plan later changes on retry.

If approval is required, the approval should reference:

  • artifact ID
  • artifact version/hash
  • risk metadata
  • time of approval
  • approver identity
  • expiry policy

If anything material changes after approval, the approval must be invalidated. Material change includes:

  • different retrieved evidence set
  • different tool arguments
  • different target entity
  • different downstream side effect

This is especially important when the approval UI renders a natural-language summary generated by an LLM. Persist the machine-readable plan and bind approval to that immutable structure, not just the prose.

A useful operational rule:

Approval gates should sit after planning/validation and before side effects, with artifact immutability enforced across retries.

Validators are part of retry design, not just quality control

Validators often sit between generation and execution, but their retry semantics matter.

Consider common validator types:

  • schema validators
  • policy/rules validators
  • grounding/citation validators
  • execution precondition validators
  • model-based critics

For each, define whether failure means:

  • repair and retry within the same stage
  • branch to fallback model/prompt
  • escalate to human
  • terminate the workflow

The danger is allowing validators to induce unbounded re-generation loops. In production, you want bounded repair strategies with recorded attempt counts and escalation rules.

For example:

  • First failure: JSON repair prompt on same model
  • Second failure: switch to a more reliable structured-output model
  • Third failure: mark planning stage failed and send to human review

Persist each attempt’s input/output pair and validator result. Otherwise you cannot distinguish “model quality issue” from “replay boundary bug” when costs spike.

Model and tool comparisons through the retry lens

Teams often compare models on benchmark quality and price but ignore how model behavior affects retry safety.

Fast, cheap model for planning

Pros:

  • lower cost per retry
  • lower latency for bounded repair loops

Cons:

  • may produce more malformed tool arguments
  • may drift more under replay
  • may increase validator failures and downstream uncertainty

This can still be the right choice if planning is strongly constrained and side effects happen only after validation.

More reliable structured-output model

Pros:

  • fewer validation failures
  • better for pre-execution stages where artifact stability matters

Cons:

  • higher token cost
  • sometimes slower latency

Often worth using specifically at boundaries where a stable machine-readable artifact is required.

Larger model for final response generation only

Pros:

  • preserves user experience quality
  • isolates expensive model usage to non-side-effecting stage

Cons:

  • if the system is not checkpointed properly, failures here can trigger catastrophic upstream replay cost

This is a strong pattern: use cheaper or more structured models for plan/execute stages, and reserve expensive generative models for the post-commit explanation layer.

Tool adapters with strong idempotency support

Prefer tools/APIs that offer:

  • caller-supplied idempotency keys
  • operation retrieval by key
  • eventual status lookup
  • explicit conflict semantics

If a vendor API lacks these, the hidden operational cost may dwarf the convenience of using it.

Cost and latency tradeoffs of finer-grained boundaries

Stateful retry boundaries are not free.

Costs increase from:

  • checkpoint writes
  • artifact storage
  • more orchestration logic
  • reconciliation jobs
  • versioning and audit metadata

Latency can increase from:

  • extra durable writes before/after side effects
  • validator stages
  • approval gates
  • fetching checkpointed artifacts on resume

But the savings are real:

  • fewer full workflow reruns
  • much lower duplicate action risk
  • lower token spend after late-stage failures
  • faster recovery from transport and provider incidents
  • easier root cause analysis

A practical heuristic:

  • Add boundaries before expensive or irreversible stages.
  • Avoid over-segmenting tiny deterministic steps unless needed for audit/compliance.
  • Measure the cost of replaying from each boundary versus the cost of persisting it.

For example, if retrieval + planning costs $0.20 and tool execution mutates customer state, a checkpoint before execution is almost always worth it. If a deterministic string transformation costs microseconds, checkpointing it separately probably isn’t.

Implementation details that matter more than teams expect

1. Use immutable artifacts plus mutable workflow pointers

Rather than updating a single mutable blob, store immutable artifact versions and keep the workflow state pointing at the currently committed versions.

This helps with:

  • audits
  • approval binding
  • replay debugging
  • comparing attempts

2. Separate “prepared” from “committed” for side effects

Before a side-effecting call:

  • persist operation intent
  • reserve dedupe key
  • mark prepared

After confirmed success:

  • mark committed
  • attach external reference

If acknowledgement is missing:

  • mark unknown_outcome
  • reconcile before retrying

This small distinction prevents a lot of duplicate calls.

3. Encode preconditions explicitly

Tool operations should record assumptions such as:

  • order status must still be delayed
  • shipment must not already be rebooked
  • approval version must equal 12

At execution time, verify preconditions. This catches cases where retries resume after the world has changed.

4. Make replay mode explicit to the runtime

Your execution engine should know whether it is:

  • first execution
  • in-stage retry
  • resuming from checkpoint
  • full restart by operator override

Why? Because some code paths should behave differently. For example, retrieval in replay mode may load the stored snapshot rather than query live systems.

5. Version prompts, tools, and retrieval corpora

Without version metadata, you cannot understand why retries diverged.

Record at least:

  • prompt/template version
  • model/provider version if available
  • tool schema version
  • retriever/index version
  • policy ruleset version

6. Build reconciliation workers

When a tool outcome is uncertain, a background reconciler should:

  • query downstream systems by idempotency key or business key
  • determine whether the action happened
  • update the operation record
  • only then release downstream stages

Do not force request-time code to guess.

Evaluation strategy: test replay semantics, not just answer quality

Many GenAI eval programs focus almost entirely on correctness, relevance, hallucination, or task success. Those matter, but they will not uncover retry-boundary bugs.

You need workflow reliability evals.

Failure-injection evals

Construct scenarios where faults occur at every boundary:

  • provider timeout before model output
  • timeout after model output but before checkpoint commit
  • tool timeout before acknowledgement
  • client disconnect mid-stream
  • validator failure on first attempt, pass on second
  • approval expires during retry
  • retrieval corpus changes between attempts

For each scenario, assert invariants such as:

  • no duplicate side effects
  • approved artifact version equals executed artifact version
  • resumed workflow does not re-run committed operations
  • final user-visible state matches durable workflow state

Determinism drift evals

Replay the same semantic request multiple times under controlled variation:

  • same retrieval snapshot
  • different retrieval snapshot
  • same prompt, different model version
  • same plan artifact, repeated response generation

Measure where outputs are stable enough to safely recompute and where they are not.

Idempotency and reconciliation evals

Simulate ambiguous tool outcomes:

  • downstream succeeded but 500 returned
  • connection dropped after request body sent
  • duplicate webhook delivery

Verify that operation logs, dedupe keys, and reconciliation logic converge to one committed action.

Streaming/resume evals

Test:

  • reconnect from chunk N
  • duplicate chunk delivery
  • final response fetch after incomplete stream
  • stream ends but workflow not committed
  • workflow committed but stream not completed

Human approval consistency evals

Inject changes between approval and execution:

  • plan version increments
  • retrieval evidence changes
  • tool args normalized differently

Assert that stale approvals are rejected.

Observability signals that expose hidden replay bugs early

If you only track latency, token count, and top-line success rate, you will miss replay bugs for a long time.

Here are the most useful signals.

1. Resume point distribution

Track where workflows resume after failures:

  • from request start
  • from retrieval checkpoint
  • from validated plan
  • from prepared operation
  • from response generation

Unexpected upstream restarts are a red flag.

2. Duplicate-intent rate

Measure how often the same business-intent dedupe key is attempted more than once, and how often that leads to more than one downstream mutation.

This should be near zero for well-behaved systems.

3. Unknown-outcome operation count

Count operations that entered unknown_outcome due to timeout/ack loss. A rising trend usually points to a flaky downstream or networking issue and is exactly where duplicate side effects emerge.

4. Approval-execution artifact mismatch

Track any case where executed artifact hash differs from approved artifact hash. This should be impossible by design. If it occurs, treat it as a severity-one bug.

5. Retry amplification factor

A critical metric:

total model/tool invocations per completed workflow / ideal invocations per completed workflow

When this drifts up, you may have silent replay loops or validator churn.

6. Checkpoint recovery latency

Measure time to resume from each boundary. If recovery is slow, teams are tempted to bypass boundaries and rerun from scratch.

7. Divergence between streamed output and committed artifact

Detect cases where the stream transcript shown to the user differs from the final committed response artifact. This catches race conditions in streaming pipelines.

8. Retrieval snapshot drift on replay

When a workflow resumes, log whether it reused a snapshot or fetched live data. If live fetches happen where snapshot reuse was expected, you have a replay-boundary violation.

9. Validator loop depth

Track how many repair attempts happen per stage. Rising depth often predicts cost spikes and hidden instability.

10. Cost by failure location

Break token/tool costs down by where the eventual failure occurred. This immediately reveals expensive late-stage failures that should have resumed from a checkpoint instead of restarting.

A reference workflow design

For a production GenAI action workflow, a solid baseline looks like this:

  1. Accept request with client-supplied request_id.
  2. Create or load workflow by request_id.
  3. Normalize entities and persist semantic request snapshot.
  4. Retrieve data and persist immutable retrieval snapshot.
  5. Generate structured plan using model chosen for reliability.
  6. Validate plan with deterministic and policy validators.
  7. Persist validated plan artifact.
  8. Request human approval if risk threshold exceeded, binding approval to plan artifact hash.
  9. For each side-effecting action:
    • persist prepared operation
    • execute via idempotent adapter
    • reconcile if outcome uncertain
    • mark committed
  10. Generate final explanation from committed action results.
  11. Persist response artifact.
  12. Stream or deliver response with resumable delivery semantics.
  13. Mark workflow completed.

This architecture is not flashy. It is what keeps “AI agent” workflows from behaving like optimistic scripts.

Practical takeaways

If you remember only a few things, make them these.

First, a retry boundary is not just a technical detail. It is a product and safety decision. It determines whether your system repeats expensive computation, repeats irreversible action, or safely resumes.

Second, not all stages are equal. LLM calls, retrieval, validators, human approvals, and side-effecting tools need different replay semantics. Model them explicitly.

Third, checkpoint the artifacts that stabilize meaning: retrieval snapshots, validated plans, approvals, operation intents, and committed results. Without those, retries change the workflow itself.

Fourth, every mutating tool call needs a durable operation record and an idempotency strategy tied to business intent. If outcome is uncertain, reconcile before retrying.

Fifth, streaming is delivery, not truth. The committed workflow state and persisted response artifact should be authoritative.

Sixth, approvals must bind to immutable machine-readable artifacts. Never allow a retry to silently execute a materially different plan under a stale approval.

Finally, evaluate and observe replay behavior directly. Inject failures, track resume points, measure retry amplification, and alert on approval/execution mismatches. Most replay bugs are invisible in happy-path evals and obvious in the right operational metrics.

In production GenAI, the question is not whether retries will happen. They will happen constantly: because models timeout, validators reject, clients disconnect, and tools lie by omission. The teams that ship reliable systems are the ones that decide, in advance and with durable state, exactly what a retry is allowed to do.

That is what a stateful retry boundary really is: a contract between your workflow, your models, your tools, and the business actions you cannot afford to execute twice.