Skip to content

[PR] fix: failed-only compress rounds end the turn — ephemeral failure frames never reach upstream (#4186) - #4217

Closed
ranxianglei wants to merge 3 commits into
masterfrom
2026-10-22_fix-4186-failed-fold-seam
Closed

ranxianglei wants to merge 3 commits into
masterfrom
2026-10-22_fix-4186-failed-fold-seam

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Closes #4186

查重: 无 — open+closed issues/PRs 按失败签名(cache-seam fuzz / seed 1592 / unsanctioned seam / mid-history divergence / I1 violation / failed-only round)检索,仅 #4186 本身命中;50 个在途 PR 无一触碰本机制(最接近的 #4139 是 marker-migrated tail append 分类、#4023 是 judge body cap,均不同机制)。

是什么

loop 内部的 compress/decompress 失败帧是临时的(#1548:只拼进当前重试请求,从不落 session、客户端从不可见)。但旧行为让这种临时帧真的到达上游一次(re-request),下一个客户端请求又不带它们 → provider 冻结前缀被中途改写 = unsanctioned cache seam。仓库自带 fuzz 在默认种子 1592 下确定性抓到它;owner 加扫确认 17 个种子里 7 个红(41%),签名全部相同。

修法(thread 里的 Option A)

commit 1 — src/loop/core.ts(主修)

commit 2 — src/invariant-judge.ts(同族告警,诊断面)
Issue 第二层证据(I1/I2 warn,5/12 场景)是同一家族的判官侧缺陷——豁免清单漏了两种合法变换:

  • G2 CCR-stored tool results:出站是 canonical placeholder(📦 [acp-stored …]),入站还是全文。用 kernel 的严格整体解析器 parseStoredPlaceholder(bug(ccr): embedded [acp-stored text is misclassified as a canonical placeholder #1456 纪律,不做子串扫描)识别,方向性豁免(只有 tool-result↔tool-result 且 b 是合法 placeholder 才放行)。
  • G3 emergency-truncation twins:kernel truncate 把旧消息裁成 prefix+marker 行+suffix。方向性结构证明:b 含 marker 行、剥 render tag 后 pre/post 非空且 na.startsWith(pre) && na.endsWith(post)、严格更短——伪造/膨胀体仍会被告警。
  • 两者计数进 count-bounded 豁免账本(exempt.ccr / exempt.truncate,warn/debug 日志行同步扩展)。纯诊断:零流量影响。

行为变更披露(旧值 → 新值 + 原因)

  1. turn 终止语义(commit 1):旧 = 仅当模型重交字节相同的已失败参数时提前结束(小模型(2B级)compress 循环空转:同参数重复失败烧满 10 轮 + 幻觉 ref + 摘要长度约束不遵守 #156);新 = 任何 failed-only mutating round 第一轮即结束(finish_reason=length + ❌ receipt)。原因:失败帧一旦到过一次上游,下一客户端请求必然断缝(41% 种子命中率);"进了就保持稳定直到 fold 顶替"(Option B)需要把临时帧写进 session 持久层,触碰 identity/persistence 边界,代价与风险都更高。这是本 PR 唯一的行为变更,按 thread 中推荐先行落地 Option A——若 owner 拍板要 B,关此 PR 即可。
  2. judge 日志格式(commit 2):warn/debug 行尾部追加 ccr=N truncate=N;JudgeVerdict.exempt 增加两个字段(additive)。仅日志/诊断面。

测试

测试套件波及(commit 3)

CI 首轮暴露 15 个文件 24 处失败——全部是把「失败的 compress」当 re-request 载体的测试,或直接钉住旧 internal-continuation 行为的断言(Option A 的合法波及)。commit 3 按三类适配:

复核:全部 19 个触碰文件本地定向跑绿(单元 107/107 + e2e 39/39);typecheck clean;fuzz 11/11 种子 × 12 场景绿、0 judge 违规。全量判定以本次 push 的 CI 为准。

另:JSON lane(compressLoopResponsesJson)单独审过、不需要 gate——其 re-request 只重发 raw client input + append 的 tail items,失败帧结构上骑不上去,seam 不存在(该 lane 的既有测试保持原样通过)。

并发热点提示

#4023(invariant judge 4MiB body cap)也改 src/invariant-judge.ts + 其测试(纯增量 +26/+47)。若 #4023 先合,本 PR 需一次小 rebase;两处改动区域不重叠(wrapper vs stableEqual/ledger),union 即可。

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-22_fix-4186-failed-fold-seam (d2c1086)

Option A — Install this exact build (recommended)

npm install -g billion-context@0.0.5015-pr.4217.6715fa7

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Option B — Download artifact

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

Install instructions are refreshed on each push.

The new breaker (a failed-only mutating round completes the turn
instead of re-requesting) invalidated 24 tests across 15 files that
used a failing compress as their re-request vehicle or pinned the old
internal-continuation behavior. Three adaptation classes:

- contract rewrites (old behavior gone by design): issue1502 salvage
  (no re-request after unsalvageable garbage args), issue3661 forms
  1/2 (exercised on salvage-SUCCESSFUL rounds so the placeholder rides
  a real re-request; raw-malformed-skeleton absence pinned), issue1195
  #2777 (the accepted overflow fold is now asserted to survive into
  the NEXT turn's rebuild), ccr-v2 unarmed opt-in (refusal receipt
  asserted on client SSE), loop-compress #422b (no fold refresh, no
  re-request).
- vehicle swaps to acp_status (the test pins a re-request property,
  not compress itself): loop-core #5, fix-stream abort tests,
  loop-compress #3/#6/#10, strict-echo-model-criterion,
  anthropic-thinking-replay x3, upstream-truncation,
  loop-strict-echo (+responses), visibility-markers streaming pair.
- made genuinely successful (the test needs a landed fold): e2e
  anthropic / responses-chat-relay / proxy-smoke round trips (unique
  session ids + protected-zone tail mass), subagent-namespace
  (positional refs + tail mass + honest usage claims — its mock had
  always failed via hash-boundary refs and only passed on master
  through retry-after-failure).
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

查重: 独立复核——按失败签名(cache-seam fuzz / seed 1592 / unsanctioned seam / failed-only round / invariant-judge CCR/truncation 豁免 / #4186)在 open+closed issue/PR 检索,除本 PR 与源 issue #4186 外无同机制条目;作者声明的「50 个在途 PR 无一触碰本机制」与我的检索一致。查重结论:不重复。

标签: bug scope/wiring p2(缺陷修复 PR;主修在 src/loop 请求管道属 wiring,judge 属诊断面;严重度沿用 #4186 讨论定论 p2——有界瞬态尾损失、非缓存泄露家族。lane-agnostic 核心路径,不打单一客户端标签)。

开始审核:先独立通读两个 commit 的完整 diff 与既有行为清单,再做定向测试与回归判定,结论按结构化八问给出。审核不照抄 PR/issue 侧分析,diff 我自己读。

@ranxianglei ranxianglei added bug Something isn't working p2 scope/wiring labels Oct 21, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

PR #4217 审核结论 — 报告路径的修复成立,但发现同机制一处残留(F1),需要你拍板

独立重读了全部 diff(未照抄 issue 侧结论)、本地重跑定向测试与 fuzz、核对 CI。先给判定:按现状提交的代码能解决 #4186 报告的「失败轮断缝」,diff 干净(19 个文件全部与本 PR 目的相关),typecheck clean,本地定向全绿,CI 在 d2c1086 上全绿/跳过。但审核中发现 breaker 条件只覆盖「一轮里全是失败」的形状,没覆盖「失败与其他调用混排」——泄漏机制与 #4186 完全相同,按你的政策不拆票,作为决策点列在下面。

结构化八问

  1. 是什么问题:loop 把 compress/decompress 的失败回执以临时帧拼进当轮 re-request,这些帧从不落 session、客户端历史里也没有对应字节;帧到过一次上游后,下一个客户端请求在同一位置字节不同 → provider 冻结前缀被中途改写 = unsanctioned cache seam。fuzz 种子 1592 确定性复现,你加扫 17 种子红 7(41%),签名一致。
  2. 什么原因导致的:坏字节的产生者是 loop「工具结果拼进 re-request 视图」的设计(codex 版本压缩后缓存大幅度降低问题 #1548 临时帧契约本身正确);缺的是出站闸门——没有「本轮含失败帧就不发」的规则。旧 小模型(2B级)compress 循环空转:同参数重复失败烧满 10 轮 + 幻觉 ref + 摘要长度约束不遵守 #156 条件(仅字节相同参数重交才 break)几乎覆盖不到实际路径。
  3. 影响范围与严重程度(按选项):
    • A(按现状合):failed-only 轮修复;残留 = 失败与其他 proxy 调用混排的轮(成功+失败、decompress 失败+acp_status 等)仍会把失败帧发出去一次 → 同类断缝。触发频率低于纯失败轮(需多调用轮+部分失败),但签名与危害相同(缓存断缝→成本)。属缓存泄露类,若坐实为 p1。
    • B(本 PR 内泛化条件):所有含失败帧的形状都覆盖;影响面 = 混合轮也提前结束(模型对失败的响应从当轮重试推迟到下一轮),不丢任何已持久化状态(fold 已落盘、回执随客户端回显走)。
  4. 怎么修复的:A = failed-only 轮立即 finish_reason=length 收尾 + ❌ receipt,删除 小模型(2B级)compress 循环空转:同参数重复失败烧满 10 轮 + 幻觉 ref + 摘要长度约束不遵守 #156 死代码;第二 commit 给 judge 豁免清单补两类合法变换(CCR placeholder 严格整体解析、emergency-truncation 结构证明),纯诊断零流量影响。B = 去掉 failedMutating.length === proxyResults.length 等式改为 >0,fuzz 补一个混排场景,调整钉住「混合轮必发 re-request」的测试。
  5. 是否有回归问题(按选项):
  6. 是否破坏缓存:这正是本 PR 的目标——消除 unsanctioned seam,方向正确;对缓存/重试/增量游标策略无副作用(judge 纯诊断,字段 additive,typecheck 证明无消费者破坏)。
  7. 是否改了设计方向:否。仍守 codex 版本压缩后缓存大幅度降低问题 #1548 临时帧契约(帧永不落持久层);B 方案不触碰 identity/persistence 边界(thread 里弃 B 选 A 的理由正是这个,泛化条件不需要碰它)。
  8. 是否引入了新的配置:无。无新 env/config/默认值。

F1 决策点 — 选项对比

A:按现状合 B:本 PR 内泛化(我的推荐)
覆盖面 failed-only 轮 一切含失败帧的轮
残留 混排轮仍断缝(同机制、频率较低) 无
行为变更面 如披露 扩大:混合轮也提前结束
代价 零额外工作 小 diff + 1 个 fuzz 场景 + 少量 fixture 调整
判断依据 可接受的止血 一个机制应一个 PR 闭环;PR 自己的理由(「失败帧一旦到过一次上游,下一客户端请求必然断缝」)对任何含失败的轮同样成立

其他发现(均不阻塞):

证据:typecheck clean;本地 loop-core+fix-stream+invariant-judge 58/58、11 个改造单测文件 61/61、5 个 hermetic e2e 文件 27/27;fuzz 种子 1592(默认 3 场景)+ 你点名的 44444/83204 独立跑绿;CI 在 d2c1086 上全部 check runs success/skipped。

判定:拍 A —— 可合(已验证的现状代码质量成立,F1/F2 记入 #4186 后续)。拍 B —— 我直接在本 PR 分支推泛化 commit、补 fuzz 场景、重跑定向套件后再交。等你拍板。

@ranxianglei

Copy link
Copy Markdown
Owner Author

(ai: zhipuai-lb/glm-5.3)

已并入合并列车 #4525(master 6d7c587,合并方式 merge commit,提交保留原作者署名)。本 PR 关单收尾;若列车中对应提交出问题,回滚点为 #4525 单提交 revert。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working p2 scope/wiring

Projects

None yet

1 participant