Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Checkpoints and undo for coding agents

After this lesson: I can record every change a run makes, so that I can rewind the files to any step.

65 min lesson5 commandsAI coding agent lab: use opencode to explain and fix code, and check its work

Make every change reversible

Undo for an agent means recording what every write replaced, so the files can be put back to any earlier step. Agents make mistakes at step seven that only show at step nine.

With a change log you rewind to step six. Without one you read a diff and guess.

By the end of this lesson

Note

I can record every change a run makes, so that I can rewind the files to any step.

Key words

WordWhat it means
Snapshota copy of a file before it changes
Change logthe list of every change, in order
Stepone numbered action of a run
Undoputting a file back as it was
Rewindundoing every change after a chosen step
Before textwhat a file held before a write

The cells students fill in are left blank in this preview.

Undo is not resume

A checkpoint saves a run so it can resume after a stop. That moves forward. Undo moves the files backward to how they were.

They keep different records. A checkpoint holds the agent's state. A change log holds what each write replaced.

Log the before text

Before every write or delete, record the step number, the path and what the file held. If the file did not exist, record that too, as nothing.

To undo to step n, walk the log from the end back to step n + 1. Put each before text back. If the before was nothing, remove the file. Last first, or two edits to one file undo in the wrong order.

Worked example: four steps

StepActionBefore text
1Write app.pyx = 1
2Write new.pynothing: it did not exist
3Delete notes.txttodo
4Write app.pyx = 2

The cells students fill in are left blank in this preview.

A common mistake

Warning

Many people think git alone can undo an agent. Git only goes back to a commit. It cannot rewind to step six of a run with no commit in it, and it ignores files that are not tracked. Keep your own log of every write.

Write the change log, part 1

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const files = { \"app.py\": \"x = 1\", \"notes.txt\": \"todo\" };' 'const ops = [' '  { op: \"write\", path: \"app.py\", text: \"x = 2\" },' '  { op: \"write\", path: \"new.py\", text: \"y = 1\" },' '  { op: \"delete\", path: \"notes.txt\" },' '  { op: \"write\", path: \"app.py\", text: \"x = 3\" },' '];' 'function copy(f) { return Object.assign({}, f); }' > undo5.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the change log, part 2

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Before every op, log what the file held: its text, or null when it did not exist.' 'function applyRun(start, list) {' '  const now = copy(start);' '  const log = [];' '  list.forEach(function (o, i) {' '    log.push({ step: i + 1, path: o.path, before: o.path in now ? now[o.path] : null });' '    if (o.op === \"write\") now[o.path] = o.text;' '    else delete now[o.path];' '  });' '  return { files: now, log: log };' '}' >> undo5.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the change log, part 3

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Undo the entries after a step, last first.' 'function undoTo(now, log, step) {' '  const back = copy(now);' '  for (const e of log.slice().reverse()) {' '    if (e.step <= step) continue;' '    if (e.before === null) delete back[e.path];' '    else back[e.path] = e.before;' '  }' '  return back;' '}' >> undo5.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the change log, part 4

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function show(label, f) { console.log(label + \" \" + JSON.stringify(f)); }' 'const run = applyRun(files, ops);' 'show(\"start\", files);' 'show(\"after 4 steps\", run.files);' 'show(\"undo to 3\", undoTo(run.files, run.log, 3));' 'show(\"undo to 1\", undoTo(run.files, run.log, 1));' 'show(\"undo to 0\", undoTo(run.files, run.log, 0));' 'console.log(\"log: \" + run.log.map(function (e) { return e.step + \":\" + e.path; }).join(\" \"));' >> undo5.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run and rewind

Command
docker exec -w /home/node/project agent node undo5.js

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Files are a dictionary here

The files live in a dictionary in the script, so nothing on disk changes. A real harness writes real files, and keeps the log in a file or a store of its own.

The Run calls no model, so it needs no helper and has nothing to fall back on.

Output of the Run

LineWhat it shows
start {"app.py":"x = 1","notes.txt":"todo"}The files before the run
after 4 steps {"app.py":"x = 3","new.py":"y = 1"}The run wrote, added and deleted
undo to 3 {"app.py":"x = 2","new.py":"y = 1"}Only step 4 was undone
undo to 1 {"app.py":"x = 2","notes.txt":"todo"}Steps 2 to 4 were undone
undo to 0 ...The files as they started
log: 1:app.py 2:new.py 3:notes.txt 4:app.pyOne entry per step

The cells students fill in are left blank in this preview.

About this lab and its source: AI coding agent lab: use opencode to explain and fix code, and check its work

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.