An AI agent creates an order. The request times out. A retry sounds reasonable—until the first request turns out to have succeeded.

Now there are two orders, a confused customer, and a perfectly fluent explanation from the agent. The model understood the request. The execution system lost track of what happened.

This article follows an illustrative order workflow, not a production incident report. The design question is simple: what evidence should a system require before it lets an agent say “done”?

A timeout is an observation about a conversation between systems. It is not a verdict on the business operation.

01 / The missing response

Imagine an order service commits order O-1042, then its response disappears on the return path. The caller observes a timeout; the database contains a valid order. From the caller’s perspective, “not executed” and “executed, response lost” look identical.

The useful state is UNKNOWN. Preserve the operation’s identity and reconcile it against an authoritative service. Do not manufacture certainty by turning every network exception into FAILED. AWS describes this ambiguity and the value of explicit caller-provided request identifiers in Making retries safe with idempotent APIs.

Figure 01 — The order commits, but its acknowledgement is lost. Reconciliation recovers the result using the original operation identity.
Figure 01 — The order commits, but its acknowledgement is lost. Reconciliation recovers the result using the original operation identity.

An accepted request is another intermediate state. A 202 response might mean that work is queued. Even a successful HTTP response must be interpreted through the endpoint’s contract: did it create the order, reserve inventory, or merely acknowledge receipt? Define completion at the business level before implementing the agent’s success message.

02 / Give intent a durable identity

Assign one operation ID to one authorized business intent, before the first side effect. Keep it stable across timeouts, process restarts, and retries. A new attempt gets a new attempt ID, not a new business operation ID. A genuinely new order gets a new operation ID, even if its contents happen to match an earlier order.

In this design, the order service scopes an idempotency key to the tenant and operation, associates it with a request fingerprint, and durably records the outcome. The deduplication record and the local order mutation share a transaction. A concurrent duplicate must observe that same operation rather than pass a separate check and create another order. Reusing the key with different parameters is a conflict, not permission to reinterpret the request.

Idempotency is a service contract with boundaries. Stripe, for example, documents that repeated requests can return the originally stored error, that parameters must match, and that a pruned key may subsequently represent a new request. Check retention and error semantics before treating a key as a permanent safety guarantee. See Stripe’s idempotency contract.

For our hypothetical checkout, the runtime stores a compact operation envelope:

operation_id   op_7f3a           # stable business intent
attempt_id     attempt_02        # changes on each delivery
scope          tenant_42 / create_order
request_hash   sha256(canonical_request)
state          UNKNOWN
resource_id    pending verification
evidence       timeout after dispatch

Persisting that envelope helps the runtime resume. It does not, by itself, deduplicate a downstream payment or shipping request. Each side-effect boundary needs its own supported identity and recovery contract.

03 / Put the execution contract around the model

The model can propose a tool call and interpret a business goal. In the architecture below, deterministic runtime code validates the proposal, binds it to an authorized operation, enforces retry limits, and records evidence. The tool service remains responsible for its own mutation and idempotency guarantees.

Figure 02 — The model proposes work; the execution runtime enforces authorization, identity, persistence, and verification around side-effecting tools.
Figure 02 — The model proposes work; the execution runtime enforces authorization, identity, persistence, and verification around side-effecting tools.

Authorization belongs here too. “Please retry” cannot expand the original amount, recipient, resource scope, or permission. If the proposed action changes materially, it becomes a new decision requiring the appropriate authorization. An operation ID identifies intent; it is not an authorization token.

Separate the execution record from the chat transcript. For this workflow, the record should answer: which action was authorized, which arguments were dispatched, which attempts occurred, which resource was observed, and why the runtime selected the next state. The model can explain this record without becoming its source of truth.

Prefer narrow tools with explicit results. A create-order tool could return an operation ID, a resource ID when available, a state, and a reference for subsequent checks. This makes UNKNOWN and IN_PROGRESS usable outcomes instead of exceptions that the model must guess its way through.

04 / Reconcile before declaring success

After an ambiguous response, read the operation status through a supported lookup or safely repeat the original request when the endpoint’s idempotency contract permits it. Keep the same operation identity. A newly generated key would describe a different operation and defeat deduplication.

Figure 03 — Unknown outcomes move through reconciliation. Verified completion, bounded waiting, a contract-safe retry, and human review are distinct paths.
Figure 03 — Unknown outcomes move through reconciliation. Verified completion, bounded waiting, a contract-safe retry, and human review are distinct paths.

Verification must be specific. Finding any recent order for the customer is insufficient. Match the tenant, operation identity, expected items, amount, and required lifecycle state. Use an authoritative status interface with understood consistency guarantees. An empty search result from a lagging replica does not establish that the original write never happened.

Keep recovery bounded: a deadline, an attempt budget, backoff, and a clear owner for unresolved work. A pending operation should remain pending when its budget expires; it should not silently become a fresh write. If the endpoint offers neither dependable status lookup nor safe retry semantics, escalation may be the only honest outcome.

The user-facing message should reflect that evidence. “Order O-1042 is confirmed” requires confirmation. “Your request was submitted; I’m still verifying the result” communicates progress without inventing success. “I could not verify completion; review is required” leaves a useful next step.

05 / Partial success needs a business recovery plan

Extend the example: reserve inventory, authorize payment, then create a shipment. Shipment creation fails after the first two steps succeeded. There is no single database transaction to roll back across these services.

Decide recovery policy before execution. The workflow might void the authorization and release inventory. It might instead retain the reservation briefly while another shipping option is reviewed. The appropriate choice depends on the business contract, not on which response sounds most helpful.

Compensation is a new action that attempts to restore an acceptable business state. It may fail, need retries, or require an operator; it does not necessarily recreate the original state. Microsoft’s Compensating Transaction pattern makes these limits explicit.

Track recovery as durable work with its own identities and evidence. “Compensation requested” and “compensation verified” are different states. Avoid telling a customer that everything was reversed while a void or release is still unresolved. Some actions—sending a notification, for example—cannot literally be undone.

06 / Evaluate the state left behind

My previous article on prompt optimization described an evaluation system that evolves with production evidence. Tool-using agents add another requirement: grade the world the agent leaves behind, as well as its explanation. Anthropic distinguishes an agent’s execution trace from its actual outcome in Demystifying evals for AI agents.

For this order workflow, start with a small fault-injection suite:

  • Lose the response after commit. Expect one order and a recovered identity.
  • Deliver the same request concurrently. Expect one business effect.
  • Restart the runtime after dispatch. Expect recovery from the stored operation.
  • Delay the read model. Expect uncertainty to remain visible until reliable evidence arrives.
  • Reuse a key with changed arguments. Expect a conflict, not another mutation.
  • Fail the recovery step. Expect a tracked unresolved case, not a false success message.

Measure duplicate-effect rate, false-completion rate, and time to resolve unknown outcomes, with explicit denominators and a defined observation window. Audit claimed successes against verified business state; otherwise the metric merely measures the agent’s confidence. Report unresolved operations separately so they cannot disappear inside an average success rate.

These tests express the proposed system’s invariants. They are not benchmark results, and no performance improvement is claimed here. Their purpose is to make a concrete promise testable: one authorized intent should produce its intended effect within the service’s guarantees, or leave a truthful, recoverable account of what remains unknown.

Let the agent say “done” when the execution record contains evidence of completion—and give it equally precise language for everything else.