Skip to the page
Conch
DocsGitHub

Decisions

0030 — Undo: every file the assistant changes can be put back

Status: accepted · 2026-10-01

  • Status: accepted
  • Date: 2026-10-01
  • Builds on: ADR 0020 (the backup manifest), ADR 0025 (protected paths), ADR 0028 (Activity, the guard before every tool)

Context

Activity (ADR 0028) shows everything the assistant did, but nothing in it could be taken back. A person who asks for "tidy my notes" and gets a deleted paragraph had to find the old text themselves, or use git (if the folder was a repository, and if they know git).

Claude Code has its own checkpoints (rewind), but they cover only Claude Code, only its file tools, and a whole session at a time. Conch runs several providers, some without tool events at all (Codex reports a turn, not each edit), and commands change files too (sed -i, mv, a formatter).

Decision

Conch keeps what a file was before the assistant changed it, for every provider, and puts it back in one press, from the chat or from Activity.

What's kept

  • File tools (Write, Edit, MultiEdit, NotebookEdit): the file is kept just before the tool runs and compared just after, inside the work folder or outside it. "Just before" is whichever comes first: the guard (Claude Code's PreToolUse hook, now given the tool_use_id), the permission question, or tool-start.
  • Commands and everything else: the work folder is looked at before and after, by size and modification time first, reading only what moved. What changed is put down to that command.
  • What a turn changed some other way (a provider without tool events): at the end of the turn, as "Changed during this turn".

Each tool call that changed something is one change set: its files, with the content hash and mode of each before and after. The chat gets a files.changed event; undoing or redoing appends files.restored.

Never kept: protected paths and secretPlaces() (keys, Passwords, SSH), CONCH_HOME itself (except the default work folder inside it), links, files over 5 MB, and folders that are rebuilt anyway (.git, node_modules, build output). A work folder that is the whole home folder, or over 10,000 files / 200 MB, keeps file-tool undo only, and says so.

Where

CONCH_HOME/undo/: content-addressed blobs/<aa>/<sha256> (each content once), sets/<id>.json, and index/<workspace>.json (the last look at a work folder). All 0700/0600. It's a derived rule in the backup manifest: it's about these files on this computer, and a backup restored elsewhere would undo into folders that aren't there.

Bounded

Change sets expire after 30 days (their copies go; a tombstone keeps Activity honest for 30 more), and the oldest expire first past 1 GB. Copies nothing refers to are swept. The sweep runs at start, every 6 hours, and in Repair everything (the undo doctor check, which also says how much is kept).

Putting it back

  1. Preview first. A dialog lists each file, what will happen (put back, removed because the assistant made it, brought back because it deleted it) and the change as a diff (Myers, 3 lines of context; binary and huge files say so instead).
  2. Conflicts. A file that changed since (you edited it afterwards) is named. It's left alone unless you choose "Undo all, replacing later changes".
  3. Never somewhere else. A restore never writes through a link, never into a folder whose real path moved (checked again after creating it), never into forbidden places, and never from a copy whose hash doesn't match.
  4. Several at once ("Undo all N changes from this turn", or from Activity): undo walks newest first, redo oldest first; each path's expected state comes from the first set that touched it and its target from the last.
  5. Redo is the same, the other way.

The API is POST /api/undo/preview, POST /api/undo (force for conflicts) and GET /api/undo/latest. Ids are checked against ^[A-Za-z0-9_-]{1,64}$ before touching the disk; 404 means not there, 410 too old.

Where you see it

  • The chat: a quiet line after each change ("Changed notes.md · Undo"), "Undone · … · Redo" after, and "Undo all N changes from this turn" on a turn's last change.
  • Activity: Undo or Redo beside each change, and Forget (or Remember again) beside each memory the assistant saved or forgot.
  • ⌘K: "Undo the last change".

Consequences

  • Undo works the same for every provider; the mock engine really writes note.md so the e2e journey (e2e/undo.spec.ts) undoes a real file.
  • Scanning the work folder costs a directory walk per command. Unchanged files aren't read, and the walk stops at its limits.
  • What the assistant changed outside the work folder with a command isn't seen. File tools are, wherever they write.