This repository was archived by the owner on Nov 5, 2026. It is now read-only.
Repository navigation
Never let a reconcile empty a live sandbox slice - #62
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Sandbox session slices in
sandbox-sessions.jsonhave been silently lost more than once. Today 62 sessions vanished from the TUI at once, leaving two registered sandboxes with no key in the registry at all.Two very different callers share one wholesale writer:
forget_session— a deliberate close. Emptying the slice is a decision.hook_state.rs:389) — passes whatever a live scan returned. Emptying the slice is a measurement.A scan returns empty for reasons that are not "every session ended": the process-table read failed, the pointer files were already gone, the hook fired during teardown.
replace_sandbox_slicecould not tell those apart from a real close, so one bad reading deleted the key — and the key is what--restore-sbx-sessionsreads, so the sessions became unrecoverable through the normal path.The module already defends against a degraded reading —
backfill_missing_identityrestores session titles a partial scan dropped. It cannot help here, because a fully empty reading leaves no entry to backfill into.Change
EmptySlice::{Freeze, Remove}makes the caller state which kind of empty it means.replace_sandbox_slicedefaults toFreeze: an empty reading against a non-empty slice is a no-op.forget_sessionopts intoRemove, so a deliberate close of the last session still drops the key.The safe behaviour is the default, so a future caller has to ask for deletion rather than get it by accident.
Tests
a_reconcile_that_measures_zero_never_empties_a_live_slice— new, covers the failure directly.a_deliberate_close_of_the_last_session_still_removes_the_sandbox— the oldreplace_sandbox_slice_empty_removes_the_sandbox, retargeted at the explicit path it was really describing.replace_sandbox_slice_keeps_sandboxes_independentused empty-removal incidentally while testing independence; switched to the explicit path so it keeps testing what it names.cargo test --lib sandbox_registry42 passed,session_lifecycle_tests7 passed,cargo clippy --all-targets -- -D warningsclean.Recovery for the sessions already lost
Not part of this change, but for the record: the 62 sessions were rebuilt into the registry from
~/.local/share/claudectl/state/*.json(entries whoselast_session_end_ts_msis 0 never emittedSessionEnd) cross-referenced against surviving transcripts under~/.claude/projects/.restore_sliceonly needssession_idandcwd, so the missing pids were not a blocker.