Skip to content

fix: resolve active blocks via their rendered summary after prune (billion-context-pi#195) - #119

Merged
ranxianglei merged 1 commit into
masterfrom
2026-08-22_promote-after-prune
Aug 22, 2026
Merged

fix: resolve active blocks via their rendered summary after prune (billion-context-pi#195)#119
ranxianglei merged 1 commit into
masterfrom
2026-08-22_promote-after-prune

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Problem

compress(startId:"b2", endId:"b2") fails when processTurn() has already replaced the block's raw messages with the synthetic acp_summary_b2 summary message (i.e. every turn after prune):

Requested range(s) already compressed (e.g. b2..b2); remaining compressible content 0 chars < min 5000. Nothing to do. Current active blocks span b2..b2 — retry with startId/endId set to active block IDs in that span.

The retry it suggests is exactly what failed — an unresolvable loop. Active T1 blocks could never be promoted to T2 in a live session. Reported in ranxianglei/billion-context-pi#195. (Replaces #118, which was closed after the branch rename.)

Root cause: resolveAnchorIndex() resolved an active bN ref only via earliestIndexOfIds(block.effectiveMessageIds, indexByRawId). Post-prune, those raw ids are absent from the visible list and only acp_summary_bN is present, so the resolver returned null and misclassified the block as "consumed". A secondary leak: ranges spanning a pruned region put the synthetic id into ResolvedRange.messageIds, which then persisted into new blocks' effectiveMessageIds (e.g. ["raw-1","acp_summary_b2","raw-5"]).

Fix

  • prune.ts: export summaryMessageId(blockId) / isSummaryMessageId(id); re-pruning an already-pruned view keeps the summary at its existing position (no jump to index 0) and dedupes stale copies.
  • boundaries.ts: new exported visibleBlockAnchor(block, indexByMessageId) = summary index ?? earliest raw id. Used at every block-anchor site: active start/end boundary resolution, nested-block discovery in resolveBoundaries(), and the adjusted-range rescan in applySingleRange(). Synthetic ids are excluded from ResolvedRange.messageIds so they never enter durable coverage.
  • activeOwnerAnchor: now snaps only for true ancestors (active block that inherited the content by consuming another block). If the active block directly compressed the message, callers keep the "already compressed — retry with bN" guidance instead of silently creating a same-tier duplicate block. This also makes the fix(compress): Bug 39 — protected tool messages excluded from effectiveMessageIds #32 snap work post-prune (owner visible via its summary).
  • sync.ts: a block whose only visible representation is its rendered summary stays active (hosts passing pruned views no longer lose block activity each turn).
  • Unresolvable-active-block error message now says so explicitly instead of claiming higher-tier consumption.

Verification

  • New tests/regression-promote-after-prune.test.ts (6 cases): single T1→T2 promotion post-prune (issue's exact fixture), two-T1→T2, inclusive b1..b3 selection consuming all three blocks incl. middle block visible only via summary, pre-prune control, syncBlocks on pruned view, prune idempotency/stable position. All fail on pre-fix code, pass on this branch.
  • Full suite: 428/428 pass; tsc --noEmit clean.
  • Downstream pre-validation: built this branch, overlaid dist onto billion-context-pi's node_modules → bcp typecheck + 409/409 tests + tsup build all pass.

No API removals; three new exports (summaryMessageId, isSummaryMessageId, visibleBlockAnchor) added for hosts/tests.

…llion-context-pi#195)

An active block whose raw messages were replaced by the synthetic
acp_summary_bN message became unresolvable: resolveAnchorIndex only
looked up effectiveMessageIds by raw id, so compress(bN..bN) failed with
a misleading 'already compressed / 0 chars < min' error and the block
could never be promoted.

- add summaryMessageId/isSummaryMessageId helpers (prune.ts)
- add visibleBlockAnchor(block, indexByMessageId): summary index ??
  earliest raw id; use at every block-anchor site (active boundary
  resolution, nested discovery, adjusted-range rescan, owner lookup)
- exclude synthetic acp_summary_* ids from ResolvedRange.messageIds so
  they never leak into effectiveMessageIds/directMessageIds
- activeOwnerAnchor: snap only for true ancestors (inherited content);
  directly-compressed content keeps the 'already compressed, use bN'
  guidance instead of a silent same-tier duplicate
- syncBlocks: a block whose only visible representation is its rendered
  summary stays active (hosts passing pruned views)
- prune: stable summary position + dedupe when re-pruning an
  already-pruned view

Adds tests/regression-promote-after-prune.test.ts (6 cases incl. the
issue's single-block, two-block, and inclusive bN..bM selection specs).
@ranxianglei
ranxianglei force-pushed the 2026-08-22_promote-after-prune branch from e708043 to 8a4dc72 Compare August 22, 2026 12:03
@ranxianglei
ranxianglei merged commit 50bfa0b into master Aug 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant