5 MB precompact-skip threshold strips pre-boundary content from chain walker, hides scrollback

Resolved 💬 3 comments Opened May 3, 2026 by ojura Closed May 3, 2026

Affected: anthropic.claude-code 2.1.126 (Antigravity / VSCode extension). The bug is in the
shared sessionStorage loader and applies to any consumer that calls loadSessionFile or its
equivalent — including the VSCode chat panel, list rendering, and --resume rendering for files
≥ 5 MB.

Symptom

Sessions whose JSONL exceeds 5 MB lose all pre-most-recent-compact-boundary visibility in
both the VSCode chat panel and the CLI --resume render. Scrolling back stops at the most
recent compact_boundary system message regardless of whether the chain walker would otherwise
follow it (i.e., independent of the [#48937 secondary] / [#46603] / Patch-D-style
logicalParentUuid fallbacks).

The pre-boundary content is on disk and well-formed — it's just never parsed into the messages
array that the chain walker sees.

Root cause

src/utils/sessionStoragePortable.ts:480 defines:

export const SKIP_PRECOMPACT_THRESHOLD = 5 * 1024 * 1024

Then in src/utils/sessionStorage.ts:3536-3556:

if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_PRECOMPACT_SKIP)) {
  const { size } = await stat(filePath)
  if (size > SKIP_PRECOMPACT_THRESHOLD) {
    const scan = await readTranscriptForLoad(filePath, size)
    buf = scan.postBoundaryBuf      // ← drops ALL pre-boundary bytes
    hasPreservedSegment = scan.hasPreservedSegment
    if (scan.boundaryStartOffset > 0) {
      metadataLines = await scanPreBoundaryMetadata(filePath, scan.boundaryStartOffset)
    }
  }
}
buf ??= await readFile(filePath)

When size > 5 MB, the loader returns only the post-most-recent-compact_boundary portion
of the file. Pre-boundary bytes never reach the JSONL parser, so the resulting messages array
that downstream consumers (chain walker, fork chooser, rewind list, render) operate on
contains only post-boundary entries. Pre-boundary metadata is recovered as a separate
metadata-only pass for things like agent-settings, but the actual user/assistant chain
messages are lost.

The justification in the source (paraphrased from comments at sessionStorage.ts:3525-3531):

Large sessions (>5 MB) almost always have compact boundaries — they got big because of many turns triggering auto-compact. Pre-boundary metadata is recovered via a cheap byte-level scan.

This reasoning holds for API context constructiongetMessagesAfterCompactBoundary
(src/utils/messages.ts:4643)
already slices to post-boundary independently, so the API path is bounded regardless of what
the loader returns.

It does not hold for any consumer that wants pre-boundary visibility:

  • Chain walker (buildConversationChain-equivalent) can't reach pre-boundary messages even

with parentUuid → logicalParentUuid fallback applied — those messages aren't in the map.

  • Rewind UI / fork-from-message picker can only target post-boundary messages.
  • Scrollback in the VSCode chat panel stops at the boundary (the topmost rendered message is

the isCompactSummary user message).

  • --resume's terminal scrollback (which the source claims preserves full history per

comment at messages.ts:4636-4639) is not actually full when size > 5 MB.

Reproduction

  1. Take any session file > 5 MB on disk that has at least one compact_boundary system

message.

  1. Open it in VSCode/Antigravity. Scroll up.
  2. Observe: scrollback stops at the most recent compact summary. Prior turns (the bulk of

the session) are unreachable.

  1. Set CLAUDE_CODE_DISABLE_PRECOMPACT_SKIP=1 in the environment, restart VSCode.
  2. Same session now scrolls back through the full chain.

Concrete observation

Verified empirically on an Antigravity install with extension 2.1.126:

  • Session 9b5cd3cc-... (49 MB JSONL, 10355 lines)
  • getSession() IPC call from the webview's loadFromServer returned 27 messages

(post-most-recent-compactSummary slice)

  • After patching the bundled equivalent of the K > Hz4 && !envTruthy(...) check to always

bypass the optimization, same call returned 1857 messages (the full in-file chain
back to the most recent stitch with cross-file logicalParentUuid).

  • Cache_read on the next API turn stayed bounded at ~30 K tokens (not the ~300 K the full

pre-boundary content would imply) — confirming getMessagesAfterCompactBoundary does its
own boundary-aware slicing on the way to the API and is unaffected.

Empirically verified the API-context guard is independent of what the loader returns. The
optimization confuses two concerns:

  1. Bounding API context — already handled correctly downstream by

getMessagesAfterCompactBoundary.

  1. Bounding parse cost — the actual purpose of SKIP_PRECOMPACT_THRESHOLD, but at the

cost of breaking everything that wants pre-boundary visibility.

Suggested fix

Two reasonable directions:

  • Move the optimization to consumers that actually need bounded output, not to the

loader. Loader returns full file; getMessagesAfterCompactBoundary slices for API
consumers; chain walker / rewind list / render consumers operate on the full set and
apply their own caps if they need them.

  • Or: keep the loader optimization but split into two return paths — the truncated

buffer for API construction, and a full-file path that the chat-panel render and
chain-walking consumers go through. Add a flag to opt into the full-file path.

The cheapest workaround for end users is CLAUDE_CODE_DISABLE_PRECOMPACT_SKIP=1 in the
launch environment. That should arguably be the default for any IDE-hosted use case (where
the chat panel is the primary surface and full visibility matters more than a small parse
speedup).

_The leak gave me the loader's SKIP_PRECOMPACT_THRESHOLD constant and its justifying comment at sessionStoragePortable.ts:480 — enough to name the optimization as the cause rather than speculating from the symptom. Without it, this would have been "scrollback breaks on long sessions" with no path from the symptom to the specific 5MB threshold that gates the truncation. Trace through the loader and empirical verification on a live Antigravity install done by an instance of Claude Opus 4.7, working with the user._

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗