docs: update checkpoint/rollback docs for new features
- Reflect that checkpoints are now enabled by default - Document /rollback diff <N> for previewing changes - Document /rollback <N> <file> for single-file restore - Document automatic conversation undo on rollback - Document terminal command checkpoint coverage - Update listing example to show change stats - Fix config path (checkpoints.enabled, not agent.checkpoints_enabled) - Consolidate features/checkpoints.md to brief summary with link
This commit is contained in:
parent
9e845a6e53
commit
00110fb3c3
2 changed files with 115 additions and 159 deletions
|
|
@ -6,10 +6,28 @@ description: "Filesystem safety nets for destructive operations using shadow git
|
||||||
|
|
||||||
# Checkpoints and `/rollback`
|
# Checkpoints and `/rollback`
|
||||||
|
|
||||||
Hermes Agent can automatically snapshot your project before **destructive operations** (like file write/patch tools) and restore it later with a single command.
|
Hermes Agent automatically snapshots your project before **destructive operations** and lets you restore it with a single command. Checkpoints are **enabled by default** — there's zero cost when no file-mutating tools fire.
|
||||||
|
|
||||||
This safety net is powered by an internal **Checkpoint Manager** that keeps a separate shadow git repository under `~/.hermes/checkpoints/` — your real project `.git` is never touched.
|
This safety net is powered by an internal **Checkpoint Manager** that keeps a separate shadow git repository under `~/.hermes/checkpoints/` — your real project `.git` is never touched.
|
||||||
|
|
||||||
|
## What Triggers a Checkpoint
|
||||||
|
|
||||||
|
Checkpoints are taken automatically before:
|
||||||
|
|
||||||
|
- **File tools** — `write_file` and `patch`
|
||||||
|
- **Destructive terminal commands** — `rm`, `mv`, `sed -i`, `truncate`, `shred`, output redirects (`>`), and `git reset`/`clean`/`checkout`
|
||||||
|
|
||||||
|
The agent creates **at most one checkpoint per directory per turn**, so long-running sessions don't spam snapshots.
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/rollback` | List all checkpoints with change stats |
|
||||||
|
| `/rollback <N>` | Restore to checkpoint N (also undoes last chat turn) |
|
||||||
|
| `/rollback diff <N>` | Preview diff between checkpoint N and current state |
|
||||||
|
| `/rollback <N> <file>` | Restore a single file from checkpoint N |
|
||||||
|
|
||||||
## How Checkpoints Work
|
## How Checkpoints Work
|
||||||
|
|
||||||
At a high level:
|
At a high level:
|
||||||
|
|
@ -21,24 +39,11 @@ At a high level:
|
||||||
- Stages and commits the current state with a short, human‑readable reason.
|
- Stages and commits the current state with a short, human‑readable reason.
|
||||||
- These commits form a checkpoint history that you can inspect and restore via `/rollback`.
|
- These commits form a checkpoint history that you can inspect and restore via `/rollback`.
|
||||||
|
|
||||||
Internally, the Checkpoint Manager:
|
|
||||||
|
|
||||||
- Stores shadow repos under:
|
|
||||||
- `~/.hermes/checkpoints/<hash>/`
|
|
||||||
- Keeps metadata about:
|
|
||||||
- The original working directory (`HERMES_WORKDIR` file in the shadow repo).
|
|
||||||
- Excluded paths such as:
|
|
||||||
- `node_modules/`, `dist/`, `build/`
|
|
||||||
- `.venv/`, `__pycache__/`, `*.pyc`
|
|
||||||
- `.git/`, `.cache/`, `.pytest_cache/`, etc.
|
|
||||||
|
|
||||||
The agent creates **at most one checkpoint per directory per turn**, so long running sessions do not spam snapshots.
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
user["User command\n(hermes, gateway)"]
|
user["User command\n(hermes, gateway)"]
|
||||||
agent["AIAgent\n(run_agent.py)"]
|
agent["AIAgent\n(run_agent.py)"]
|
||||||
tools["File tools\n(write/patch)"]
|
tools["File & terminal tools"]
|
||||||
cpMgr["CheckpointManager"]
|
cpMgr["CheckpointManager"]
|
||||||
shadowRepo["Shadow git repo\n~/.hermes/checkpoints/<hash>"]
|
shadowRepo["Shadow git repo\n~/.hermes/checkpoints/<hash>"]
|
||||||
|
|
||||||
|
|
@ -50,108 +55,128 @@ flowchart LR
|
||||||
tools -->|"apply changes"| agent
|
tools -->|"apply changes"| agent
|
||||||
```
|
```
|
||||||
|
|
||||||
## Enabling Checkpoints
|
## Configuration
|
||||||
|
|
||||||
Checkpoints are controlled by a simple on/off flag and a maximum snapshot count **per directory**:
|
Checkpoints are enabled by default. Configure in `~/.hermes/config.yaml`:
|
||||||
|
|
||||||
- `checkpoints_enabled` – master switch
|
|
||||||
- `checkpoint_max_snapshots` – soft cap on history depth per directory
|
|
||||||
|
|
||||||
You can configure these in `~/.hermes/config.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
agent:
|
checkpoints:
|
||||||
checkpoints_enabled: true
|
enabled: true # master switch (default: true)
|
||||||
checkpoint_max_snapshots: 50
|
max_snapshots: 50 # max checkpoints per directory
|
||||||
```
|
```
|
||||||
|
|
||||||
Or via CLI flags (exact wiring may depend on your version of the CLI):
|
To disable:
|
||||||
|
|
||||||
```bash
|
```yaml
|
||||||
hermes --checkpoints
|
checkpoints:
|
||||||
# or
|
enabled: false
|
||||||
hermes chat --checkpoints
|
|
||||||
```
|
```
|
||||||
|
|
||||||
When disabled, the Checkpoint Manager is a no‑op and never attempts git operations.
|
When disabled, the Checkpoint Manager is a no‑op and never attempts git operations.
|
||||||
|
|
||||||
## Listing Checkpoints
|
## Listing Checkpoints
|
||||||
|
|
||||||
Hermes exposes an interactive way to list checkpoints for the current working directory.
|
From a CLI session:
|
||||||
|
|
||||||
From the CLI session where you are working on a project:
|
```
|
||||||
|
|
||||||
```bash
|
|
||||||
# Ask Hermes to show checkpoints for the current directory
|
|
||||||
/rollback
|
/rollback
|
||||||
```
|
```
|
||||||
|
|
||||||
Hermes responds with a formatted list similar to:
|
Hermes responds with a formatted list showing change statistics:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
📸 Checkpoints for /path/to/project:
|
📸 Checkpoints for /path/to/project:
|
||||||
|
|
||||||
1. a1b2c3d 2026-03-13 10:24 auto: before apply_patch
|
1. 4270a8c 2026-03-16 04:36 before patch (1 file, +1/-0)
|
||||||
2. d4e5f6a 2026-03-13 10:15 pre-rollback snapshot (restoring to a1b2c3d0)
|
2. eaf4c1f 2026-03-16 04:35 before write_file
|
||||||
|
3. b3f9d2e 2026-03-16 04:34 before terminal: sed -i s/old/new/ config.py (1 file, +1/-1)
|
||||||
|
|
||||||
Use /rollback <number> to restore, e.g. /rollback 1
|
/rollback <N> restore to checkpoint N
|
||||||
|
/rollback diff <N> preview changes since checkpoint N
|
||||||
|
/rollback <N> <file> restore a single file from checkpoint N
|
||||||
```
|
```
|
||||||
|
|
||||||
Each entry shows:
|
Each entry shows:
|
||||||
|
|
||||||
- Short hash
|
- Short hash
|
||||||
- Timestamp
|
- Timestamp
|
||||||
- Reason (commit message for the snapshot)
|
- Reason (what triggered the snapshot)
|
||||||
|
- Change summary (files changed, insertions/deletions)
|
||||||
|
|
||||||
|
## Previewing Changes with `/rollback diff`
|
||||||
|
|
||||||
|
Before committing to a restore, preview what has changed since a checkpoint:
|
||||||
|
|
||||||
|
```
|
||||||
|
/rollback diff 1
|
||||||
|
```
|
||||||
|
|
||||||
|
This shows a git diff stat summary followed by the actual diff:
|
||||||
|
|
||||||
|
```text
|
||||||
|
test.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/test.py b/test.py
|
||||||
|
--- a/test.py
|
||||||
|
+++ b/test.py
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-print('original content')
|
||||||
|
+print('modified content')
|
||||||
|
```
|
||||||
|
|
||||||
|
Long diffs are capped at 80 lines to avoid flooding the terminal.
|
||||||
|
|
||||||
## Restoring with `/rollback`
|
## Restoring with `/rollback`
|
||||||
|
|
||||||
Once you have identified the snapshot you want to go back to, use `/rollback` with the number from the list:
|
Restore to a checkpoint by number:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
# Restore to the most recent snapshot
|
|
||||||
/rollback 1
|
/rollback 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Behind the scenes, Hermes:
|
Behind the scenes, Hermes:
|
||||||
|
|
||||||
1. Verifies the target commit exists in the shadow repo.
|
1. Verifies the target commit exists in the shadow repo.
|
||||||
2. Takes a **pre‑rollback snapshot** of the current state so you can “undo the undo” later.
|
2. Takes a **pre‑rollback snapshot** of the current state so you can "undo the undo" later.
|
||||||
3. Runs `git checkout <hash> -- .` in the shadow repo, restoring tracked files in your working directory.
|
3. Restores tracked files in your working directory.
|
||||||
|
4. **Undoes the last conversation turn** so the agent's context matches the restored filesystem state.
|
||||||
|
|
||||||
On success, Hermes responds with a short summary like:
|
On success:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
✅ Restored /path/to/project to a1b2c3d
|
✅ Restored to checkpoint 4270a8c5: before patch
|
||||||
Reason: auto: before apply_patch
|
A pre-rollback snapshot was saved automatically.
|
||||||
|
(^_^)b Undid 4 message(s). Removed: "Now update test.py to ..."
|
||||||
|
4 message(s) remaining in history.
|
||||||
|
Chat turn undone to match restored file state.
|
||||||
```
|
```
|
||||||
|
|
||||||
If something goes wrong (missing commit, git error), you will see a clear error message and details will be logged.
|
The conversation undo ensures the agent doesn't "remember" changes that have been rolled back, avoiding confusion on the next turn.
|
||||||
|
|
||||||
|
## Single-File Restore
|
||||||
|
|
||||||
|
Restore just one file from a checkpoint without affecting the rest of the directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
/rollback 1 src/broken_file.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful when the agent made changes to multiple files but only one needs to be reverted.
|
||||||
|
|
||||||
## Safety and Performance Guards
|
## Safety and Performance Guards
|
||||||
|
|
||||||
To keep checkpointing safe and fast, Hermes applies several guardrails:
|
To keep checkpointing safe and fast, Hermes applies several guardrails:
|
||||||
|
|
||||||
- **Git availability**
|
- **Git availability** — if `git` is not found on `PATH`, checkpoints are transparently disabled.
|
||||||
- If `git` is not found on `PATH`, checkpoints are transparently disabled.
|
- **Directory scope** — Hermes skips overly broad directories (root `/`, home `$HOME`).
|
||||||
- A debug log entry is emitted, but your session continues normally.
|
- **Repository size** — directories with more than 50,000 files are skipped to avoid slow git operations.
|
||||||
- **Directory scope**
|
- **No‑change snapshots** — if there are no changes since the last snapshot, the checkpoint is skipped.
|
||||||
- Hermes skips overly broad directories such as:
|
- **Non‑fatal errors** — all errors inside the Checkpoint Manager are logged at debug level; your tools continue to run.
|
||||||
- Root (`/`)
|
|
||||||
- Your home directory (`$HOME`)
|
|
||||||
- This prevents accidental snapshots of your entire filesystem.
|
|
||||||
- **Repository size**
|
|
||||||
- Before committing, Hermes performs a quick file count.
|
|
||||||
- If the directory has more than a configured threshold (e.g. `50,000` files),
|
|
||||||
checkpoints are skipped to avoid large git operations.
|
|
||||||
- **No‑change snapshots**
|
|
||||||
- If there are no changes since the last snapshot, the checkpoint is skipped
|
|
||||||
instead of committing an empty diff.
|
|
||||||
|
|
||||||
All errors inside the Checkpoint Manager are treated as **non‑fatal**: they are logged at debug level and your tools continue to run.
|
|
||||||
|
|
||||||
## Where Checkpoints Live
|
## Where Checkpoints Live
|
||||||
|
|
||||||
By default, all shadow repos live under:
|
All shadow repos live under:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
~/.hermes/checkpoints/
|
~/.hermes/checkpoints/
|
||||||
|
|
@ -160,21 +185,19 @@ By default, all shadow repos live under:
|
||||||
└── ...
|
└── ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Each `<hash>` is derived from the absolute path of the working directory. Inside each shadow repo you will find:
|
Each `<hash>` is derived from the absolute path of the working directory. Inside each shadow repo you'll find:
|
||||||
|
|
||||||
- Standard git internals (`HEAD`, `refs/`, `objects/`)
|
- Standard git internals (`HEAD`, `refs/`, `objects/`)
|
||||||
- An `info/exclude` file containing a curated ignore list
|
- An `info/exclude` file containing a curated ignore list
|
||||||
- A `HERMES_WORKDIR` file pointing back to the original project root
|
- A `HERMES_WORKDIR` file pointing back to the original project root
|
||||||
|
|
||||||
You normally never need to touch these manually; they are documented here so advanced users understand how the safety net works.
|
You normally never need to touch these manually.
|
||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
- **Keep checkpoints enabled** for interactive development and refactors.
|
- **Leave checkpoints enabled** — they're on by default and have zero cost when no files are modified.
|
||||||
- **Use `/rollback` instead of `git reset`** when you want to undo agent‑driven changes only.
|
- **Use `/rollback diff` before restoring** — preview what will change to pick the right checkpoint.
|
||||||
- **Combine with Git branches and worktrees** for maximum safety:
|
- **Use `/rollback` instead of `git reset`** when you want to undo agent-driven changes only.
|
||||||
- Keep each Hermes session in its own worktree/branch.
|
- **Combine with Git worktrees** for maximum safety — keep each Hermes session in its own worktree/branch, with checkpoints as an extra layer.
|
||||||
- Let checkpoints act as an extra layer of protection on top.
|
|
||||||
|
|
||||||
For running multiple agents in parallel on the same repo without interfering with each other, see the dedicated guide on [Git worktrees](./git-worktrees.md).
|
|
||||||
|
|
||||||
|
For running multiple agents in parallel on the same repo, see the guide on [Git worktrees](./git-worktrees.md).
|
||||||
|
|
|
||||||
|
|
@ -1,97 +1,30 @@
|
||||||
# Filesystem Checkpoints
|
# Filesystem Checkpoints
|
||||||
|
|
||||||
Hermes can automatically snapshot your working directory before making file changes, giving you a safety net to roll back if something goes wrong.
|
Hermes automatically snapshots your working directory before making file changes, giving you a safety net to roll back if something goes wrong. Checkpoints are **enabled by default**.
|
||||||
|
|
||||||
## How It Works
|
## Quick Reference
|
||||||
|
|
||||||
When enabled, Hermes takes a **one-time snapshot** at the start of each conversation turn before the first file-modifying operation (`write_file` or `patch`). This creates a point-in-time backup you can restore to at any time.
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/rollback` | List all checkpoints with change stats |
|
||||||
|
| `/rollback <N>` | Restore to checkpoint N (also undoes last chat turn) |
|
||||||
|
| `/rollback diff <N>` | Preview diff between checkpoint N and current state |
|
||||||
|
| `/rollback <N> <file>` | Restore a single file from checkpoint N |
|
||||||
|
|
||||||
Under the hood, checkpoints use a **shadow git repository** stored at `~/.hermes/checkpoints/`. This is completely separate from your project's git — no `.git` directory is created in your project, and your own git history is never touched.
|
## What Triggers Checkpoints
|
||||||
|
|
||||||
## Enabling Checkpoints
|
- **File tools** — `write_file` and `patch`
|
||||||
|
- **Destructive terminal commands** — `rm`, `mv`, `sed -i`, output redirects (`>`), `git reset`/`clean`
|
||||||
|
|
||||||
### Per-session (CLI flag)
|
## Configuration
|
||||||
|
|
||||||
```bash
|
|
||||||
hermes --checkpoints
|
|
||||||
```
|
|
||||||
|
|
||||||
### Permanently (config.yaml)
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ~/.hermes/config.yaml
|
# ~/.hermes/config.yaml
|
||||||
checkpoints:
|
checkpoints:
|
||||||
enabled: true
|
enabled: true # default: true
|
||||||
max_snapshots: 50 # max checkpoints per directory (default: 50)
|
max_snapshots: 50 # max checkpoints per directory
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rolling Back
|
## Learn More
|
||||||
|
|
||||||
Use the `/rollback` slash command:
|
For the full guide — how shadow repos work, diff previews, file-level restore, conversation undo, safety guards, and best practices — see **[Checkpoints and /rollback](../checkpoints-and-rollback.md)**.
|
||||||
|
|
||||||
```
|
|
||||||
/rollback # List all available checkpoints
|
|
||||||
/rollback 1 # Restore to checkpoint #1 (most recent)
|
|
||||||
/rollback 3 # Restore to checkpoint #3 (further back)
|
|
||||||
/rollback abc1234 # Restore by git commit hash
|
|
||||||
```
|
|
||||||
|
|
||||||
Example output:
|
|
||||||
|
|
||||||
```
|
|
||||||
📸 Checkpoints for /home/user/project:
|
|
||||||
|
|
||||||
1. abc1234 2026-03-10 14:22 before write_file
|
|
||||||
2. def5678 2026-03-10 14:15 before patch
|
|
||||||
3. ghi9012 2026-03-10 14:08 before write_file
|
|
||||||
|
|
||||||
Use /rollback <number> to restore, e.g. /rollback 1
|
|
||||||
```
|
|
||||||
|
|
||||||
When you restore, Hermes automatically takes a **pre-rollback snapshot** first — so you can always undo your undo.
|
|
||||||
|
|
||||||
## What Gets Checkpointed
|
|
||||||
|
|
||||||
Checkpoints capture the entire working directory (the project root), excluding common large/sensitive patterns:
|
|
||||||
|
|
||||||
- `node_modules/`, `dist/`, `build/`
|
|
||||||
- `.env`, `.env.*`
|
|
||||||
- `__pycache__/`, `*.pyc`
|
|
||||||
- `.venv/`, `venv/`
|
|
||||||
- `.git/`
|
|
||||||
- `.DS_Store`, `*.log`
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
Checkpoints are designed to be lightweight:
|
|
||||||
|
|
||||||
- **Once per turn** — only the first file operation triggers a snapshot, not every write
|
|
||||||
- **Skips large directories** — directories with >50,000 files are skipped automatically
|
|
||||||
- **Skips when nothing changed** — if no files were modified since the last checkpoint, no commit is created
|
|
||||||
- **Non-blocking** — if a checkpoint fails for any reason, the file operation proceeds normally
|
|
||||||
|
|
||||||
## How It Determines the Project Root
|
|
||||||
|
|
||||||
When you write to a file like `src/components/Button.tsx`, Hermes walks up the directory tree looking for project markers (`.git`, `pyproject.toml`, `package.json`, `Cargo.toml`, etc.) to find the project root. This ensures the entire project is checkpointed, not just the file's parent directory.
|
|
||||||
|
|
||||||
## Platforms
|
|
||||||
|
|
||||||
Checkpoints work on both:
|
|
||||||
- **CLI** — uses your current working directory
|
|
||||||
- **Gateway** (Telegram, Discord, etc.) — uses `MESSAGING_CWD`
|
|
||||||
|
|
||||||
The `/rollback` command is available on all platforms.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
**Does this conflict with my project's git?**
|
|
||||||
No. Checkpoints use a completely separate shadow git repository via `GIT_DIR` environment variables. Your project's `.git/` is never touched.
|
|
||||||
|
|
||||||
**How much disk space do checkpoints use?**
|
|
||||||
Git is very efficient at storing diffs. For most projects, checkpoint data is negligible. Old checkpoints are pruned when `max_snapshots` is exceeded.
|
|
||||||
|
|
||||||
**Can I checkpoint without git installed?**
|
|
||||||
No — git must be available on your PATH. If it's not installed, checkpoints silently disable.
|
|
||||||
|
|
||||||
**Can I roll back across sessions?**
|
|
||||||
Yes! Checkpoints persist in `~/.hermes/checkpoints/` and survive across sessions. You can roll back to a checkpoint from yesterday.
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue