How the Checkpoint Navigation System in Roo Code Works: Task History and Resumption

Roo Code’s checkpoint navigation system creates lightweight Git checkpoints in a hidden shadow repository, allowing you to save, navigate, and restore task states by rewinding both the workspace and message history without affecting your actual Git repository.

The checkpoint navigation system in Roo Code enables safe experimentation by capturing task states as recoverable snapshots. Implemented in the RooCodeInc/Roo-Code repository, this feature uses an isolated Git-based architecture defined in src/services/checkpoints/ShadowCheckpointService.ts to track changes without polluting your project's version control history, making task resumption reliable and transparent.

Architecture Overview

The system centers on the ShadowCheckpointService class, an abstract service that manages a per-task shadow Git repository. Concrete implementations like RepoPerTaskCheckpointService handle lifecycle instantiation through a static create method that accepts CheckpointServiceOptions (task ID, workspace path, and global storage location).

Key components include:

  • getCheckpointService in src/core/checkpoints/index.ts: Lazily initializes the service, handles Git availability checks, and manages UI warnings during initialization timeouts.
  • checkpointSave, checkpointRestore, and checkpointDiff: Public API functions in src/core/checkpoints/index.ts that coordinate between the UI and shadow repository, automatically handling logging and telemetry.
  • Event-driven updates: The service emits "initialize", "checkpoint", and "restore" events. Listeners registered in index.ts (around lines 65-90) update the webview and emit checkpoint_saved chat messages.

Shadow Repository Creation

Each task receives an isolated shadow repository stored in global storage at <globalStorage>/checkpoints/<hash>. The initialization process in ShadowCheckpointService.ts configures your workspace as a Git worktree while keeping metadata completely separate:

await fs.mkdir(this.checkpointsDir, { recursive: true })
const git = createSanitizedGit(this.checkpointsDir)
await git.init({ "--template": "" })
await git.addConfig("core.worktree", this.workspaceDir)
await this.stageAll(git)
const { commit } = await git.commit("initial commit", { "--allow-empty": null })
this.baseHash = commit

The service writes an exclusion file to .git/info/exclude (via writeExcludeFile) to keep Roo Code-specific files like .rooignore out of snapshots. This setup ensures all file operations occur on your actual workspace while Git data remains isolated in the shadow directory.

Saving Checkpoints

When you invoke checkpointSave() or when the system auto-saves, the service stages all workspace changes—including untracked files—and commits them to the shadow repository. The implementation maintains an internal ordered array _checkpoints to track commit hashes:

await this.stageAll(this.git)
const result = await this.git.commit(message, options?.allowEmpty ? { "--allow-empty": null } : undefined)
const fromHash = this._checkpoints.at(-1) ?? this.baseHash!
const toHash = result.commit ?? fromHash
this._checkpoints.push(toHash)
this.emit("checkpoint", { fromHash, toHash, duration, suppressMessage })

The allowEmpty option (exposed as the force argument in checkpointSave) permits creating checkpoints even when no files have changed, ensuring you can mark specific moments in the conversation history.

Restoring Checkpoints and Task Resumption

Restoration involves more than file reversion. The checkpointRestore function in src/core/checkpoints/index.ts coordinates three critical actions:

  1. Workspace reset: Forces the working tree back to the target commit using git reset --hard and git clean -f -d, removing any new files created since the checkpoint.
  2. History truncation: Removes checkpoint entries newer than the restoration point from the internal _checkpoints array.
  3. Message rewinding: Discards assistant messages generated after the checkpoint by manipulating task.clineMessages.
await this.git.clean("f", ["-d", "-f"])
await this.git.reset(["--hard", commitHash])
const idx = this._checkpoints.indexOf(commitHash)
if (idx !== -1) this._checkpoints = this._checkpoints.slice(0, idx + 1)
this.emit("restore", { commitHash, duration })

This ensures that restoring a checkpoint rewinds both your files and the conversation context to that exact moment.

The checkpoint navigation system derives its history list from task chat messages rather than querying Git logs directly. Each successful save inserts a message with say: "checkpoint_saved" and text: <commitHash> into task.clineMessages.

To enumerate available checkpoints for the UI:

const checkpoints = task.clineMessages
    .filter(m => m.say === "checkpoint_saved")
    .map(m => m.text!);   // each text is a commit hash

This design ensures the checkpoint history remains synchronized with the conversation timeline visible in the UI, using the message history as the single source of truth for navigation.

Diffing Checkpoints

The checkpointDiff function generates comparisons between any two checkpoints or between a checkpoint and the current workspace. ShadowCheckpointService.getDiff stages current changes temporarily to compute the diff using git.diffSummary and git.show, then presents results using VS Code's native diff view via vscode.changes, showing files side-by-side.

Error Handling and Safety Measures

The system includes several safeguards:

  • Git availability: If Git isn't installed, checkpoints are disabled and the UI displays a warning via the webview.
  • Nested repository detection: The getNestedGitRepository check disables checkpoints when nested Git repositories are detected to prevent corruption.
  • Initialization timeouts: Slow shadow repository creation triggers a checkpointInitWarning message to the webview while the operation continues asynchronously.

Implementation Example

Here is a complete workflow demonstrating the checkpoint navigation system API:

import { checkpointSave, checkpointRestore, checkpointDiff } from "./core/checkpoints";

// Save a checkpoint (force empty commit if you want)
await checkpointSave(task, /*force=*/false, /*suppressMessage=*/false);

// List saved checkpoints (hashes) – UI typically does this:
const savedHashes = task.clineMessages
  .filter(m => m.say === "checkpoint_saved")
  .map(m => m.text!);

// Restore a previous checkpoint (e.g., the first one)
await checkpointRestore(task, {
  ts: task.clineMessages[0].ts,   // timestamp of the checkpoint_saved message
  commitHash: savedHashes[0],
  mode: "restore",                // actually revert workspace & task state
});

// Show a diff between two checkpoints
await checkpointDiff(task, {
  commitHash: savedHashes[1],
  previousCommitHash: savedHashes[0],
  mode: "checkpoint",   // diff current → next checkpoint
});

All three functions are thin wrappers around the shadow-repo service; they handle service creation, logging, telemetry, and UI notifications automatically.

Summary

  • Roo Code's checkpoint navigation system uses isolated shadow Git repositories to snapshot task states without affecting project history, stored under global storage at <globalStorage>/checkpoints/<hash>.
  • The ShadowCheckpointService class in src/services/checkpoints/ShadowCheckpointService.ts manages repository initialization, commits, and restorations using core.worktree to target your actual workspace.
  • High-level APIs in src/core/checkpoints/index.ts provide checkpointSave, checkpointRestore, and checkpointDiff functions that handle UI integration and message history synchronization.
  • Checkpoint history is tracked via checkpoint_saved messages in task.clineMessages, enabling the UI to navigate the task timeline reliably.
  • Restoration rewinds both the file system (via Git hard reset and clean operations) and conversation history (via message truncation), ensuring complete state resumption.

Frequently Asked Questions

How does Roo Code's checkpoint system avoid modifying my project's Git history?

The system creates a separate shadow repository in VS Code's global storage and configures Git's core.worktree setting to point at your actual workspace directory. This ensures all commits, branches, and Git metadata live in the isolated shadow directory, leaving your project's .git directory and history completely untouched.

What happens to my conversation history when I restore a checkpoint?

The checkpointRestore function automatically truncates task.clineMessages to remove any assistant messages generated after the checkpoint's timestamp. This synchronizes the conversation view with the restored workspace state, effectively undoing any AI outputs and file modifications that occurred after the saved point.

Can I create a checkpoint when there are no file changes?

Yes. The checkpointSave function accepts a force parameter that maps to Git's --allow-empty flag. When enabled, the system creates a checkpoint commit even with no file modifications, allowing you to mark specific decision points or conversational milestones in the task history.

Where are the shadow repositories physically stored?

Shadow repositories are stored in your VS Code global storage directory under checkpoints/<hash>/, as defined in the CheckpointServiceOptions passed to the service factory. Each task receives its own isolated directory containing the full Git history for that specific session, completely separate from your workspace file system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →