Essay · Agent Infrastructure

We Gave Agents Tools but No Undo Button

Agent infra copied the model and skipped fifty years of database wisdom. A long agent with no rollback doesn’t just fail — it finishes and quietly corrupts your state.

The short version

We let agents act on the world — book, pay, delete, send — through dozens of tool calls, with no transaction wrapped around any of it. When step 37 fails, there is no rollback. The run is either dead or, worse, done and wrong.

Move the sliders in the live simulator, then open the Colab notebook and run the real 40k-trial experiment yourself.

A surgeon has a protocol for “stop, that’s wrong” mid-operation. A bank transfer that debits one account and crashes before crediting the other gets rolled back — the whole thing or nothing. We’ve handed agents the power to act on the real world with the same stakes, and given them none of that machinery. An agent that’s booked the flight, charged the card, and then fails to reserve the hotel doesn’t un-book the flight. It just stops, holding a mess.

The model got the glory; the execution layer got ignored. We obsess over reasoning traces and tool schemas, then wire those tools to production systems with no commit, no rollback, no idempotency — the exact guarantees databases spent fifty years getting right. Agent reliability isn’t only a smarter-model problem. It’s a transactions problem.

01Three ways a missing undo button bites

Long agentic runs fail in three distinct ways once they touch the world. Each maps to a database guarantee we simply never gave them.

1. One failure poisons the whole run (no atomicity)

Chain twenty tool calls with no transaction and the run is all-or-nothing in the worst sense: it only fully succeeds if every step succeeds, and per-step failure compounds. At a 5% failure rate, a 20-step run finishes clean barely a third of the time. There’s no “commit the good steps, retry the bad one” — because nothing defined a unit of work to commit.

Go deeper: why length is the enemy

Per-step success multiplies. A run of L steps that each work with probability p succeeds end-to-end with probability pL — an exponential decay in length. At p = 0.95, ten steps is 60%, forty steps is 13%, eighty steps is under 2%. The model didn’t get worse; the run got longer, and without checkpoints every extra step is another independent chance to throw the whole thing away.

2. Retrying without rollback corrupts state (no idempotency)

So you add retries — the obvious fix, and it does help the run finish. But a tool call that already half-executed before erroring isn’t safe to just run again. Retry “send payment” after a timeout and you may have paid twice. The run reports success; the world is now wrong. Finishing is not the same as being correct.

Go deeper: the silent-corruption tax

This is the failure mode nobody puts on a dashboard. A crashed run is loud — you see the stack trace. A run that retried a non-idempotent action and quietly double-booked, double-charged, or double-emailed reports green. The cost doesn’t show up in your success rate; it shows up in support tickets a week later. Idempotency keys (a unique token per logical action, so a replay is a no-op) are the cheap, decades-old fix — and almost no agent tool wrapper uses them.

3. There’s nothing to undo with (no compensation)

Even when you detect that a run went bad, what do you do? Real side-effects — an email sent, a row deleted, a charge made — can’t be wished away by clearing context. You need an explicit compensating action for each step: refund the charge, recall the message, restore the row. Without that inverse operation defined up front, “roll back” is just a word.

Go deeper: the saga pattern, borrowed from microservices

Distributed systems hit this wall years ago: you can’t hold a database lock across twelve services for ten minutes. The answer was the saga — a sequence of local transactions, each paired with a compensating transaction that semantically undoes it. If step 7 fails, you run the compensations for steps 6…1 in reverse. It’s not a true rollback (the email was sent), but a deliberate, defined “undo” for each action. An agent that touches the world needs exactly this: every tool ships with its inverse.

02Borrow the guarantees databases already wrote

The reassuring part: none of this is new. Atomicity, idempotency, durable checkpoints, and compensation are bread-and-butter in transaction processing and distributed systems. Each maps cleanly onto an agent that uses tools.

Transaction guarantee → agent move
Guarantee (DB / systems) Why it works The agent move What it prevents
Checkpoint & resume Commit good work so a failure costs one step, not the run Persist state after each step; retry from the last checkpoint Exponential collapse with run length
Idempotency keys A replayed action becomes a safe no-op Attach a unique key per logical action; dedupe on the tool side Double-charges and double-sends on retry
Compensating actions (saga) Every step ships with its semantic inverse Define undo(step) for each tool; run them in reverse on failure Partial side-effects with no way back
Write-ahead log Record intent before acting, so you can recover either way Log the planned call before executing; reconcile on restart “Did that step actually happen?” ambiguity
Dry-run / shadow mode Validate the plan before it touches anything real Simulate side-effects first; commit only the validated plan Acting on a plan that was never going to work
Two-phase commit on the risky bit Prepare-then-commit gates irreversible actions Gate irreversible tools (pay, delete, send) behind a confirm phase Irrecoverable actions fired mid-plan

03I simulated it — and you can run it live

To isolate the execution layer from model quality, I built a Monte-Carlo simulation of an L-step agent where each step fails independently. The model is held fixed; only the recovery machinery changes. Then we ask the question that actually matters: did the run finish AND leave the world in a correct state?

P(run finishes AND state is correct) — 40,000 trials/point 5 steps   No-undo 66%  |  Retry 80%  |  Checkpoint 100%
10 steps  No-undo 44%  |  Retry 64%  |  Checkpoint 100%
40 steps  No-undo 4%  |  Retry 16%  |  Checkpoint 100%
80 steps  No-undo 0%  |  Retry 3%  |  Checkpoint 100%
Probability a run finishes and is correct versus number of steps, for No-undo, Retry-only, and Checkpoint-plus-compensation. No-undo collapses to zero; Retry-only climbs but plateaus low; Checkpoint stays near 100%.
P(finishes AND correct) vs. run length — no-undo collapses, retry-only finishes-but-corrupts, only checkpoint+compensation holds.

Two things jump out. First, no-undo doesn’t degrade gracefully — it falls off a cliff, hitting zero by 80 steps purely because length compounds failure. Second, and more important: retry-only is a trap. It looks like a fix — the run finishes far more often — but every retried non-idempotent step is a coin-flip to corrupt state, so “finishes AND correct” plateaus far below 100% and the gap is invisible from inside the run. Only checkpoint-plus-compensation gives you both. The model was identical in all three. The execution layer was the whole story.

Live simulation · runs in your browser

Break my assumptions

Every slider re-runs the Monte-Carlo on the spot. Push the failure rate up, or the corruption rate down, and watch where retry-only stops being good enough.

No-undo Retry-only Checkpoint + compensation
At 80 steps: retry-only 3% · checkpoint 100%
Go deeper: exactly what the simulation does

Each of the L steps fails independently with probability f. No-undo takes no retries, so the run is correct only if all L steps pass first try — that’s (1−f)L. Retry-only retries each step up to R times (so a step dies only if it fails R+1 times in a row); but any step that needed a retry leaves a corrupting partial side-effect with probability c, and there’s no rollback to clean it — so “correct” requires finishing and never corrupting. Checkpoint has the same retry success but compensation removes the corruption, giving (1−fR+1)L. The in-browser version runs up to 6,000 trials/point; the Colab notebook runs 40,000 for the figure — identical logic.

Open in Colab Run the real experiment. Free CPU, a few seconds, pure NumPy. Hit Runtime → Run all, then change the failure and corruption rates and watch the curves move. It’s the exact code behind the figure above.

04Where transactions still won’t save you

Borrowing the database playbook is powerful, not free. Three honest limits:

· Some actions have no inverse. You can refund a charge, but you can’t un-send a leaked secret or un-delete what’s already gone downstream. Compensation handles the reversible majority; the truly irreversible tail needs a hard human gate, not a clever undo.

· Writing compensations is real work — and they can fail too. Every tool now needs a tested inverse, and the compensating action itself can error mid-rollback, which is its own gnarly recovery problem. This is engineering the “just add memory” crowd is hand-waving past.

· Checkpoints can preserve a poisoned state. If a step “succeeds” but commits subtly wrong data, resuming from that checkpoint faithfully carries the error forward. Transactions guarantee consistency, not correctness of intent — a confidently wrong plan checkpoints just fine.

The takeaway

Stop grading agents only on whether they finish. A long agent that touches the world needs the boring machinery we already built for databases: checkpoints so a failure costs one step, idempotency keys so retries are safe, and compensating actions so “undo” is real. None of it is novel research — it’s just unglamorous, and we skipped it.

The agent you’ll actually trust in production won’t be the one with the best reasoning trace. It’ll be the one that can take it all back.

Companion piece: We Gave Agents Memory but No Way to Forget — why “memory” that can’t forget makes agents worse, with its own live simulation.

SM
Sugeerth Murugesan Staff ML Engineer / Scientist · Intel / Intuit