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) andagentcall, and before the turn’s first file-changing call; - a path picture before each later
write_fileoredit_filecall 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.gitignoreexcludes; without git, folders namednode_modules,.venv,venv,target,__pycache__,.gradleor.next;- anything
[checkpoints] skipnames, and the harness’s own folders; - credential files (
.env,.env.*except.example,.sampleand.template,*.pem,*.key,*.p12,*.pfx, anything in a.secretsfolder) and files overmax_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 gitmeans 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
| Limit | Default | Key |
|---|---|---|
| Largest file copied | 100 MiB | max_file_mib |
| Whole store, per project | 5 GiB | max_store_gib |
| Age before a picture is cleared | 14 days | keep_days |
| Files one picture copies | 10,000 | max_picture_files |
| Bytes one picture copies | 500 MiB | max_picture_mib |
| Time one picture may take | 10 seconds | max_picture_seconds |
| Entries one walk lists | 100,000 | max_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
busygap; its call still runs. - The credential list has gaps:
id_rsa,.netrcandcredentials.jsonare 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.