fix(minimald): lead the shell-exit prompt with the files changed since activation - #1123
Conversation
…e activation The keep-or-delete prompt asked users to decide without saying what a delete would lose. The host now baselines the workspace before the session process launches and the prompt lists the delta: added, modified, and deleted files (capped at 10 rows), or states that nothing changed — in which case the delete option says so. Change detection is best-effort: an unwalkable workspace or a failed re-walk renders the plain prompt and never blocks exit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyZLpkRf9G4A2hUDgDvn5f
📝 WalkthroughWalkthroughThe session host captures a workspace baseline before launch. At shell exit, it detects added, modified, and deleted files and displays the results in the exit prompt. Tests cover snapshot behavior, symlinks, content changes, and session integration. ChangesWorkspace delta reporting
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant Host
participant DeltaSource
participant Binding
participant ExitPrompt
Host->>DeltaSource: Capture workspace baseline
Host->>Binding: Provide shared delta source
Binding->>DeltaSource: Read changes after shell exit
DeltaSource-->>Binding: Return sorted changed paths
Binding->>ExitPrompt: Display changes and action options
Possibly related issues
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/minimald/src/session_delta.rs`:
- Around line 15-19: Update the Sig type and its file-change detection usage to
include an exact content digest, ensuring equal-length edits with preserved
modification times are detected before reporting no files changed or no data was
lost. Compute and compare the digest for each file, preserve deletion detection,
and add a regression test covering same-length content changes with unchanged
mtime.
- Around line 52-60: Bound the blocking filesystem operations in
SessionDelta::changed_files and DeltaSource::arm with the established async
timeout policy, while keeping traversal inside spawn_blocking. Return None or
the existing fallback result when the timeout expires, and preserve normal
results when the task completes before the deadline.
- Around line 72-77: Update the directory traversal loop in the relevant
session-delta walker to inspect each entry’s own file type instead of following
links: only push genuine directories onto the stack, and use symlink metadata
when recording non-directory entries. Add tests covering a directory symlink and
a symlink cycle, verifying traversal stays within the root and terminates.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3f7e7393-4399-4f57-96f7-4116169fb350
📒 Files selected for processing (4)
crates/minimald/src/lib.rscrates/minimald/src/session.rscrates/minimald/src/session_delta.rscrates/minimald/src/session_host.rs
|
Unless there's a reason not to, I would strongly recommend using either the |
… exit walk Review follow-ups: the walk moves to walkdir (no symlink following, no hand-rolled recursion), file signatures gain a size-capped blake3 content digest so same-length mtime-preserved edits cannot masquerade as "no files changed", and the exit-time re-walk is bounded by a timeout that degrades to the plain prompt instead of blocking teardown. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyZLpkRf9G4A2hUDgDvn5f
There was a problem hiding this comment.
🧹 Nitpick comments (2)
crates/minimald/src/session_delta.rs (2)
99-141: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider excluding build/VCS directories from the walk.
The prior review comment about following directory symlinks is resolved:
walkdir's defaultfollow_links(false)meansentry.file_type().is_dir()at Line 109 never descends into a symlinked directory, andentry.metadata()at Line 114 returns the symlink's own metadata rather than the target's. Verified both behaviors against thewalkdirAPI docs.One practical concern remains.
WalkDir::new(root)at Line 107 has no entry filtering. For a typical Rust or JS workspace, this walks (and, for files underDIGEST_CAP_BYTES, hashes) every entry undertarget/,.git/, ornode_modules/as well. Those directories can hold tens of thousands of files, which risks exceedingWALK_TIMEOUT(5 seconds) on exactly the active development workspaces this feature targets — degrading it to "detection disabled" most of the time.Filter out common heavy, non-source directories (or switch to the
ignorecrate, as suggested in review), so the timeout stays meaningful for real workspaces.♻️ Example: skip known heavy directories during the walk
fn snapshot(root: &Path) -> std::io::Result<Snapshot> { let mut out = Snapshot::new(); - for entry in WalkDir::new(root) { + for entry in WalkDir::new(root).into_iter().filter_entry(|e| { + e.depth() == 0 + || !matches!(e.file_name().to_str(), Some(".git" | "target" | "node_modules")) + }) { let entry = entry?;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/minimald/src/session_delta.rs` around lines 99 - 141, Update snapshot to prune common heavy non-source directories such as target, .git, and node_modules while walking, using WalkDir’s directory filtering/pruning mechanism so their descendants are never visited or hashed. Preserve existing symlink-safe traversal and snapshot behavior for all remaining entries.
64-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLog timeout and error paths for diagnosability.
armandchanged_filesboth resolve the two prior review comments about bounding the walk withWALK_TIMEOUT. Based on learnings, this correctly keeps the filesystem walk on the blocking pool and bounds the.await.One gap remains: on timeout, I/O error, or panic, both methods silently return
None. Nothing signals which case occurred. If a workspace mount wedges in production, this becomes hard to diagnose from logs alone.Add a
tracing::warn!(ordebug!) call at eachNone-producing path, noting the cause (timeout vs. I/O error vs. panic).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/minimald/src/session_delta.rs` around lines 64 - 97, Update DeltaSource::arm and DeltaSource::changed_files to log each failure before returning None, distinguishing timeout, snapshot I/O errors, and spawned-task panics. Preserve the existing timeout and Option-based control flow while adding tracing::warn! or debug! messages with the relevant workspace context and error details where available.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/minimald/src/session_delta.rs`:
- Around line 99-141: Update snapshot to prune common heavy non-source
directories such as target, .git, and node_modules while walking, using
WalkDir’s directory filtering/pruning mechanism so their descendants are never
visited or hashed. Preserve existing symlink-safe traversal and snapshot
behavior for all remaining entries.
- Around line 64-97: Update DeltaSource::arm and DeltaSource::changed_files to
log each failure before returning None, distinguishing timeout, snapshot I/O
errors, and spawned-task panics. Preserve the existing timeout and Option-based
control flow while adding tracing::warn! or debug! messages with the relevant
workspace context and error details where available.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bc95bcb8-8bc8-4f57-8299-be5491fa9d2c
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (2)
crates/minimald/Cargo.tomlcrates/minimald/src/session_delta.rs
| // Lead with what a "delete" would lose. An unavailable delta (no | ||
| // baseline, or the re-walk failed) renders the plain prompt — the | ||
| // exit path never blocks on change detection. | ||
| let changed = match &self.delta { |
There was a problem hiding this comment.
Nit: move the bulk of this to a helper function, the handler here is getting long
| /// would misreport their contents as added later. | ||
| fn snapshot(root: &Path) -> std::io::Result<Snapshot> { | ||
| let mut out = Snapshot::new(); | ||
| for entry in WalkDir::new(root) { |
There was a problem hiding this comment.
A nice optimization here might be to skip a .git in the root
|
Review follow-ups landed in 3767383 (and merged into feat/exit-prompt-archive as 4cb51be):
|
….git in the delta walk Review follow-ups: Binding::run's ProcessExited epilogue moves into a shell_exit_prompt associated fn (byte-identical channel output), and both workspace walks now skip the root-level .git directory — the delta reports working-tree files, and git-internal churn (index, refs, objects) would otherwise flood the capped row list the moment any git command runs in the session. Nested .git directories (vendored subrepos) remain ordinary workspace content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyZLpkRf9G4A2hUDgDvn5f
|
Thanks @twitchyliquid64 — both addressed in 90ba487 (merged forward to feat/exit-prompt-archive as d6400ee):
|
Exiting a session asks keep-or-delete without saying what would be lost; the prompt now leads with the files changed since activation and the delete option says when nothing would be lost.
N files changed since activation:with up to 10A/M/D <path>rows, orNo files changed since activation.(delete option gains— nothing will be lost).🤖 Generated with Claude Code
https://claude.ai/code/session_01KyZLpkRf9G4A2hUDgDvn5f
Note
Lead the minimald shell-exit prompt with files changed since activation
session_deltamodule that snapshots a session workspace at activation time usingwalkdirandblake3, then diffs it on shell exit to produce sorted A/M/D rows.session_hostnow shows up to 10 changed-file rows with A/M/D markers; if no files changed, the Delete option appends '— nothing will be lost'.deltais left asNoneand the prompt falls back to the plain (no-diff) display.Changes since #1123 opened
snapshotfunction insession_deltamodule to exclude the root-level .git directory from baseline and re-walk operations by adding afilter_entrythat rejects entries where depth equals 1 and file_name equals ".git", while preserving nested .git directories as ordinary content [90ba487]Binding.runmethod withinsession_hostmodule by extracting inline prompt code into a new generic async associated functionBinding.shell_exit_promptthat computes changed files viaDeltaSource, renders either a no-changes banner or a capped list of changed files, and presents a two-item selection menu for Exit or Delete actions [90ba487]Macroscope summarized 3767383.
Summary by CodeRabbit
New Features
Bug Fixes