Skip to content

fix(compaction): Ignore stale usage in replacement estimates - #2648

Merged
thomasjiangcy merged 1 commit into
mainfrom
fix/compaction-replacement-estimate
Nov 4, 2026
Merged

thomasjiangcy merged 1 commit into
mainfrom
fix/compaction-replacement-estimate

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Over the last 24h, 58 of 128 prepared-summary swaps that had a ready summary were thrown away as over_target. Every discard carried 2 or more messages after the mark.

The cause is estimateHistoryTokens → pi estimateContextTokens. It takes the provider usage on the last assistant message as its base. A carried assistant message still holds the usage of its own model call, and that call saw the full history from before the swap. So the replacement scored at about the old size (~309k in one case, against a 288k target) no matter how short the summary was. That conversation then discarded the same summary on each turn for ~2h, until an active compaction ran inside the turn (~28s).

Changes:

  • Replacement estimates (in the swap and in the normal capacity compaction) now add up pi's per-message estimateTokens and ignore usage. The pre-swap estimate still uses usage, because that history really is that big.
  • Replacement estimates now also count the system prompt and tool definitions (estimateRequestOverheadTokens, chars/4 over the prompt plus { name, description, parameters } of each tool). The usage-based trigger already counts them, so without this the check compared a full-request number with a history-only number.

The chars/4 heuristic itself stays as it is. Against prod usage it undercounts by ~1.6× (and leaves out ~36k of overhead before this change). That makes the target check lenient, but it's safe: the next model call reports real usage, and the normal compaction still runs at the threshold.

via @thomasjiangcy.

--

View Session [Sentry]

@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Nov 4, 2026 7:40am UTC

Request Review

@sentry-junior

sentry-junior Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

check / types and test / junior unit failed here because of a break on main, not this PR: web-batch-messages.test.ts imported webActorFromEmail, which #2630 removed (TS2305). #2643 fixed it on main as e35103a2d. Rebase onto main to clear those checks. ci / required failed only because those two did.

A carried assistant message holds provider usage for the whole request
before the swap. The replacement estimate took that usage as its base, so
prepared summaries that carried 2+ messages were scored at the old size
and discarded as over_target.

Estimate each replacement message by its own size, and add the system
prompt and tool definitions, which the usage-based trigger already counts.

Co-Authored-By: Thomas Jiang <20501235+thomasjiangcy@users.noreply.github.com>
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

6 screenshot changes — 6 changed · 0 added · 0 removed

Review screenshots in Frameshift

Conversation Attachment · Desktop
Conversation Attachment · Desktop
Changed
Conversation Attachment · Mobile
Conversation Attachment · Mobile
Changed
Conversation Attachment Modal · Desktop
Conversation Attachment Modal · Desktop
Changed
Conversation Share Details · Desktop
Conversation Share Details · Desktop
Changed
Conversation Subagents · Desktop
Conversation Subagents · Desktop
Changed
Settings Mcp · Desktop
Settings Mcp · Desktop
Changed

@thomasjiangcy
thomasjiangcy merged commit 5f0f85d into main Nov 4, 2026
46 checks passed
@thomasjiangcy
thomasjiangcy deleted the fix/compaction-replacement-estimate branch November 4, 2026 07:46
thomasjiangcy added a commit that referenced this pull request Nov 5, 2026
…2743)

A background summary was requested only after the agent loop ended, so a
single long Turn that crossed the compaction threshold mid-run never had
one ready. Since #2648 shipped, prod traces show no `over_target`
discards. The inline compactions that remain are `active` ones (~20s
inside the Turn), and mid-Turn swaps almost never apply.

The request now runs in `prepareNextTurnWithContext`, after every model
call. That covers the final reply too, so the Turn-end call is removed.
Pi waits for `turn_end` listeners before this hook, so the tool results
are already committed when the mark is taken, and the mark lands on a
safe cut after a tool result. The request is skipped when the hook just
replaced history, because the last usage then counts the old history.

The hook runs before every model call, so the check above the mark now
reads only the newest `compaction_requested`/`compaction_prepared` fact
first. It loads the full history only when no request is pending and no
summary became ready in this slice. `loadLatestBackgroundCompaction`
moves from the swap module into `background-compaction.ts`, so both use
one copy.

The mid-Turn swap stays at the threshold. Swapping as soon as a summary
is ready would break the warm prompt cache in the middle of a Turn.

**Test coverage gap:** I verified the hook wiring with a real-agent
integration test (Slack harness, scripted model stream). It fails on
main and passes here. I did not commit it, because
`test-architecture:check` does not allow tests in
`packages/junior/tests` to run the agent. An integration eval would have
to tune the context window to real token counts, and I couldn't run
evals in this environment. Only the request rule is covered: a pending
request blocks another one. Decide whether you want an eval before
merge.

<!-- junior-request-attribution:start -->
via @thomasjiangcy.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0CCUMX7ET0%3A1793877933.460399 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0CCUMX7ET0%3A1793877933.460399)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0CCUMX7ET0%3A1793877933.460399/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Thomas Jiang <20501235+thomasjiangcy@users.noreply.github.com>
Co-authored-by: Thomas Jiang <thomas.jiang@sentry.io>

This branch was successfully deployed

1 active deployment
Preview – junior-docs — be59d285 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant