Repository navigation
fix(shim): trust a Claude session manifest only if it's live (MUS-107) - #198
Merged
Merged
Conversation
Claude leaves ~/.claude/sessions/<pid>.json behind when it dies without cleaning up, so once the pid is reused an unrelated process's shim or waiter would take a dead session's id. The manifest is now trusted only if it names that pid, a process holds the pid, and on Linux its procStart equals the process's start ticks (/proc/<pid>/stat field 22); elsewhere, or without procStart, the process must not have started after the manifest's startedAt (2 s slack). Discovery's tier 0 and canonical_session_id (MUS-104's waiter, the drift check) both apply it. Whatever can't be read is trusted, as before. Story: MUS-107 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`ps -o lstart` prints local time with no offset, and it was parsed as UTC. With MUS-107's wall-clock fallback, a Mac east of UTC saw a live Claude as starting hours after its manifest's startedAt and refused the manifest. `ps` now runs with TZ=UTC; the parse is split out (parse_lstart_utc) and tested, and a macOS-only test checks this process's start time is close to now. Story: MUS-107 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its day and month names follow LC_TIME, so a non-English locale failed the parse (trusted, so it failed open). ps now runs with LC_ALL=C beside TZ=UTC. Story: MUS-107 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
MUS-107 fixes the root cause behind the ghost rows that MUS-104's waiter ran into. robust-warbler filed it and handed it over.
The bug: both the shim and the waiter take
sessionIdfrom~/.claude/sessions/<pid>.json. Claude leaves that file behind when it dies without cleaning up. Once the pid is reused, an unrelated process's shim or waiter would pick up a dead session's id.The fix:
manifest_is_liveincrates/shim/src/session_discovery.rstrusts a manifest only when all of these hold:procStartequals field 22 of/proc/<pid>/stat, the process's start time in clock ticks; Claude records exactly that value, and I checked it against a live session on MurphprocStart, the process didn't start after the manifest'sstartedAt, allowing 2 s of slack; macOS checks that a process holds the pid withkill(pid, 0)Two readers apply it:
canonical_session_id, which MUS-104's waiter (superseded) and the shim's drift check readAnything that can't be read is trusted, so an older Claude's manifest, or a platform that can't report these things, behaves as it did before. A stale manifest is skipped with a warning, and discovery falls through to the other tiers.
The process probe is threaded through
resolve_under, the existing test seam. The real callers passreal_liveness. Existing tests use made-up pids, so they passunprobed, which is what the behaviour was before this change.Tests:
procStartdiffers, is refused/procstat with a comm that contains parenthesesLocal results: fmt,
clippy --workspace --all-targets -D warningsandcargo test --workspaceare clean. I can't cross-compile here, so the macOS and Windows paths are covered by CI only.🤖 Generated with Claude Code