feat(state): Hermes-style human rewind for shadow checkpoints - #98
Conversation
Expose Session.list_checkpoints / restore_checkpoint so operators can restore the worktree and truncate the matching transcript turns — the gap vs Hermes /rollback after auto pre-mutate snaps landed. Co-authored-by: Cursor <cursoragent@cursor.com>
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@greptileai please review |
Greptile SummaryThe PR adds default-on shadow checkpoints and an operator-facing combined filesystem/transcript rewind.
Confidence Score: 2/5The PR is not safe to merge until checkpoint session propagation, session ownership during restore, and cross-session worktree synchronization are corrected. PRE_TOOL_USE loses the session identifier and disables subsequent checkpoints, while shared checkpoint selection and unsynchronized restore operations can restore unrelated state or overwrite another active session's filesystem changes. Files Needing Attention: src/dream/state/shadow/_hook.py, src/dream/state/shadow/_manager.py, src/dream/session.py
|
| Filename | Overview |
|---|---|
| src/dream/state/shadow/_hook.py | The new session-scoped deduplication is not propagated through PRE_TOOL_USE, causing checkpoints to stop after the first conclusive snapshot. |
| src/dream/state/shadow/_manager.py | Adds size probing and combined restore, but shared worktree operations and checkpoint ownership remain unsynchronized across sessions. |
| src/dream/session.py | Adds prompt-boundary tracking and rewind APIs, but default restore selection and the active-send guard are only locally session-aware. |
| src/dream/_factory.py | Correctly wires the feature by default, while making the manager and worktree shared across all harness sessions. |
Sequence Diagram
sequenceDiagram
participant A as Session A
participant B as Session B
participant H as Shared checkpoint hook
participant M as Shared manager
participant W as Shared worktree
A->>H: USER_PROMPT_SUBMIT(session A)
H->>M: begin_turn("A")
A->>H: PRE_TOOL_USE(no session_id)
H->>M: "ensure(session_id=None)"
M->>W: Create checkpoint
B->>H: USER_PROMPT_SUBMIT(session B)
H->>M: begin_turn("B")
B->>H: PRE_TOOL_USE(no session_id)
H->>M: "ensure(session_id=None)"
M-->>B: ALREADY_THIS_TURN
A->>M: restore newest worktree checkpoint
M->>W: Reset shared worktree
Note over A,B: Only A's transcript is rewound
Prompt To Fix All With AI
### Issue 1
src/dream/state/shadow/_hook.py:53-57
**Session dedup key is lost**
`PRE_TOOL_USE` payloads do not contain `session_id`, so this call passes `None` and all sessions share a deduplication set that real-session `begin_turn` calls never clear. After the first conclusive checkpoint, subsequent mutations return `ALREADY_THIS_TURN` and no longer receive recoverable pre-mutation checkpoints.
### Issue 2
src/dream/session.py:231-237
**Restore selects cross-session checkpoints**
When two sessions on the harness create interleaved checkpoints, `list_for` returns their shared working-directory history and this branch selects the globally newest snapshot. The filesystem can therefore be restored to the other session's state while only the calling session's transcript is rewound, leaving chat and disk unrelated.
### Issue 3
src/dream/session.py:207-208
**Restore lacks worktree synchronization**
If another session is executing a mutating tool in the same harness worktree, this per-session `_active` check still permits a restore. Because the shared manager has no worktree-level lock, the reset can overwrite or interleave with that session's writes, leaving its transcript inconsistent with the resulting filesystem.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "Skip checkpoints for oversized worktrees" | Re-trigger Greptile
| self._manager.ensure( | ||
| self._working_dir, | ||
| reason=reason, | ||
| session_id=self._session_id(payload), | ||
| ) |
There was a problem hiding this comment.
PRE_TOOL_USE payloads do not contain session_id, so this call passes None and all sessions share a deduplication set that real-session begin_turn calls never clear. After the first conclusive checkpoint, subsequent mutations return ALREADY_THIS_TURN and no longer receive recoverable pre-mutation checkpoints.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/dream/state/shadow/_hook.py
Line: 53-57
Comment:
**Session dedup key is lost**
`PRE_TOOL_USE` payloads do not contain `session_id`, so this call passes `None` and all sessions share a deduplication set that real-session `begin_turn` calls never clear. After the first conclusive checkpoint, subsequent mutations return `ALREADY_THIS_TURN` and no longer receive recoverable pre-mutation checkpoints.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| listed = manager.list_for(engine.working_dir) | ||
| if not listed: | ||
| return CombinedRestoreResult( | ||
| fs=RestoreResult( | ||
| outcome=RestoreOutcome.NOT_FOUND, | ||
| detail="no checkpoints for working directory", | ||
| ), |
There was a problem hiding this comment.
Restore selects cross-session checkpoints
When two sessions on the harness create interleaved checkpoints, list_for returns their shared working-directory history and this branch selects the globally newest snapshot. The filesystem can therefore be restored to the other session's state while only the calling session's transcript is rewound, leaving chat and disk unrelated.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/dream/session.py
Line: 231-237
Comment:
**Restore selects cross-session checkpoints**
When two sessions on the harness create interleaved checkpoints, `list_for` returns their shared working-directory history and this branch selects the globally newest snapshot. The filesystem can therefore be restored to the other session's state while only the calling session's transcript is rewound, leaving chat and disk unrelated.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| ) -> CombinedRestoreResult: | ||
| """Hermes-style human rewind: restore FS and truncate the transcript. |
There was a problem hiding this comment.
Restore lacks worktree synchronization
If another session is executing a mutating tool in the same harness worktree, this per-session _active check still permits a restore. Because the shared manager has no worktree-level lock, the reset can overwrite or interleave with that session's writes, leaving its transcript inconsistent with the resulting filesystem.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/dream/session.py
Line: 207-208
Comment:
**Restore lacks worktree synchronization**
If another session is executing a mutating tool in the same harness worktree, this per-session `_active` check still permits a restore. Because the shared manager has no worktree-level lock, the reset can overwrite or interleave with that session's writes, leaving its transcript inconsistent with the resulting filesystem.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Restack operator rewind onto current main so Session.list_checkpoints / restore_checkpoint restore the worktree and truncate Session-owned prompt turns, with build_harness auto-wiring the shared shadow store. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the reviewed current-main implementation while joining the original PR branch without rewriting remote history.
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
There was a problem hiding this comment.
divo12 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
User description
Summary
Operator rewind that restores the worktree and truncates the matching transcript turns (Hermes
/rollback), closing the Bex-vs-Hermes gap left after shadow FS snaps.build_harnessauto-wiresShadowCheckpointHook+ a shared store underDreamPaths.checkpoints_dir;Session.list_checkpoints/Session.restore_checkpointare the human surface, not an LLM tool.Turn boundaries come from the
Session, not from message inspection. Transcripts contain engine-generated user messages (compaction summaries, attachments, delegation completions, STOP nudges), so classifying "which user message is a human turn" is unreliable — insteadSessionrecords the transcript index at each real prompt submit:Compaction rewrites the transcript and invalidates those boundaries, so it clears them and a later rewind returns a failed result (
requested rewind boundary is unavailable) rather than truncating at a guessed position. FS restore is validated first, so a failed snap never desyncs chat from disk, andrewind_turns < 0is rejected before any filesystem mutation.Checkpoints are default-on, with the per-turn cost bounded. Measured here: the first mutating tool call costs ~0.02 s at 1k files but ~0.31–0.38 s at 50k, and it is not incremental. So
ShadowCheckpointConfig.max_files(default 10,000) skips oversized worktrees with a distinctCheckpointOutcome.DIRECTORY_TOO_LARGEbefore the shadow store is touched; the probe usesgit ls-files -co --exclude-standard(boundedos.walkfallback for non-git dirs) and is cached per working dir so it runs once, not per call. Override viabuild_harness(..., shadow_checkpoint_config=...).Per-session state: the per-turn dedupe set and the checkpoint bookkeeping are keyed by
session_id(bounded to the 64 most recent sessions), so concurrent sessions sharing one manager can't clear each other's state.Type
Checklist
tests/test_public_api.pyupdated if the public API changedCHANGELOG.mdupdated for user-visible changes.env.local, or credentials in the diffdream(org/company features belong in sibling repos)Test plan
pytest tests/test_state/test_transcript_rewind.py tests/test_state/test_combined_restore.pypytest tests/test_session/test_checkpoint_rewind.py tests/test_state/test_shadow_checkpoint.py— 31 passedrewind_turnsleaves the FS untouched, missing boundary after compaction, oversized tree skips without touching the store, size probe runs once per diruv run ruff check src tests/uv run mypyLink to Devin session: https://app.devin.ai/sessions/4f7deb4af0914056a24a4d660a661713
Requested by: @divo12
CodeAnt-AI Description
Add safe operator rewind for worktrees and conversation history
What Changed
Impact
✅ Restore files and chat together after an unwanted change✅ Fewer checkpoint delays on large worktrees✅ Safer operator recovery without transcript desynchronization💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.