Hidden-ref checkpoints, diffs, and revert
Each completed turn can leave a Git tree snapshot under a hidden ref. Review reads those snapshots; revert restores workspace content first, then attempts provider-history rollback before it can complete the durable history rewrite.
What this chapter resolves
- Follow a pre-turn baseline and post-turn checkpoint from a Git workspace into the durable thread projection.
- Separate checkpoint turn diffs from live working-tree and branch comparisons.
- Recognize that restore is destructive and that a failed provider rollback can leave a real partial state.
- Compare provider rollback implementations without inventing a universal undo guarantee.
A T3 checkpoint is neither a Git branch nor a provider transcript backup. It is a Git tree snapshot stored under a hidden ref for one thread and numbered turn boundary. Its job is narrow: make a turn’s filesystem delta reviewable and make a local content restore possible. The product then coordinates that filesystem action with a separate provider-conversation rollback and a durable thread-history update.
A checkpoint is a hidden tree snapshot
The ref naming helper makes one location per thread and turn count:
refs/t3/checkpoints/<base64url thread id>/turn/<n>. A baseline at turn 0
is attempted before the first real turn when absent. When the checkpoint reactor
observes a matching completion, it attempts the next snapshot. If that hot
subscription misses completion while runtime ingestion receives it, the resulting
durable turn.diff.updated path can first create a missing placeholder and later
trigger a replacement attempt. A successful capture diffs against the preceding
checkpoint, parses the patch into changed-file rows, and only then dispatches the
durable thread.turn.diff.complete command.
Git capture deliberately uses a temporary index. It reads HEAD into that index
when one exists, stages the workspace into the isolated index, writes a tree,
creates a commit from that tree, and updates the hidden ref. The ordinary checked-out
branch is not moved by this sequence. That makes the checkpoint an implementation
snapshot of content, not a normal branch-history commit a user is expected to
checkout or push.
The durable checkpoint summary keeps the associated turn id, numbered boundary,
hidden ref, a ready / missing / error status, changed-file additions and
deletions, assistant-message linkage, and completion time. For the runtime-derived
path, failed becomes error; cancelled or interrupted becomes missing; and
completed or an unknown state becomes ready. Those are projection/capture-flow
diagnostics—not proof that a later diff, restore, or provider rollback will work.
missing can be a placeholder that the checkpoint reactor later attempts to replace
with a real Git-backed snapshot. error is an error-status result, not proof that
the workspace stayed untouched.
One review screen contains three different comparisons
The web review chooser labels these as Working tree, Branch changes,
Latest turn, and a selected prior turn. Only the turn modes call the checkpoint
diff query. A selected turn compares its prior checkpoint to its own numbered
checkpoint; a full-thread diff uses turn 0 as the baseline and a selected later
checkpoint as the target. Both read hidden refs and default to ignoring whitespace.
Working-tree and branch comparisons are not checkpoint snapshots. They query Git’s current state at the selected workspace, can refresh on focus or a newer completed turn, and may therefore include edits made after the selected turn. They are useful side-by-side review modes, but they answer different questions.
| Mode | Revisions compared | Time model | Can change while open? |
|---|---|---|---|
| Turn n | hidden checkpoint n - 1 → hidden checkpoint n | completed-turn boundary | no; it is a stored pair until refs are pruned |
| Full thread through n | hidden baseline 0 → hidden checkpoint n | accumulated completed turns | no; it is a stored pair until refs are pruned |
| Working tree | HEAD → current tracked worktree, plus patches for untracked files |
live filesystem | yes |
| Branch changes | merge-base(selected base, HEAD) → HEAD via git diff <base>...HEAD |
live repository refs | yes |
Zoom with the controls, +/−, or Ctrl/⌘ + trackpad scroll. Enable Pan to drag, use two-finger scrolling, or use the arrow keys. 0 fits the diagram; Esc leaves Pan or expanded view.
Text equivalent
A pre-turn workspace can become hidden checkpoint zero. The provider runs and changes the workspace. A matching completion can reach the checkpoint reactor directly through the hot runtime stream, or runtime ingestion can create a durable missing placeholder whose event triggers a later replacement attempt. Successful capture creates hidden checkpoint one. A diff from checkpoint zero to one is parsed into changed files and committed into the durable thread model. Turn review reads zero to one, while working-tree and branch review go directly to live Git state.
A revert is an ordered, cross-boundary saga
The client command thread.checkpoint.revert only records a
thread.checkpoint-revert-requested event after the decider confirms the thread
exists. The checkpoint reactor does the effectful work. It requires a bound provider
session with a Git workspace, checks that the requested turn count is not ahead of
the latest checkpoint, and resolves a target ref. A nonzero target must be available
in the checkpoint read model. Turn 0 uses the generated baseline ref and the Git
restore may fall back to current HEAD when that hidden baseline is unavailable.
Several of these preconditions fail before restore; failures inside the sequential
effect path can occur after filesystem mutation has begun.
If the target is available, the order is important:
- Git restores target content into worktree and index, removes untracked
non-ignored paths, then resets the index path when
HEADexists. - The workspace file index refreshes.
- The reactor asks
ProviderServiceto roll back exactly the number of later checkpoint turns. - It attempts best-effort deletion of hidden refs newer than the target.
- It dispatches
thread.revert.complete; projections then retain only derived messages, turns, plans, activities, and checkpoint data at or before that count.
There is no compensating Git restore if step 3 fails. The code catches the reactor
failure and appends checkpoint.revert.failed; because the completion command is
not dispatched, the durable projection and ref pruning do not report a completed
revert. That ordering creates a genuine partial failure: the files may already
match the target while provider history, newer refs, and durable thread history have
not converged. This is a code-path inference from the ordered calls, not a claim that
T3 repairs every such state automatically.
“Rollback” means different work in each adapter
The service asks every bound adapter for rollbackThread(threadId, numTurns), but
the implementation is not uniform. Codex delegates to its live app-server runtime.
Claude and Cursor truncate their adapter-maintained turn arrays (Claude also updates
its resume cursor). OpenCode reads native session messages, finds the earlier
assistant-message boundary, then calls session.revert. Grok deliberately returns
an unsupported provider-side rollback error at this pinned revision.
That table is a capability map for this implementation, not a promise that the upstream provider has erased files, billed tokens, cached context, tool side effects, or an external action. In particular, an adapter-local array truncation is not a general remote-history deletion guarantee.
| Adapter path | Rollback behavior observed here | Important limit |
|---|---|---|
| Codex | delegates counted rollback to the session runtime | requires a recoverable live/bound session and native support |
| Claude | truncates local tracked turns and refreshes resume cursor | this adapter-local state is not a provider transcript eraser |
| Cursor | truncates local tracked turns | adapter state only |
| Grok | returns “not supported … yet” | a revert that removes one or more checkpoint turns becomes unsupported after Git restore |
| OpenCode | calls native session.revert at an earlier assistant-message boundary |
semantics depend on the OpenCode session/message history it can read |
Walk one turn through capture, review, and a partial rollback failure
Each button is a discrete state. Nothing plays by itself, and the full ledger stays available without JavaScript.
State: Before turn. The current workspace is captured as the baseline hidden ref. The branch still points at its ordinary commit; the checkpoint is a separate content snapshot.
circles are illustrative content snapshots; a checkpoint ref is not shown as a normal branch tip.
Before turn
The current workspace is captured as the baseline hidden ref. The branch still points at its ordinary commit; the checkpoint is a separate content snapshot.
- Reference / operation
refs/t3/checkpoints/<base64url-thread-id>/turn/0- Observed files
- README.md · unchanged
- src/app.ts · unchanged
- Boundary
- A later turn diff can compare this baseline to the next checkpoint.
Complete state ledger
- Before turn
The current workspace is captured as the baseline hidden ref. The branch still points at its ordinary commit; the checkpoint is a separate content snapshot.
Boundary: A later turn diff can compare this baseline to the next checkpoint.
- After turn
After the turn, T3 builds a tree with an isolated Git index, stores it under a hidden ref, then derives a changed-file summary before it records checkpoint metadata.
Boundary: The visible summary is derived from the checkpoint diff; it is not an independent file watcher.
- Inspect a diff
A turn view compares checkpoint 0 → 1. A full-thread view compares the baseline → a selected checkpoint. Working-tree and branch views are separate live Git comparisons.
Boundary: Inspecting a patch does not restore files or alter the provider conversation.
- Revert to turn 0
The reactor restores worktree and index content, cleans untracked paths, refreshes workspace entries, asks the bound provider to roll back counted turns, attempts best-effort deletion of newer hidden refs, then records a completed revert.
Boundary: This is a content restore plus a provider-history operation, not a Git branch reset or an undo of a remote push.
- Provider rollback fails
Filesystem restore happens before provider rollback. If the live provider cannot roll back, the reactor appends a failure activity and does not dispatch thread.reverted; the working tree may already be restored while durable history and stale refs remain.
Boundary: The user must treat this as a mismatched partial state, not as a completed revert.
Read the status and failures as diagnostics
- A
missingcheckpoint can mean there is no usable hidden ref for that boundary yet. The reactor has a specific placeholder-replacement route, somissingis not a claim that Git capture is permanently impossible. - An
errorcheckpoint status and acheckpoint.capture.failedactivity describe different stages: a ref may exist while its changed-file summary cannot be derived, and a turn diff can fail because a required ref/workspace is unavailable. - A revert can fail before restore when the thread, bound session workspace, Git
repository, requested count, or nonzero read-model ref is unavailable. For turn
0, restore may fall back to currentHEADwhen the hidden baseline ref is unavailable. Sequential effect failures can still occur after filesystem mutation. - A completed
thread.revertedevent prunes selected derived projection rows; it does not rewrite Git’s ordinary branch history, certify every stale-ref deletion, or roll back actions outside the workspace/provider contract.