Compact fork-resume silently retires the session id — live children and external tooling keep the dead ancestor id, no signal announces the new one
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Auto-compact (daemon path) relaunches the conversation as a new session id via fork-resume, but nothing running from before the compact ever learns this:
- Pre-compact-spawned long-lived children (MCP servers, background processes) keep
CLAUDE_CODE_SESSION_ID=<old>forever. - The interactive terminal client process keeps the old id in its env, so external tooling that resolves "which session is in this window" via env/proc inspection binds the dead ancestor.
- The statusline inside the same terminal shows the new id — the UI and the env contradict each other.
- The fork's
SessionStarthook fires inside the daemonbg-pty-host, which has no terminal env ($KITTY_WINDOW_ID,$WINDOWID), so hooks that record terminal context at session start silently record nothing for the post-compact session. - Both
.jsonlfiles continue to exist; the ancestor looks like a live-but-stalled session to any transcript-watching tool.
Impact: session-id-keyed tooling (statusline scripts, window/session binders, telemetry) mislabels the window with the pre-compact session and treats the ancestor as live indefinitely. MCP servers spawned pre-compact report against a dead session id.
The docs still describe /compact as replacing history within a session (https://code.claude.com/docs/en/sessions), and PreCompact semantics imply in-place; the fork-resume relaunch is undocumented.
What Should Happen?
Any of these would fix the class:
- Document the contract: compact may mint a new session id via fork-resume;
<old>.jsonlbecomes an ancestor. - Emit a positive signal on fork-resume — fire
SessionEnd(or aSessionForkedevent carryingold_session_id→new_session_id) for the ancestor, and/or aSessionStartwithsource: "compact"distinct from"fork". (An existing constituency for lifecycle signals: #47023.) - Refresh
CLAUDE_CODE_SESSION_IDwhere feasible (the client process), and document that pre-fork children hold a stale value.
Error Messages/Logs
# Observed process tree (v2.1.216, Linux) after an auto-compact:
claude --resume "<session name>" # terminal client, env: CLAUDE_CODE_SESSION_ID=<OLD> claude daemon run --origin transient --spawned-by …
claude bg-pty-host --bg-pty-host /tmp/cc-daemon-<uid>/<hash>/pty/<NEW8>.sock … -- ~/.local/share/claude/versions/2.1.216 --session-id <NEW> --fork-session --resume ~/.claude/projects/<slug>/<OLD>.jsonl --reply-on-resume …
# Live env divergence at the same moment:
# fresh Bash-tool subshell: CLAUDE_CODE_SESSION_ID=<NEW> (correct)
# pre-compact MCP server processes: CLAUDE_CODE_SESSION_ID=<OLD> (stale, permanent)
# terminal client process environ: CLAUDE_CODE_SESSION_ID=<OLD> (stale)
# statusline JSON session_id: <NEW>
Steps to Reproduce
- Start an interactive session; note
$CLAUDE_CODE_SESSION_IDfrom a Bash tool call. Any MCP stdio server counts as a long-lived pre-compact child automatically. - Drive the session to auto-compact (or trigger the daemon compact path).
- Compare: statusline session id (new) vs `tr '0' '
' < /proc/<client-pid>/environ | grep CLAUDE_CODE_SESSION_ID` (old) vs the MCP server's env (old).
ps aux | grep fork-session: the conversation now runs as--session-id <NEW> --fork-session --resume <OLD>.jsonl— and no hook/event announced the transition.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
~2.1.138 ?
Claude Code Version
2.1.216
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
Searched before filing — this exact defect is unreported. Closest:
- #54508 (closed not_planned by the inactivity bot, never acknowledged): "session ID not preserved" across
/compact, seen via the TUI task panel emptying — the same root cause, different symptom. This report is the second independent sighting, with the mechanism identified (--fork-sessiondaemon relaunch) and/procevidence. - #68534 (open): stale inherited
CLAUDE_CODE_SESSION_IDat launch → silent transcript loss. Same silent-staleness class, different trigger. A commenter there independently observed the same detection signal: env id ≠ hook-payloadsession_id. - #77306 (open):
--fork-session//branchforfeit the prompt cache — corroborates the fork lifecycle minting new ids. - #59415 (closed): compact fork vs
/resumepicker; #78956 (open): SessionStartforksource undocumented.