Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Checkpoints, /undo and the turn-end list

The harness takes a picture of the project before a command runs, lists what changed at the end of each turn, and restores files with /undo, even ones git does not hold. Checkpoints are on by default; [checkpoints] in config.md sets the limits.

What a picture is, and when one is taken

A picture records each covered file’s type, size, mode and times, and copies its content to the store. One is taken after a call is approved, before it runs:

  • a whole picture before every bash, exec (including a script’s) and agent call, and before the turn’s first file-changing call;
  • a path picture before each later write_file or edit_file call in the turn.

Later pictures copy only what changed. A failed picture never blocks the call; the gap is reported. Child agents run with checkpoints off; the picture before their agent call covers them.

What is covered

Every regular file and symlink in the project (links are recorded, not followed), except:

  • .git/ and what .gitignore excludes; without git, folders named node_modules, .venv, venv, target, __pycache__, .gradle or .next;
  • anything [checkpoints] skip names, and the harness’s own folders;
  • credential files (.env, .env.* except .example, .sample and .template, *.pem, *.key, *.p12, *.pfx, anything in a .secrets folder) and files over max_file_mib: recorded, never copied;
  • empty folders, FIFOs, sockets, devices, and anything outside the project.

A .gitignore the harness cannot use whole (unreadable, a link, over 1 MiB, or an ambiguous line such as ***) leaves its folder not checked.

A file with no copy says why: no copy (over 100 MiB), no copy (credential file), no copy (picture budget), no copy (changed during capture), no copy (unreadable name), no copy (unreadable).

The turn-end list

When a turn ends (however it ends) with changes, the harness lists them against the turn’s first whole picture:

files: 2 changed, 1 new, 16 deleted
  changed  src/parser.rs
  changed  Cargo.toml
  new      scripts/tidy.sh
⚠ deleted  docs/notes/ (16 files)   not in git   /undo

Up to 10 paths show, then … and N more; flagged lines always show. Changes you make in your editor appear too. harness export includes the list. With checkpoints off, there is no list. A path no picture covered is not checked, never new or deleted.

Flags are fixed rules. Each flagged line offers /undo <path> or /undo, except a new protected file (a restore never deletes).

  • not in git: a deleted or emptied file git has no copy of. Git is asked about at most 100 files per turn; not checked against git means it could not answer.
  • 20+ deleted: 20 or more files deleted in the turn.
  • protected: a file on the protected list changed (modes.md, “Protected files”).

A loss with no copy shows the reason instead of an undo; a folder line counts them (3 of 16 without a copy).

Notes state any gap, so a partial list never reads as “nothing changed”: no checkpoint: <why> (such as the checkpoint store is busy (another session is using it) or the checkpoint store is full), checkpoint partial: …, or this list starts before <command>: an earlier call this turn had no checkpoint.

Resuming a killed session

If the harness is killed mid-command (closed terminal, dropped SSH, reboot), harness resume <session> picks the session up without re-running the command. Run from the session’s folder, it prints the killed call’s failed line, then:

This session was killed while a command ran: `printf x >> a.txt && printf ok > started && sleep 60.071`. It may have partly run; its output was not recorded.
  Since it started:
    changed   a.txt      /undo restores
    new       started
  Not covered: gitignored files, anything outside the project.

The model gets the same text as the command’s failed result. The first line depends on what was killed; a file write, for example, says The file may be empty or cut short. A killed agent call names its child sessions, which are not resumed.

The change list runs from the last good picture before the call to the project now, so it includes your edits since the kill. When it cannot, it says why, such as no checkpoint: checkpoints were off.

The turn-end list follows the notice, for the interrupted turn:

files: 1 changed, 1 new
  changed  a.txt
  new      started
↳ 0.0k in / 0.0k out (interrupted)

Leftover processes and protected files the command planted appear as further notice lines; see sandbox.md, “A killed command’s leftovers”.

Replay refuses such a session: not replayable — session contains an interrupted turn or an abandoned effect.

/undo

/undo opens a picker of this session’s whole pictures, newest first (before ./tidy.sh · 14:02). Enter previews the restore:

undo to before ./tidy.sh: 1 to write back, 1 to recreate, 1 kept, 1 left alone
  write back  src/parser.rs
  recreate    docs/notes/a.md
  leave       big.bin   no copy (over 100 MiB)
  kept        scripts/tidy.sh   (created since)

On the panel, Enter restores; Esc or Ctrl-C cancels. /undo <path> restores one file to its state before its latest change, with the same preview.

During a turn, /undo answers wait for the turn to end. With nothing to undo it says no checkpoints in this session; a path outside the project gets /undo takes a path inside the project. A cleared picture says the checkpoint … was cleared (older than keep_days, or the store was full).

It never deletes. Files created since the picture are kept. A path it cannot write is skipped and reported.

It is guarded. It skips files changed since the preview, and first saves every file it will overwrite; one it cannot save (too big, a credential file) is left alone. Hard links and symlinks survive, and modes are never widened. Times are not restored, so build tools see restored files as new.

It can be undone. The picture it saves first appears in the next /undo as before /undo.

restored 2 files to before ./tidy.sh
  written     src/parser.rs
  recreated   docs/notes/a.md
  skipped     big.bin   no copy (over 100 MiB)
  kept        scripts/tidy.sh
  saved first: /undo can undo this

At its next turn the model is told “The user restored N files to their state before <label>.” The agent has no undo tool and cannot reach the store.

In the plain line client (pipes, redirects), bare /undo prints the pictures numbered and reads a number (type a number to preview it, anything else cancels), then asks restore? type y to confirm. y, Y, yes or Yes restores; anything else prints undo cancelled.

/checkpoints

/checkpoints shows checkpoints: on or checkpoints: off. /checkpoints on and /checkpoints off switch until the next resume, which reads [checkpoints] enabled again. /undo still works on earlier pictures.

A project rooted at your home folder or / takes no pictures. The start notice says checkpoints are off: the project is your home folder (or the project is the root folder (/)), /checkpoints shows checkpoints: off (<why>), and /checkpoints on repeats the notice.

Limits

LimitDefaultKey
Largest file copied100 MiBmax_file_mib
Whole store, per project5 GiBmax_store_gib
Age before a picture is cleared14 dayskeep_days
Files one picture copies10,000max_picture_files
Bytes one picture copies500 MiBmax_picture_mib
Time one picture may take10 secondsmax_picture_seconds
Entries one walk lists100,000max_walk_entries

Files past a picture’s budget get no copy (picture budget) and are copied by later pictures. A picture that stops early says so, for example checkpoint partial: 48,000 new files in out/, 10,000 copied. Add out/ to .gitignore or raise the limit. At the store cap, the oldest pictures are cleared first. Esc or Ctrl-C during a picture stops the call: interrupted while taking a checkpoint; the call did not run.

Where the store is, and who can read it

$DATA/checkpoints/<project key>/, owner-only (folders 0700, files 0600). The key is the project path as spelled, so two spellings of one project make two stores.

Sandboxed commands never see checkpoints/ or sessions/ (sandbox.md), so harness export does not work inside one. The agent’s file tools refuse both: <path> is inside the harness's private <name> folder. Commands you approve to run outside the sandbox can reach the store. Deleted content stays there for keep_days.

Known limits

  • Two sessions in one project share a store. One that cannot get it in time reports a busy gap; its call still runs.
  • The credential list has gaps: id_rsa, .netrc and credentials.json are copied.
  • A file whose name is not UTF-8 cannot be restored.
  • A writer racing a restore can leave a .harness-displaced-* or .harness-restore-* file, named in the result.
  • Folders deeper than 64 levels are not pictured and show as not checked.