Skip to content

Checkpoints & review gates

Autonomy is only comfortable if it is reversible and inspectable. LatchAI’s answer is two mechanisms that share one diff surface: checkpoints, which make every run reviewable after the fact, and review gates, which make a run stop and ask.

A shadow git repository (<home>/runs/checkpoints.git, with the workspace as its work-tree) snapshots around every run: pre-run before it starts, run <id> after it finishes. Two consequences follow from the “shadow” part — your own repositories are never touched by LatchAI’s undo history, and the workspace doesn’t have to be a git repository at all for this to work.

Nothing about this is opt-in. Every workflow run and every chat turn begins with a pre-run commit of whatever was outstanding and ends with a run <id> commit of what changed, so “what did that do?” always has an answer.

Run diffs surface as checkpoint.created events carrying a short sha, a --stat summary and the diff itself (capped at 100,000 characters), and the Runs sidebar grows a ± per run that opens it.

A run’s diff, opened from the Runs sidebar

Every run has a before and after.

A few boundaries are deliberate:

  • Workspace only. Mounted project folders are not in the shadow repo — they usually have their own git, and the git panel is the surface for those. What gets checkpointed is what agents write into workspace/: drafts, briefs, chat notes, dashboard and atlas exports, and agent memory.
  • Dependency trees and symlinked project roots are excludednode_modules/, *.log, .DS_Store and workspace/projects — because a diff drowning in ten thousand vendored files is not a review, and git’s output buffer agrees.
  • A run that changed nothing produces no checkpoint and no event, so the sidebar’s ± simply doesn’t appear.

The overlay’s ⟲ Revert changes button applies an inverse commit in the shadow repo (git revert --no-edit <sha>), which undoes that run’s file changes without rewriting history. Today it is wired to chat turns — the ± Review changes affordance in the chat panel — while run diffs opened from the Runs sidebar are read-only. The endpoint itself is not chat-specific: POST /api/checkpoints/revert with {"sha": "…"} reverts any checkpoint.

The review_gate node type pauses a run where you put it. It snapshots the mid-run diff, emits a gate.waiting event, and blocks until the gate is resolved. The workbench auto-opens a modal review overlay — a colored unified diff, an optional note, and Approve/Reject.

Approving continues the run, and the note becomes the node’s output, so downstream nodes can read what the reviewer said. Rejecting fails the run.

A review gate waiting for approval

The run is blocked until someone decides.

A gate is an ordinary node with no config — everything it needs it gets from where you put it in the graph:

{
"nodes": [
{ "id": "draft", "type": "agent", "position": { "x": 320, "y": 160 },
"config": { "agent": "release-writer", "prompt": "Draft the notes into out/notes.md." } },
{ "id": "review", "type": "review_gate", "position": { "x": 580, "y": 160 }, "config": {} },
{ "id": "publish", "type": "agent", "position": { "x": 840, "y": 160 },
"config": { "agent": "publisher",
"prompt": "Publish out/notes.md. The reviewer said: {{review}}" } }
],
"edges": [
{ "from": "draft", "to": "review" },
{ "from": "review", "to": "publish" }
]
}

The diff the reviewer sees is everything the run has changed so far — from the run’s starting commit to the working tree at this instant — not just the immediately preceding node. That is usually what you want: the question a gate asks is “is this run’s work acceptable”, not “did that one step behave”.

{{review}} resolves to approved when the note is empty, and approved: <your note> when it isn’t. That makes the note a real input rather than an audit comment: “approved: tighten the headline before publishing” is something the next agent can act on.

Rejecting fails the node with review rejected: <note>, which fails the run — the run.failed event carries the reason, so the log records why a nightly job stopped.

Gates are resolved over HTTP, which is what the overlay calls:

Terminal window
curl -X POST http://127.0.0.1:7777/api/gates/resolve \
-H 'content-type: application/json' \
-d '{"runId":"run-1748…-3","nodeId":"review","approved":true,"note":"ship it"}'

GET /api/gates lists the pending gate keys (<runId>:<nodeId>). A key that no longer exists comes back 404, so a double-approve is a clear error rather than a silent no-op.

Chat turns are checkpointed too, and working-changes review points the identical overlay at your git diff. Whether the change came from a scheduled run, a chat turn, or your own uncommitted work, “show me the diff” looks the same.