Repository navigation
[PR] fix(fork-adopt): defer adoption when the capability probe cannot reach the proxy (#4266) - #4345
ranxianglei wants to merge 7 commits into
Conversation
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g [email protected]This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4345.tgz
npm install -g packageInstall instructions are refreshed on each push. |
c61d0f1 to
d7cf89b
Compare
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复——本 PR 是 #4266 家族 item ⑨ 的修复(我自己开的);①–⑧ 由各自 PR 承载(#3024/#3039/#3951/#4223/#4263),无其他 open issue/PR 覆盖「capability probe 瞬态失败分类」这一机制。 标签: bug / dsh+pi+opencode / scope/wiring / p1(fork 继承被进程级永久放弃,属「会话数据丢失类」,按 2026-10-19 口径一律 p1)——已打上。 审核结论(八问)
行为变更披露(old→new):单次瞬态 capability-probe 失败,旧=子会话采纳资格进程级永久退役 → 新=在 本轮动作(已验证执行)
判定:代码与行为审核通过,无拦下项;新 head |
…h the proxy (#4266) Item 9 of the zero-inheritance-cloning family (#4266): createForkAdopter's capability gate treated ANY failed manifest probe as terminal (done.add on first failure, no retry budget), even when the failure was only that the proxy was not reachable yet (host starting up / restarting / port changing). A child forked in that window was permanently zero-inherited, then dead-ended on the first-turn context_length_exceeded downstream. probeForkCapability now distinguishes definitive=true (manifest read OK but fork not advertised - old proxy: terminal on first probe, unchanged, avoids 404 noise) from definitive=false (manifest unreadable right now - !res.ok / timeout / network error: transient, deferred and retried up to maxAttempts, adopting once the proxy is reachable). Only definitive results stay cached; a failed read is never cached, so the next request re-hits the wire and self-heals. manifestForkCapable stays a thin boolean wrapper so existing callers/tests are unaffected. All native lanes (dsh/opencode/pi) share createForkAdopter, so the fix covers all of them. Behavior change (old -> new): a single transient capability-probe failure used to retire a child's adoption for the process lifetime; it now retries within the existing maxAttempts budget (default 3). Definitively-incapable proxies are unchanged (still terminal on first probe). Tests: rewrote the exploding-network case (now asserts deferred-until-cap instead of closes-on-first) and added a regression repro (proxy down on request 1, up on request 2 -> child adopts). 45/45 green across the three fork-adopt suites. Addresses item 9 of #4266.
ForkCapabilityProbe is module-internal; only probeForkCapability and manifestForkCapable are consumed across modules. INTERNAL_ONLY fix per scripts/check-unused-exports.mjs.
…used-exports gate) In-passing fix, unrelated to this PR's fork-adopt change: #4378 landed ENGPROOF_CHECK_NAMES / BANNER_CHECK_NAMES / CorruptIndexCheck / CorruptIndexVerdict exported but referenced only inside the same file, which the unused-exports gate (scripts/check-unused-exports.mjs, ci.yml unused-exports lane) rejects on every head including this one. Dropped the four leading exports; verified no importer outside the file (parents use ENGPROOF_CHECKS/BANNER_CHECKS/spawnCorruptIndexChild only). Gate clean + both consumer suites 17/17.
d7cf89b to
65f9db3
Compare
(ai: vllm-qwen/qwen3.8-27b) master 已转绿,本分支已按承诺 rebase 并推送(新 head
|
…help-merge sweep)
…help-merge sweep)
master window commits 293ae65/de5e03659 bumped kernel/package.json to 0.0.165 without regenerating the lockfiles; lock-sync re-resolution then rewrites the stale recorded versions (root 0.0.163, kernel 0.0.164) and fails. Pure recorded-version sync via npm install --package-lock-only; no dependency or semantic change.
Addresses item ⑨ of the zero-inheritance-cloning family tracker #4266 — reported as a production incident on that issue.
What was wrong
createForkAdopter's capability gate (src/agent/fork-adopt.ts) treated any failedGET /__bili/plugin/manifestprobe as terminal: on the first failure it calleddone.add(child)with no retry budget. A probe fails for two very different reasons:The gate conflated the two. In case 2 the child was permanently retired from adoption for the process lifetime (the capability cache self-heals, but
done.addhad already pinned the child), then dead-ended downstream on the first-turncontext_length_exceeded. The snapshot/fork degraded path immediately below already classified transient failures and retried withinmaxAttempts; the capability gate above it had no such classification.Fix
New
probeForkCapability()returns{capable, definitive}:!res.ok/ timeout / network error): transient, deferred and retried up tomaxAttempts, adopting once the proxy becomes reachable.Only definitive results stay cached per base; a failed read is never cached, so the next request re-hits the wire and self-heals.
manifestForkCapableremains a thin boolean wrapper so existing callers/tests are untouched. All three native lanes (dsh / opencode / pi) sharecreateForkAdopter, so one fix covers all of them.Behavior change (old → new)
A single transient capability-probe failure used to retire a child's adoption for the process lifetime; it now retries within the existing
maxAttemptsbudget (default 3). Definitively-incapable proxies are unchanged (still terminal on the first probe). No config surface, wire-shape, persistence, message-identity, or kernel change.Tests
tests/{pi-omp,dsh,opencode}-fork-adopt.test.ts; typecheck clean.Part of #4266 (item ⑨). Not closing the tracker — sibling items ①–⑧ are tracked there and land via their own PRs (#3024 / #3039 / #3951 / #4223 / #4263 plus pending owner decisions).