You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
During PR #641 wrap-up (running Skill("PACT:unwatch-inbox")), the skill's lead-session guard refused to call TaskStop on its own Monitor task because ~/.claude/pact-session-context.json contained stale fixture data:
The actual current session was pact-ec28101d (from CLAUDE.md Current Session block, from team config leadSessionId, from inbox file paths). The mismatch caused the unwatch-inbox guard to skip TaskStop on its own legitimate Monitor — leaving an orphan Monitor task running until session-end.
Fixture indicators:
session_id is fake UUID (aabb1122-0000-0000-0000-000000000000, not a real format)
project_dir is /Users/mj/Sites/test-project (not the actual /Users/mj/Sites/collab/PACT-prompt)
started_at is 2026-04-03 (over a month before the session that's reading it)
Symptom & impact
Skill guards refuse legitimate operations: any skill that uses pact-session-context.json for team_name/session_id validation (the unwatch-inbox skill, possibly others) will skip privileged operations on the actual current session.
Cross-session-LLM speculation footgun: an editing LLM seeing the mismatch might "fix" by bypassing the guard, which is exactly the failure mode the guard is designed to prevent against actual cross-session attack.
Audit confusion: any tooling reading pact-session-context.json for diagnostics gets stale data.
Likely cause
pact-session-context.json is a singleton file at the user's home directory. It is supposed to be updated authoritatively at session start (probably by session_init.py or similar). Either:
(a) The update path doesn't fire reliably — possibly conditional on certain hook paths that this session didn't traverse; OR
(b) The file is written once and never updated — fixture data from a test session leaks into all subsequent sessions.
Investigation needed
Audit session_init.py and any other writer for pact-session-context.json to determine the update path.
Confirm whether the file SHOULD be authoritatively updated on every session start, or whether it's intentionally singleton-static (and what the intended semantics are).
If the update path exists but is gated, identify the gating condition and why this session didn't traverse it.
If the update is missing, design and implement the update path.
Fires on every session start. Atomic-rename so concurrent reads don't see torn state.
Option B — per-session context (not singleton)
# Move pact-session-context.json into the session-specific directory:# ~/.claude/pact-sessions/{slug}/{session_id}/pact-session-context.json
Eliminates the singleton-staleness class entirely. Skills look up via pact_context.get_session_context() which knows the session_id. But this is invasive — many tools may rely on the singleton path.
Acceptance criteria
Root cause identified (where pact-session-context.json should be updated and why it isn't).
Update path implemented and tested.
Test asserts pact-session-context.json reflects current session's team_name + session_id after session_init fires.
Manual verification: a fresh session updates the file from any prior state.
Background
During PR #641 wrap-up (running
Skill("PACT:unwatch-inbox")), the skill's lead-session guard refused to callTaskStopon its own Monitor task because~/.claude/pact-session-context.jsoncontained stale fixture data:{ "team_name": "pact-aabb1122", "session_id": "aabb1122-0000-0000-0000-000000000000", "project_dir": "/Users/mj/Sites/test-project", "started_at": "2026-04-03T03:33:14.954845+00:00" }The actual current session was
pact-ec28101d(from CLAUDE.mdCurrent Sessionblock, from team configleadSessionId, from inbox file paths). The mismatch caused the unwatch-inbox guard to skip TaskStop on its own legitimate Monitor — leaving an orphan Monitor task running until session-end.Fixture indicators:
session_idis fake UUID (aabb1122-0000-0000-0000-000000000000, not a real format)project_diris/Users/mj/Sites/test-project(not the actual/Users/mj/Sites/collab/PACT-prompt)started_atis2026-04-03(over a month before the session that's reading it)Symptom & impact
pact-session-context.jsonforteam_name/session_idvalidation (the unwatch-inbox skill, possibly others) will skip privileged operations on the actual current session.pact-session-context.jsonfor diagnostics gets stale data.Likely cause
pact-session-context.jsonis a singleton file at the user's home directory. It is supposed to be updated authoritatively at session start (probably bysession_init.pyor similar). Either:(a) The update path doesn't fire reliably — possibly conditional on certain hook paths that this session didn't traverse; OR
(b) The file is written once and never updated — fixture data from a test session leaks into all subsequent sessions.
Investigation needed
session_init.pyand any other writer forpact-session-context.jsonto determine the update path.Proposal
Option A — authoritative update at session_init
Fires on every session start. Atomic-rename so concurrent reads don't see torn state.
Option B — per-session context (not singleton)
Eliminates the singleton-staleness class entirely. Skills look up via
pact_context.get_session_context()which knows the session_id. But this is invasive — many tools may rely on the singleton path.Acceptance criteria
team_name+session_idaftersession_initfires.Cross-references
Skill("PACT:unwatch-inbox")invocation.unwatch-inboxskill lead-session-guard logic.watch-inboxskill lead-session-guard logic (same guard, same reliance onpact-session-context.json).pact_context.pyaccessor.