Desktop app renders kept messages twice after a `session.compact` hook returns `{ messages }`; copies grow with every compaction
Environment
- Claude desktop app (Code tab), Windows 11 Home 10.0.26200
- Claude Code 2.1.280 (SDK process started by the app)
- Plugin with a
session.compacthook that returns{ messages }, i.e. the kept messages with their enginehandle, no summary message (fast-jev-compaction 0.3.0). A companion plugin queues/compactvia$.command.run.
What happens
After each compaction, the engine appends the kept messages to the session .jsonl as new rows after the compact_boundary: new uuid, same timestamp, same message.id. The desktop app renders the whole file, so every kept user and assistant message appears once more after each compaction. After N compactions a message can show up N+1 times.
The model context is fine; only the transcript file and the UI are affected.
Evidence (from local transcripts, 23.09.2026)
- One session with 3 compactions (2247, 710 and 405 ms): 127 user/assistant messages duplicated, up to 4 copies. For example, the same user prompt appears at rows 909 and 1122 with identical
timestamp2026-09-23T20:32:11.613Z, and the assistant reply at rows 928 and 1129 with the samemessage.idmsg_011CfM1P4pFjdhHUJHVQ6SY3. - In long sessions the copies compound: up to 63 copies of one message, and
compact_boundaryrows get duplicated as well, so the file grows fast. - 25 sessions from the last 4 days: every session compacted by the hook has duplicates. The one session compacted only by the built-in summarizer (22968 ms,
isCompactSummary) has 0.
Expected
Kept messages returned with their engine handle should be relinked or preserved, as with the built-in compaction's preserved segment, and not copied as new rows. Or the UI should not render messages that are before the latest boundary and also kept after it.
Repro
- Install a plugin with
on('session.compact', async ($, e) => ({ messages: e.messages.slice(-20) })). - In the desktop app, run a conversation and then
/compact. - The last 20 messages are shown twice. Run
/compactagain and they are shown three times.