anthropics / anthropics/claude-code
A session.compact function hook's compaction is undone when the session is resumed
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
**Version:** Claude Code 2.1.276, macOS arm64, `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1`
**What happens.** A plugin answers `session.compact` with `{ messages }`, handing most
messages back unchanged (with the engine's `handle`) and leaving some out. The running
process is compacted. After `claude --resume ` the dropped messages are back: the
first API call carried 311,812 input tokens on a session compacted from 309,936, and
the model could quote a tool call the hook had dropped.
**Why (from the transcript and the resume loader).**
1. A kept `tool_result` record is re-persisted after the boundary with a new `uuid`, but
its `sourceToolAssistantUUID` — written as `parentUuid` — still names the *old*
assistant record behind the boundary. The parent walk from the newest record leaves
the boundary behind and returns the whole history.
2. A kept assistant record keeps its `message.id`. The loader's parallel-tool-result
recovery gathers every record in the file sharing a chain record's `message.id`, so
the pre-boundary originals — dropped `tool_use` records and their results included —
are recovered. A core compaction avoids this because records before the boundary that
are not in `preservedSegment` are discarded; a hook's boundary has no such list.
**Also.** `compact_boundary.compactMetadata.postTokens` (51,140) and the `/context`
figure after a resume do not reflect the context actually sent (kept assistant records
carry their old `usage`; core zeroes it for a preserved segment).
**Expected.** Records a hook keeps are re-parented and re-identified like a preserved
segment, or records before a hook's boundary are discarded on load.
**Workaround I use.** Hand kept `tool_result` messages and older assistant
messages back without their `handle`;
a small script that walks `parentUuid` and `message.id` across the boundary confirms both causes on the transcript.
Function hooks are experimental and undocumented, so this may be out of scope — filing it in case the resume loader change is cheap. Reproduced headless with `claude -p "/compact" --resume --fork-session` followed by `claude --resume` of the fork.
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce the issue with the documented headless /compact and --resume commands, then inspect the resume loader and the transcript records across the compact boundary. Trace parentUuid, message.id, preservedSegment, and compactMetadata through resume; done means dropped records stay absent and postTokens matches the context actually sent.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100