Skip to content

fix(shim): trust a Claude session manifest only if it's live (MUS-107) - #198

Merged
Castor-o7 merged 6 commits into
mainfrom
mus107/live-manifest
Nov 7, 2026
Merged

Castor-o7 merged 6 commits into
mainfrom
mus107/live-manifest

Conversation

@Castor-o7

Copy link
Copy Markdown
Owner

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 sessionId from ~/.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_live in crates/shim/src/session_discovery.rs trusts a manifest only when all of these hold:

  • it names that pid
  • a process holds the pid
  • on Linux, the manifest's procStart equals 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 Murph
  • off Linux, or when there's no procStart, the process didn't start after the manifest's startedAt, allowing 2 s of slack; macOS checks that a process holds the pid with kill(pid, 0)

Two readers apply it:

  • discovery's tier 0
  • canonical_session_id, which MUS-104's waiter (superseded) and the shim's drift check read

Anything 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 pass real_liveness. Existing tests use made-up pids, so they pass unprobed, which is what the behaviour was before this change.

Tests:

  • a live manifest is trusted
  • a reused pid, where procStart differs, is refused
  • a pid with no process is refused
  • an unreadable start time is trusted
  • a missing manifest returns nothing
  • the wall-clock fallback (a process younger than the session is stale; the slack holds; a manifest naming another pid is stale; an old manifest with neither field is trusted)
  • parsing /proc stat with a comm that contains parentheses
  • the real probe on this process

Local results: fmt, clippy --workspace --all-targets -D warnings and cargo test --workspace are clean. I can't cross-compile here, so the macOS and Windows paths are covered by CI only.

🤖 Generated with Claude Code

Castor-o7 and others added 6 commits November 6, 2026 21:54
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>
@Castor-o7
Castor-o7 merged commit be93b58 into main Nov 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant