Skip to main content

The Agent Undo Button Is a Saga, Not a Stack

· 10 min read
Tian Pan
Software Engineer

A user clicks "undo" on an agent action that fanned out to twelve tool calls. The agent sent two emails, created a calendar invite, updated a CRM record, charged a card, and posted to a Slack channel. Three of those operations are non-reversible by API. Two are reversible only by an inverse operation that fires its own downstream notification. The remaining seven each have their own definition of idempotency that the planner never reconciled. The undo button you shipped looks reassuring. It quietly succeeds about 60% of the time and silently fails the rest.

This is not a UX bug. It is a saga-pattern problem that distributed-systems engineers have been working on for thirty years, and ignoring that lineage is the most expensive way to discover it.

The instinct, when product asks for an undo button on agent actions, is to model it like a text editor's undo stack: a list of operations, each with an inverse, popped in reverse order. That mental model maps cleanly to single-process state mutations — typing a character, moving a shape, deleting a row in a local document. It maps poorly to a system where each action is a network call to a different vendor with its own consistency guarantees, where some actions cause humans on the other side to take irreversible action of their own, and where the inverse of one step depends on the result of a step that has not yet been compensated.

What "Reversible" Actually Means When the Tool Crosses a Process Boundary

A useful first move is to stop treating reversibility as a binary property and start treating it as a contract on each registered tool. Three coarse classes are enough to be honest with yourself:

  • Cleanly reversible — the tool exposes an inverse API call whose effect, if executed, fully erases the original action. Creating a draft document and deleting the draft. Updating a record and writing back the prior values. These are the easy cases, and they are rarer than your tool catalog implies.
  • Compensable with residue — the original action cannot be erased, but a forward-going compensating action restores a known-good business state. A payment cannot be unsent; a refund can be issued. A meeting cannot be un-scheduled silently; a cancellation can be sent that fires a notification to every invitee. The compensation is a real action with real side effects, not a database rollback.
  • Non-reversible — once executed, the action's effects cannot be neutralized by any sequence of API calls. An email read by a human. An SMS that triggered a callback. A wire transfer past its reversal window. The only honest "undo" is a follow-up message authored by a human acknowledging the mistake.

Every tool registered with the agent should declare which class it belongs to, and the registration should be the source of truth. If a tool author cannot answer the question, the safe default is non-reversible — the failure mode of treating a non-reversible tool as compensable is silently shipping the lie that the user can take it back.

Pre-Compute the Inverse at Execution Time, Not at Undo Time

The other pattern teams reach for first is the wrong shape: store the executed steps in a log, and at undo time, walk the log and synthesize the inverse for each step. This fails for the same reason database rollback fails when state has already escaped the database: the information needed to construct the inverse is no longer recoverable.

If the agent updated a CRM record, the inverse is "set field X back to its prior value Y." That prior value Y must have been captured at the moment of the write, because by undo time the record may have been touched by a human, by another agent, by a webhook from a downstream system, and the field is now Z. Reconstructing Y from the log of agent actions does not work — Y was never written to the log; only the new value was.

The discipline that has to land is that every executed step writes a saga log entry containing both the forward operation and the pre-computed inverse, captured against the world state observed at execution time. The inverse is data, not code synthesized later. At undo time, the engine walks the log and dispatches the pre-computed inverse for each step, in reverse order, with each compensation idempotent and retryable.

This is the same discipline that orchestrators like Temporal codify, and it is the same discipline a homegrown harness has to invent if it is not using one. Skipping it means your undo button works on the demo flow because the demo flow does not have concurrent writers, and breaks on the first user whose CRM is also being updated by their email integration.

The UX Has to Tell the Truth About Partial Reversal

Even with pre-computed inverses, a real undo will land in three states, and a UX that collapses them into a single "undone" toast is lying to the user.

  • Fully reversed — every step had a compensable inverse, every inverse succeeded, the system is in a state observationally equivalent to "the agent never acted." This is the rare case and the one your screenshot shows in the slide deck.
  • Partially reversed with residues — some steps were cleanly reversed, others left side effects (the cancellation email that went to four people, the refund that posted but will take three business days to clear, the Slack message that was deleted but is already in someone's notification feed). The user needs to see the residue list, in plain language, with the affected parties and the time horizon for each item to settle.
  • Cannot be undone — one or more steps were non-reversible, and the agent honestly cannot make the world look like the action never happened. The UX has to surface what would need to change manually, who can change it, and ideally a draft of the message the user might send to do so.

The product team will push back on showing the residue list because it makes the agent look fallible. Show it anyway. The alternative is a user who trusts the undo button, discovers the residue weeks later through a complaint from the other party, and stops trusting any agent action your product takes. The trust cost of an unsurfaced partial reversal is much higher than the trust cost of an honest one.

Cap the Cascade Budget Before the Compensation Becomes Worse Than the Original

A subtler failure mode emerges once compensations themselves are agentic. The agent, asked to undo, plans the compensation as another sequence of tool calls. Some of those calls fan out — one cancellation triggers two notifications, each of which the agent then wants to "soften" with a follow-up, each of which fires more downstream effects. The compensation cascade can balloon past the size of the original action, and now the user is one undo click away from a hundred-tool-call apology spree.

The mitigation is a cap, set at the saga level: the compensation graph is bounded by an explicit budget — number of tool calls, total tokens, total latency — and once the budget is exhausted, the engine stops and surfaces a residue rather than continuing to chase symmetry. The cap is not a polish item; it is the only thing standing between a misclicked undo and an incident.

Build the Eval Suite for Adversarial Multi-Tool Chains

The functional version of this discipline is an eval suite that constructs adversarial sagas and asserts the system reaches a known-good state in bounded time after an undo request. The interesting cases are not the happy paths:

  • A saga where step three's inverse depends on the success of step five's inverse. (Order matters; the engine must respect dependencies, not just reverse-chronological iteration.)
  • A saga where one tool's inverse fails permanently mid-undo. The engine must not leave the system in a worse state than it started in — better to halt and surface a residue than to keep retrying compensations whose preconditions no longer hold.
  • A saga where a non-agentic actor (a human, another system) modifies the world between forward execution and undo. The pre-computed inverse may now be wrong; the engine has to detect divergence and degrade to "cannot be undone — manual review needed."
  • A saga where the user clicks undo twice in quick succession. The compensation must be idempotent at the saga level, not just at the individual tool level, or the second click will compound the residue.

These cases will not appear in the synthetic traces your team writes by hand. They appear when the eval generator is given license to vary the surrounding world between forward execution and undo, and when the assertions check end-state validity rather than per-step success.

Stop Calling It "Undo" Where the Word Doesn't Fit

The last move is a vocabulary one. "Undo" is a UX promise borrowed from a model that does not apply to your system. The honest options are narrower: revert for cleanly reversible state changes, compensate for actions that need a forward-going inverse, cancel for in-flight operations that have not yet committed, and acknowledge for actions that have already escaped your blast radius and now require a human-authored response. The product copy will resist this — "undo" tests well in usability research because users already know what it means. The reason it tests well is exactly the reason it misleads them about your system. If the product surface keeps the word, the engineering surface should not, and the gap between the two needs to be tracked as a known UX-engineering debt rather than papered over.

The Architectural Realization

Agent undo is not a feature; it is a contract that runs through every tool registration, every tool execution, every saga log entry, and every UX surface where the agent reports outcomes. Treating it as a feature — a button to add at the end of the project — is how teams ship the 60%-success undo button and discover, three quarters later, that the residue from the other 40% is what their users actually remember.

The lineage is not new. The pattern is the saga, the discipline is compensation, the building block is the pre-computed inverse, and the hard part has always been the boundary between the system you control and the world you cannot reverse. The novelty is that agents now generate plans that cross that boundary at machine speed, and the team that ships the undo button is on the hook for the boundary the planner forgot.

The forward-looking version of this work is small and unglamorous. Audit your tool catalog and label every tool with a reversibility class. Make pre-computed inverses a required field in your saga log. Build the residue UX before you build the undo button. Cap the cascade. Test against adversarial sagas. And rename the button, in the engineering surface if not the product surface, to something the system can actually keep its promise about.

References:Let's stay in touch and Follow me for more thoughts and updates