Skip to content

fix: separate confirmed overflow evidence from weak truncation guesses in window self-heal (#570) - #572

Merged
ranxianglei merged 1 commit into
masterfrom
2026-09-06_weak-overflow-self-correction
Sep 7, 2026
Merged

ranxianglei merged 1 commit into
masterfrom
2026-09-06_weak-overflow-self-correction

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Problem (#570)

The weak-overflow heuristic (3 high-usage stream truncations in 15 min) and the strong path (a real non-2xx overflow rejection) both wrote into the same per-model learnedContextLimits. Any mid-stream death — llama.cpp shared-KV exhaustion (failed to find free space in the KV cache), OOM, network reset — looked like an overflow, so one misjudged session permanently shrank the model's window for every later session, across restarts, with no correction path: the only upward self-heal is gated on fallback windows, never on the authoritative configured value.

Fix (three parts)

  1. Provenance split. Strong evidence (an actual non-2xx overflow rejection, incl. the payload-size upper bound) now lands in metadata.confirmedContextLimits / confirmedContextLimit; the weak heuristic keeps writing only to learnedContextLimits / learnedContextLimit. Resolution order is confirmed > speculative, so a weak guess can no longer clobber a confirmed window.

  2. Retraction. A successful turn whose upstream-reported input exceeds a learned window beyond a margin (max(256 tokens, 3%)) proves it stale — KV-pressure false positive, resized server, … — and removes it before this request's config is resolved. True overflows cannot be contradicted (a turn cannot succeed above the real window); the armed emergency value (== the learned value exactly) sits below the margin and never retracts itself.

  3. Ground truth for llama.cpp. inspectContextOverflow now recognizes exceed_context_size_error and parses the real limit from the (N / M > W) body shape, so a genuine overflow learns W instead of letting the weak path guess.

Both compression modes considered: plugin mode (agent-owned compression; proxy suppresses injection) and proxy mode — the changed paths are upstream-facing (error inspection, per-request limit resolution, session metadata) and identical on both; no wire-format change.

Pre-flight

  • npm run typecheck — clean
  • npm test — 1056/1057 pass; the single failure (launcher.test.ts: resolveClientCommand codex/claude resolve to themselves) is pre-existing on clean master in this environment (sandbox stub at /usr/bin/codex makes the resolver return an absolute path) and unrelated to this change
  • npm run build — success
  • New unit coverage: provenance precedence (confirmed governs; weak can't clobber), retraction (stale removed, margin respected, armed no-op, confirmed retracts too, other models untouched), llama.cpp body parsing → window 150528

Note: the real-client E2E suite (tests/e2e/, needs ACP_TEST_E2E=1 + live Responses upstream) was not runnable in this sandbox; the local e2e-style self-heal tests (tests/e2e-overflow-selfheal.test.ts, issue-393, issue496) all pass.

@github-actions

github-actions Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-09-06_weak-overflow-self-correction (35d2809)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-572

Each push to this PR publishes a new version under the pr-572 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr572.tgz
npm install -g package

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 Review complete — preflight independently verified, one defect found in review and fixed on the branch.

Preflight (all three re-run by me):

  • npm run typecheck — clean ✓
  • npm test — 1056/1057; the single failure (launcher.test.ts: resolveClientCommand codex/claude resolve to themselves) reproduces identically on clean master in this environment, so it's environmental, not from this change ✓
  • npm run build — success ✓

Design review: provenance split, the retraction margin (max(256 tokens, 3%)), and the llama.cpp ground-truth parsing all check out against the code. One deliberate divergence worth noting (not a bug): handle() resolves limits in order cPM→cS→sPM→sS while resolveLearnedLimit uses cPM→sPM→cS→sS — the former drives the provenance log label, the latter drives noteWeakOverflow's usage-% denominator where the tighter bound is preferred. Harmless; left as-is.

Defect found in review (fixed): retraction reads session.stats.lastInputTokens, which is a high-water mark that failed turns also write — noteWeakOverflow arms it at the failing input size, and forward()'s overflow floor takes max(prev, floor). Consequence: three mid-stream deaths at 185k input (KV-pressure pattern) → mark at 185k; then a genuine parseable overflow learns W=150528 into confirmedContextLimits; on the next request, retraction sees 185000 − 150528 ≥ margin and deletes the just-learned ground truth, logging "a later turn succeeded at 185000" when nothing succeeded. That contradicts the PR's own stated invariant ("True overflows cannot be contradicted") — and a second variant (kills above an already-governing confirmed window) deletes a correct value. Impact is narrow and transient (one wasted overflow cycle + misleading log pair), so non-blocking — but it undermines the guarantee this PR exists to provide, so I fixed it on the branch:

  1. forward(): when the overflow body parses a window, arm at exactly it — no turn can succeed above the real window, so any higher armed value provably came from a failed turn; unparsed cases keep the max-floor.
  2. noteWeakOverflow(): while a confirmed window governs, cap the armed value at it — a mid-stream death above the real window is a non-window kill mode (KV pressure / OOM / reset), not evidence about the window.
  3. Regression coverage: unit tests for both caps; a new e2e rig (three 370k KV-pressure deaths + a parsed 128k overflow, asserting the confirmed window survives the next request's retraction check — fails without the fix); tightened the existing armed-value assertion to exact equality.

Re-ran everything after the fix: typecheck clean, npm test 1058/1059 (same sole pre-existing failure), build success. Commit pushed to the PR branch. Merge remains yours: #572

ranxianglei pushed a commit that referenced this pull request Sep 6, 2026
Semantic merge onto current master: master's #554 regex superset kept in
util.ts (exceed[_\s]?context[_\s]?size); the raw-body fallback for
side-passthrough payload estimates kept, now writing through the
confirmed-channel naming; import union with backfillHostUsage (#591).
Stale #554 test assertion updated to the confirmed channel (#572).
@ranxianglei
ranxianglei force-pushed the 2026-09-06_weak-overflow-self-correction branch from 779a314 to ee79b4c Compare September 6, 2026 16:24
Semantic merge onto current master: master's #554 regex superset kept in
util.ts (exceed[_\s]?context[_\s]?size); the raw-body fallback for
side-passthrough payload estimates kept, now writing through the
confirmed-channel naming; import union with backfillHostUsage (#591).
Stale #554 test assertion updated to the confirmed channel (#572).
@ranxianglei
ranxianglei force-pushed the 2026-09-06_weak-overflow-self-correction branch from ee79b4c to 35d2809 Compare September 6, 2026 16:25
@ranxianglei

Copy link
Copy Markdown
Owner Author

已在 35d2809 将本 PR 语义合并到当前 master 并重写为线性单提交(修 #575 同样的 diff-爆炸问题)。全量 1146/1146 通过、typecheck 0 错、build 成功。语义合并要点:#570 weak-overflow:util.ts 取 master #554 的超集正则;server 的保守估计保留 raw-body 兜底但走 confirmed 通道命名;side-request guard 改读 resolveLearnedLimit(confirmed→speculative),老 #554 测试断言对齐 confirmed 通道。CI 绿后请人工合并。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 Verified the rewritten branch independently:

  • Linearity: 35d2809 is a single linear commit on current master (parent 3f57ce4) ✓
  • Guard survival: the review-fix (exact arm at parsed window in forward(), armInput cap in noteWeakOverflow(), plus both regression tests incl. the e2e rig) is present in the semantic merge ✓
  • Preflight (fresh npm ci — note the base now carries acp-kernel 0.0.54, stale deps initially surfaced two unrelated acp_status 会计缺陷:tool 结果全计为 text + 同一报告两套互斥的 tool 定义 + 双估计器混印不标尺 + pct 地板 #386 CJK failures that cleared after install): typecheck clean ✓, full suite 1152/1153 — sole failure is the environmental launcher.test.ts: resolveClientCommand codex/claude sandbox-stub issue that reproduces identically on clean master here ✓, build success ✓

Everything checks out. On the merge itself: I can't merge PRs — system rules forbid it. Please merge yourself: #572

@ranxianglei
ranxianglei merged commit 56d0484 into master Sep 7, 2026
6 checks passed
antclt pushed a commit to antclt/billion-context that referenced this pull request Sep 19, 2026
The window is a deployment property, owned by declarations (config /
registry / runtime-info), never adjusted from session traffic. The
learned-window channel (confirmedContextLimit/confirmedContextLimits,
retraction, weak-overflow arming) accumulated patch-on-patch fixes
(ranxianglei#570, ranxianglei#572, ranxianglei#857, ranxianglei#969) and still mis-sized windows in the field
(ranxianglei#987: a session window silently re-centered to 16898).

Removed:
- src/weak-overflow.ts (armFailureShrink/armedAtLeast ladder) and its
  tests; an upstream rejection now arms the one-shot emergency shrink
  directly at the stated window, or min(declared, payload-estimate)
  when no number is parseable (source=usage).
- The self-heal application ladder in server.ts (learnedLimit /
  operatorOwnsWindow / nativeFromFallback learn paths) — the declared
  window resolves once and keeps governing.
- confirmedContextLimit(s) session metadata fields.
- sideRequestGuard's learnedLimit param; the guard now intersects the
  declared window with the usage-grounded arm only.

Kept: the one-shot emergency shrink (per ranxianglei#969/ranxianglei#987 decisions), ranxianglei#496
forward-once gate (now keyed on the rejection arm), ranxianglei#857 usage-grade
provenance, ranxianglei#570 no-retraction guarantees (trivially — nothing to
retract). Tests rewritten to the new semantics; exit-matrix updated.
@ranxianglei
ranxianglei deleted the 2026-09-06_weak-overflow-self-correction branch October 15, 2026 05:32
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