fix(compaction): preserve thinking blocks during overflow - #1393
Conversation
|
Code Review — PR #1393 (fix: preserve thinking blocks during overflow) Reviewed against CLAUDE.md. Overall this is a careful, well-targeted fix for a high-severity issue (#1386): it addresses both prevention (the validation guard) and recovery (the rebuild guard for already-corrupted sessions), and the test coverage is genuinely thorough — whole-entry skips, sibling-block preservation, paired tool-result restoration, redacted-thinking parity, and the later-valid-filter provenance case are all exercised. Nice work. A few points below, mostly minor, with one design question worth a deliberate decision. Design question (worth confirming) Whole-entry deletion of thinking-bearing assistants is now rejected — stricter than the issue proposed, and may reduce overflow-recovery headroom. Issue #1386 explicitly said "Whole-entry deletion of such assistant messages can remain allowed … since that removes the message entirely rather than mutating its thinking blocks in place." The Anthropic invariant is about mutating the latest assistant turn in place; deleting an entire turn (latest or historical) does not violate it. This PR instead forbids whole-entry deletion of any thinking-bearing assistant entry (assertNoAssistantThinkingDeletionTargets, the in-loop entry-kind check, and the grep assistant_thinking_entry skip). Consequence: in a long interleaved-thinking session, every assistant turn carries thinking blocks, so during critical_overflow none of them are evictable at all. That removes a large, legitimately-reclaimable chunk of context from the LRU pass and could leave a genuine overflow unresolved (the very thing critical-overflow mode exists to fix). If the goal is only to guard the replay invariant, allowing whole-entry deletion while keeping the partial/content-block rejection would preserve more headroom. If the stricter behavior is intentional (the docs/changelog do state it), a one-line code comment explaining why whole-entry deletion is also forbidden would help future readers, since it contradicts the issue own analysis. Bugs / correctness
Style / maintainability
Scope notes (not blockers)
Tests Coverage is strong and tracks the suggested matrix closely. Two small additions would round it out:
Conventions Bun-only workflow, raw-TS (no build step), .js import specifiers, changelog under Unreleased/Fixed, and user-facing docs in docs/compaction.md are all correctly followed. The changelog/doc entries are accurate but very dense single run-on sentences — consider splitting for readability, though they satisfy the comprehensive-not-lazy rule. Solid, defensive fix overall. The main thing I would like confirmed is the deliberate choice to forbid whole-entry deletion of thinking-bearing assistants vs. the issue recommendation to allow it. |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
5761ab1 to
68b63ae
Compare
|
Updated per review/user clarification: critical_overflow may now evict old/non-latest thinking-bearing context, while deterministic validation/rebuild safeguards protect the latest retained thinking-bearing assistant message from content-block mutation. I also added a clear context_delete self-correction error for non-deletable latest thinking blocks.\n\nValidation rerun:\n- |
Review: fix(compaction): preserve thinking blocks during overflowThorough fix with strong regression coverage. The layered approach — prompt guidance, deterministic validation, grep-tool skips, and a rebuild-time repair ( Potential bug / behavioral inconsistency1. Content-block eviction of old (non-latest) thinking blocks is silently a no-op on rebuild. The PR's stated goal is that critical overflow may evict old non-latest thinking blocks. Validation now allows this ( But on rebuild, // session-manager.ts:526
case "assistant": {
if (hasAssistantThinkingContentBlock(message.content)) return message; // verbatim, ignores deletedBlocks
...
}Meanwhile If only whole-entry eviction is intended, then validation/grep should reject content-block deletions of thinking blocks even in critical overflow (or the rebuild guard should be scoped to the latest thinking assistant). Right now the validation surface and the rebuild surface disagree. The new tests cover whole-entry old-thinking deletion and validation acceptance, but none assert that an old thinking block is actually removed from the rebuilt content — so this gap is untested. 2. Two different definitions of "latest retained assistant".
These can pick different entries when a non-thinking assistant follows a thinking assistant. They happen to converge because the rebuild guard in #1 over-preserves, but the divergence is fragile. Worth aligning on one definition tied to the actual provider replay invariant. Edge case in the rebuild repair
Minor / nits
Things that look good
Overall solid; the main item to resolve is the validation-vs-rebuild mismatch in #1 (decide whether partial old-thinking-block eviction is supported, and make both layers agree), plus a quick look at the cross-compaction-entry restoration edge case. 🤖 Generated with Claude Code |
68b63ae to
05a04cd
Compare
|
Addressed the latest review feedback.\n\nChanges in the pushed update:\n- Rebuild now applies accepted old/non-latest thinking content-block deletions, so critical_overflow actually reclaims that context instead of no-oping.\n- The latest-retained-assistant definition is aligned between validation and rebuild: only the absolute latest retained assistant is protected when it contains thinking/redacted_thinking.\n- Paired tool results restored because of a skipped latest-assistant partial filter are protected from later stale whole-entry deletion, preventing dangling retained tool calls. Later valid content-block trimming of those result entries still works.\n- Added regression tests for old thinking block rebuild eviction and later stale whole-result deletion.\n\nValidation rerun:\n- |
Code Review —
|
05a04cd to
746010a
Compare
|
Addressed the latest review feedback.\n\nUpdates pushed:\n- Removed the redundant effective-filter recomputation in |
Review:
|
746010a to
effc375
Compare
|
Addressed the latest review feedback.\n\nUpdates pushed:\n- Corrected the PR description to state that latest-assistant protection is implemented by effective deletion-filter repair, not a |
Code Review — PR #1393:
|
Prevent critical-overflow compaction from deleting, partially filtering, or prompting removal of assistant thinking/redacted_thinking blocks. Persisted unsafe deletion filters are ignored during session rebuild while same-compaction paired tool results are restored and later valid result block filters remain effective. Add regression coverage for critical-overflow prompts, deletion validation, persisted filter repair, paired tool-result restoration, and future valid content-block deletions. Closes #1386 Assistant-model: GPT-5.5
effc375 to
88d68bd
Compare
Review:
|
Prevent critical-overflow compaction from deleting, partially filtering, or prompting removal of assistant thinking/redacted_thinking blocks. Persisted unsafe deletion filters are ignored during session rebuild while same-compaction paired tool results are restored and later valid result block filters remain effective. Add regression coverage for critical-overflow prompts, deletion validation, persisted filter repair, paired tool-result restoration, and future valid content-block deletions. Closes #1386 Assistant-model: GPT-5.5
Summary
Prevents critical-overflow context compaction from deleting, partially filtering, or instructing removal of assistant
thinking/redacted_thinkingcontent blocks. Also repairs unsafe persisted deletion filters during session rebuild so existing affected sessions recover without mutating Anthropic thinking-bearing assistant turns.Closes #1386.
Changes
New:
thinking-blocks.tsExtracted shared detection utilities:
isAssistantThinkingBlockType(type)— type guard for"thinking"/"redacted_thinking"contentArrayHasAssistantThinkingBlock(content)— scans a content arraymessageHasAssistantThinkingContentBlock(message)— checks a full messageCore compaction (
context-compaction.ts)assistantEntryHasThinkingContentBlockhelper to detect thinking-bearingCompactableTranscriptEntryvaluesassertNoAssistantThinkingDeletionTargets— throws if any reconciled deletion target touches an assistant entry containing thinking blocks (whole-entry or per-block); runs post-reconciliation in standard modeassertNoLatestRetainedThinkingAssistantContentBlockDeletionTargets— throws duringcritical_overflowif a content-block deletion targets the latest retained thinking-bearing assistant entryvalidateContextDeletionRequestto call both assertions after tool-dependency reconciliationcreateContextDeletionToolgrep path to skip (rather than match) assistant entries and blocks belonging to thinking-bearing entries, emitting"assistant_thinking_entry"/"assistant_thinking_block"skip reasonshas_assistant_thinking_blocks,role, andtypefields toStoredTranscriptEntry,StoredContentBlock, and their SQL row typescontextCompactionModePromptforcritical_overflow: removes the instruction to evict reasoning traces first; adds an explicit invariant prohibiting deletion of thinking-bearing assistant entries or their sibling blocksprepareContextCompactionto usebuildEffectiveContextDeletionFiltersinstead of rawbuildContextDeletionFiltersSession rebuild (
session-manager.ts)buildEffectiveContextDeletionFilters— replays all compaction entries and skips any deletion target whose assistant message carries a thinking blocktoolCallIDs and finds theirtoolResultentries so active context has no dangling tool callsbuildContextDeletionFilteredPathand the call inbranch-summarization.tswithbuildEffectiveContextDeletionFiltersso every session rebuild is safeTests
context-compaction-deletion-tool.test.ts— grep-path skips on thinking entries and blocks, skip reason assertionscontext-compaction.test.ts—validateContextDeletionRequestrejection of thinking-block targets (whole-entry and per-block), critical-overflow prompt wording invariantbuild-context.test.ts— persisted-filter repair: stale whole-entry deletion, stale block deletion, paired tool-result restoration, provenance-aware preservation of later valid block filters on restored tool resultsDocs & changelog
docs/compaction.md— extends the critical-overflow description to document the thinking-block verbatim-preservation invariant and the session-rebuild repair behaviorCHANGELOG.md—### Fixedentry under## [Unreleased]referencing Verbatim Compaction can corrupt the latest assistant message'sthinkingblocks #1386Breaking Changes
None. All changes are additive guard-rails and internal behavior fixes; no public API surfaces changed.
Validation
Pre-push/pre-commit hooks ran successfully (
bun run lint,bun run test:unit).