anthropics / anthropics/claude-code

A session.compact function hook's compaction is undone when the session is resumed

Open
#95,328 1 comment 0 reactions 0 assignees View on GitHub
area:core area:hooks bug has repro platform:macos
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.