An agent tries to move 24 due dates from Thursday to Friday. Two records can’t be updated. A teammate later changes one of the remaining dates to Monday. Another date goes from Friday to Monday and back to Friday.
The user clicks Undo. Which dates should change?
A button labeled Undo hides several decisions: what the original operation actually did, what has happened since, and whose work recovery is allowed to overwrite. When the product can’t answer those questions, “undo” promises more than the product can deliver.
Begin with the effects that remain
Restoring a field and reversing the field’s consequences are different jobs. A due-date change can send a notification, trigger an automation, or make somebody reschedule their own work. Putting the field back to Thursday can’t make those effects un-happen.
The useful distinction is the boundary of the recovery:
| Original operation | Possible recovery | What may remain |
|---|---|---|
| Prepare an unsaved edit | Discard the proposal | The conversation and review history |
| Update a due date | Restore the previous date, if nothing changed since | Notifications, dependent changes, and later human decisions |
| Create a draft in another app | Remove or archive the draft, if allowed | Outside history, references, and copies |
| Send an email | Send a correction | The original message, its recipients, and what they did next |
The rows are examples with conditions, not a universal ranking. A supposedly private draft may have been shared. An apparently local edit may already have synced elsewhere. Describe the effects the recovery can actually reverse.
Approval should name the work recovery will refer to
Before talking about undo, make the original operation concrete. A search for “unfinished tasks in this project” isn’t the same thing as the 24 records that matched when the person reviewed the change. A twenty-fifth task could appear while the approval waits.
There are two reasonable policies. Freeze the reviewed list and leave out later matches. Or refresh the proposal and ask the person to review the new records. Either policy works when the interface explains the policy. Quietly adding an unreviewed record to the run makes recovery harder before any write happens.
The proposal should record each task’s ID, old date, intended date, and version. A new version of the proposal needs a new approval. The new version doesn’t inherit approval just because the title still says “Move dates to Friday.”
The operation needs a stable ID too, so a person returning after a timeout can retrieve the original result instead of applying the change again to today’s records.
A current value is not a complete history
The easy conflict is visible. The agent wrote Friday, a teammate wrote Monday, and recovery should keep the teammate’s later decision. Comparing the current date with Friday catches the conflict.
The harder case looks unchanged:
| Event | Date | Record version |
|---|---|---|
| Original state | Thursday | 1 |
| Agent update | Friday | 2 |
| Teammate changes the plan | Monday | 3 |
| Teammate changes it back | Friday | 4 |
A check on the value alone sees Friday and may overwrite the teammate’s newest decision with Thursday. A check on the version sees 4, not the agent’s 2, and stops automatic restoration for that record.
The policy is intentionally cautious. Another product might track versions per field, so an unrelated title edit doesn’t block date recovery. Either way, the comparison has to represent the changes that matter. HTTP’s If-Match is one standard building block: the server refuses an update when the resource’s current version doesn’t match the version the client supplied. RFC 9110, section 13.1.1
The check and the write must also happen together. Reading the current version, deciding the write is safe, and then writing unconditionally leaves a gap in which another edit can arrive.
Partial success needs two receipts
An execution receipt should account for every attempted record. Successful updates need the previous value, the written value, and the written version. Locked, deleted, and conflicting records need an explicit outcome even though nothing changed.
Recovery starts from the successful writes in that receipt and produces a result of its own. A task skipped during execution isn’t a failed restoration: the operation never changed the task, so there’s nothing to restore.
The distinction should survive the summary a person sees. Say a refreshed proposal covered 25 tasks: 23 dates changed, and 21 can be restored after two newer edits. “Restored 21 dates; 2 have newer edits” tells a different story from “21 of 25 restored.” The second version makes four records sound like failures of undo, though two never changed. Link both receipts so a returning user can see the full account.
Repeating a recovery should return the original recovery receipt too. When a teammate edits a restored record afterward, a retry of the old recovery must not reverse the teammate’s new work. A fresh, authorized correction is a different operation.
Reverting the agent is a separate operation
A product can keep versions of an agent’s instructions, tools, and settings. Restoring those settings changes how future work runs. Restoring settings doesn’t, by itself, reverse the work the agent already produced.
Notion documents restoring a Custom Agent to a past configuration. Restoring settings is configuration recovery. Notion’s docs don’t say that pages, messages, or outside objects from earlier runs revert along with the settings. Notion’s Custom Agents
Give these actions different names when their effects differ: restore settings, cancel remaining work, restore eligible dates, prepare a correction. One global Undo label makes people discover the difference after they act.
Recovery has authority and limits of its own
The original permission to edit a record may no longer apply. The object may have moved, a connected account may have been revoked, or the person may have lost access. Undo is another write and needs the current permission checks for that write. A receipt is evidence of what happened, not a permanent license to change the object again.
Retention matters too. When previous values are kept for a limited time, show the limit while recovery is still possible. Put the operation and its remaining options in history, not only in a toast that disappears.
For a product review, ask for one concrete answer: after this operation and everything that happened since, what exactly will this recovery change, and what will the recovery leave behind? When the interface can’t show that before the person clicks, the product needs a narrower promise.