Skip to content

[PR] fix: fork prefix gate is advisory — adopt drifted fork children with a classified warn (#3741) - #3757

Merged
ranxianglei merged 5 commits into
masterfrom
fix/fork-prefix-advisory
Oct 19, 2026
Merged

ranxianglei merged 5 commits into
masterfrom
fix/fork-prefix-advisory

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

ai: zhipuai-lb/glm-5.3

Fixes #3741(保守档,基于已合并的 #3743 分类诊断实现,owner 在 #3741 拍板)。

一句话:fork 子会话的首请求与继承前缀不匹配时,不再 409 卡死——照常收养,打一条带分类的 warn。

为什么拆这扇门

改动(src/server/handle.ts,净 -6 行)

  • receipt 块:verdict 不 ok 且 requests===0 → 原 409 分支改为 loggerLog warn + return true 收养;warn 带 [PR] fix: classify FORK_PREFIX_CONFLICT rejections instead of failing blind (#3740) #3743 分类 reason(id-drift / hash-drift-under-same-id / prefix-longer-than-incoming 等)+ protocol
  • publicForkPrefix 漂移时保持 false:side-lane 降级启发照常发言,不让未经证实的前缀一票否决
  • 门保持 one-shot:首请求后 stats.requests>0 自然绕过,不重复检查

测试(tests/plugin-fork.test.ts 翻转,48/48 绿)

三种漂移形态(语义改写/重排/过短重放)全部:200 + 转发增长 + 父会话 parentRevision 不动 + 子会话 revision 前进 + 恰一条 warn(one-shot 语义钉死)+ 后续请求正常。dsh-fork-adopt 42/42、pi-omp-fork-adopt 含在内全绿,tsc 干净。

…a classified warn (#3741)

The ordered-prefix 409 was a permanent dead-end: the rejected first
request never incremented stats.requests, so every retry re-hit the
same gate with no clear or TTL path (field report: dsh fork child
stuck after an in-place proxy upgrade re-keyed message ids, #3642
canonical re-key generation). Routing is the session-id binding
(recordPluginSession -> resolveConversation); the byte-exact pre-gate
added no correctness fold reconcile (#3641 order-over-hash) doesn't
already enforce — it only produced unrecoverable 409s.

Now: mismatched first request is adopted, logs one classified warn
(#3743 reason taxonomy), publicForkPrefix stays false so the side-lane
demotion heuristics keep their normal say. Cost: a divergent child's
first request goes out unfolded — a one-time token spike, never
corruption. Gate stays one-shot (stats.requests > 0 bypasses it).
@ranxianglei ranxianglei changed the title fix: fork prefix gate is advisory — adopt drifted fork children with a classified warn (#3741) [PR] fix: fork prefix gate is advisory — adopt drifted fork children with a classified warn (#3741) Oct 19, 2026
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: fix/fork-prefix-advisory (af61c62)

Option A — Install this exact build (recommended)

npm install -g billion-context@0.0.4452-pr.3757.22b6935

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-pr3757.tgz
npm install -g package

Install instructions are refreshed on each push.

awork added 3 commits October 19, 2026 09:42
…3757 review)

The HASH PARITY CONTRACT still described the pre-#3757 behavior (409
FORK_PREFIX_CONFLICT, permanently poisoned child). A mismatched first
request is now adopted with a classified warn; the real cost of hash
drift is losing the verified-prefix fast path plus one unfolded first
request.
#3757 review)

Inverting the 409 sub-cases removed the only coverage of the
hash-drift-under-same-id and ref-broken verdict branches (including
#3743's fix that a pruned ref reports ref-broken alone, never
hash-drift). Pin both directly against publicForkInputCheck with
negative assertions so a future classifier regression cannot ship
silently behind the advisory warn.
…review)

lane-guard pairing for the handle.ts lane-adjacent edit, with real
coverage: a drift-mismatched first request that is a declared side
call (dsh title-gen shape) must be adopted AND still diverted to the
#388 passthrough without touching child kernel state — reachable only
after the gate stopped rejecting.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

查重:不重复。 独立复核:按 fork prefix / FORK_PREFIX_CONFLICT / adoption 检索 open+closed issue——只有已关闭历史家族(#2399/#2403/#2625/#2394/#2383)、已合并的诊断 PR #3743、本 PR;近期 15 个 open PR 无一触碰 fork 门或 side-lane 区域(#3745 标题里的「(#3741)」是平台编号撞车,那条是 delegate-wait 措辞,不相关)。

标签: bug / dsh / pi / opencode / scope/kernel / p2 —— fork 子会话卡死是真实缺陷家族,机制在会话核心的 fork 继承路径;受影响宿主是全部三个 plugin fork 家族(dsh/pi/opencode);整体可用但个别会话永久死掉、可新建绕行 → p2(与 #3743 triage 一致)。

审核结论(八问)

  1. 是什么问题:fork 子会话的首请求与继承前缀不一致时被 409 拒绝,且这个拒绝是永久的——被拒请求不计入会话请求计数,每次重试都重新撞同一扇门,没有 TTL、没有自愈路径。现场案例(This run failed with 409: "first child request does not match its inherited ordered prefix" | 本轮运行失败409 "first child request does not match its inherited ordered prefix" #3741):代理原地升级换代(The intermediate message (core) is not protocol-agnostic: switching protocols/models causes identity churn; cross-protoc | 中间消息(core)没有做到协议无关:切协议/切模型制造身份 churn,补齐跨协议映射 #3642 id re-key)后 dsh fork 子会话确定性卡死。
  2. 什么原因导致的(分层):表层——gate 把「前缀失配」当致命错误拒掉整个请求;深层——这道门不提供独有保护:子会话的真正唤醒路由是 session-id 绑定(recordPluginSession→resolveConversation),字节级前缀校验只是唤起后的附加安检,其保护面(历史漂移)由每轮 resent history 的 fold reconcile 完整覆盖,却握有对子会话唯一入口的生杀权。另外拒绝原因在决策点被吞掉(诊断盲点,已由 [PR] fix: classify FORK_PREFIX_CONFLICT rejections instead of failing blind (#3740) #3743 分类诊断补齐)。
  3. 影响范围与严重程度:所有首请求相对继承前缀漂移的 plugin-mode fork 子会话(版本偏斜导致 id 重铸、host 改写 body、重放截断/重排等触发面);受影响的子会话完全不可用(主会话不受影响,用户可新建会话绕行)→ p2。
  4. 怎么修复的:gate 从 fail-closed 降为 advisory——失配时照常转发(收养)+ 打一条带分类的 warn(id-drift / hash-drift-under-same-id / ref-broken / prefix-longer-than-incoming 等),后续轮由 reconcile 拉平继承 folds;门保持 one-shot(首请求后自然绕过)。失配时 publicForkPrefix 保持 false,side-lane 启发照常发言。代价:漂移子会话首请求未折叠出站(一次性 token 峰),正确性无损。
  5. 是否有回归问题:逐项审过——accept 路径不变(匹配仍走快速路径);one-shot 语义不变;父会话隔离有测试钉住(parentRevision 不动);标题生成等侧请求污染主会话状态:prepareAnthropic 缺 isTitleGen 守卫、快照无条件覆盖、survivedCount 每请求空转 #388 side-lane kernel-no-touch 契约保持——新增测试钉住「首请求本身是声明的 side call(title-gen 形态)时同样收养且走 passthrough 分流,stats.requests 保持 0、snapshot 不动」。新可达路径已分析:收养后 publicForkPrefix=false 使启发式 demotion 理论可达,但 demotion 要求「全 bili-tools、无 host tools」的 body 形状,真实主请求恒带 host tools → 实际不可达,最坏情形一轮 passthrough(仍会转发,下一轮正常)。顺带发现一个既有小问题(requests>0 后每请求仍重算 verdict)——非本 PR 引入,未动。无 config surface / 持久化格式 / wire shape / kernel 变更。
  6. 是否破坏缓存:不涉及。
  7. 是否改了设计方向:有一处明确变更——fork 前缀门语义 fail-closed → advisory,属「产品决策」类变更(§7.4 human-gated)。⚠️ PR 描述与 CHANGELOG 均引用「owner 在 This run failed with 409: "first child request does not match its inherited ordered prefix" | 本轮运行失败409 "first child request does not match its inherited ordered prefix" #3741 拍板」,但我查了平台 acp_delegate_wait reports "Failed" on a normal wait timeout while the delegate is still running #3740(全部楼层)、GitHub This run failed with 409: "first child request does not match its inherited ordered prefix" | 本轮运行失败409 "first child request does not match its inherited ordered prefix" #3741(全部评论)、PR [PR] fix: classify FORK_PREFIX_CONFLICT rejections instead of failing blind (#3740) #3743(全部评论)——所有评论均为 agent 所发,任何公开线程里都没有找到明确的 owner 批准记录。 若你在别处拍过板,请在本线程链接/确认;否则请在此明确 sign-off 后再合。
  8. 是否引入了新的配置:不涉及。

审核中发现的问题与处置(均已直接修掉,3 个标注 commit 已推到 fix/fork-prefix-advisory,当前 head d1da179)

  1. 过期注释:src/agent/fork-adopt.ts 的 HASH PARITY CONTRACT 注释仍描述旧 409 行为(「any drift here poisons the child conversation permanently」)——与代码事实不符,已更新为 advisory 语义。
  2. 本 PR 引入的测试覆盖缺口:翻转掉的测试删掉了 [PR] fix: classify FORK_PREFIX_CONFLICT rejections instead of failing blind (#3740) #3743 分类器两个分支仅有的 pin(hash-drift-under-same-id、ref-broken——含「pruned ref 不得误标 hash-drift」的修正),新 warn 正则也不覆盖 ref-broken → 补了直接调用 publicForkInputCheck 的单测带负向断言把两分支钉住。
  3. lane-guard 红(真实 CI 拦点):handle.ts 的注释改动命中 lane 符号正则,但四个专用 side-lane 套件一个都没更新 → 在 tests/fork-side-request-lane.test.ts 加了真实覆盖用例(漂移首请求 + 声明 side call → 收养且 passthrough 分流),本地模拟 lane-guard 逻辑通过。

行为变更披露(旧 → 新)

旧:首请求前缀失配 → HTTP 409 {ok:false, code:'FORK_PREFIX_CONFLICT', detail:<分类原因>},子会话永久卡死;新:照常转发(200)+ 一条 [fork-prefix] <sessionId>: inherited prefix mismatch — adopting anyway (<reason>; protocol=<protocol>) warn。有意变更,CHANGELOG 已披露。

验证状态

本地定向:typecheck 干净;tests/plugin-fork.test.ts 49/49(含新增 1)、tests/fork-side-request-lane.test.ts 2/2(含新增 1)、dsh-fork-adopt + pi-omp-fork-adopt 42/42。全量交远程 CI(d1da179a5 上运行中,出结果后跟进)。

判定:技术可合——方向正确(移除产生缺陷的机制而非掩盖症状)、回归清点干净、测试有牙。唯一拦点是第 7 问:PR 引用的「owner 决策」记录在所有公开线程中找不到,请确认或在此明确 sign-off;CI 转绿后人工合并即可。

小注:PR 描述写「src/server/handle.ts,净 -6 行」,-6 实为整个 PR 的净值(handle.ts 因注释块展开自身略增)。

@ranxianglei ranxianglei added bug Something isn't working dsh opencode p2 pi scope/kernel ci:full Run the FULL CI matrix (all e2e lanes + 12-leg test matrix) on this PR labels Oct 19, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

CI 跟进(上条回复承诺的):给 PR 加了 ci:full 标签(与 #3743 同一机制——无此标签时 pull_request 事件的全量矩阵按设计跳过),全量矩阵已在 head d1da179 上跑完。

结果:全绿。

最终状态:CI 全绿 + 本地定向验证全过 + 回归清点干净。唯一待办仍是上条第 7 问——PR 引用「owner 在 #3741 拍板」,但公开线程里找不到这条决策记录;请确认或在此明确 sign-off advisory 方向后即可人工合并(合并由你操作)。

@ranxianglei
ranxianglei merged commit a8853fe into master Oct 19, 2026
26 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

📦 Merged Build Published

This merge to master was published as 0.0.588-master.a8853fe (rolling master tag).

npm install -g billion-context@0.0.588-master.a8853fe  # this exact build
npm install -g billion-context@master  # always the newest merged build

npm view billion-context@master prints the current version at any time.

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

Labels

bug Something isn't working ci:full Run the FULL CI matrix (all e2e lanes + 12-leg test matrix) on this PR dsh opencode p2 pi scope/kernel

Projects

None yet

1 participant