Skip to content

fix(engine): 保留终态错误的真实来源 - #142

Open
zhuowp wants to merge 2 commits into
pinvou3-cleanfrom
fix/terminal-error-source-20261105
Open

zhuowp wants to merge 2 commits into
pinvou3-cleanfrom
fix/terminal-error-source-20261105

Conversation

@zhuowp

@zhuowp zhuowp commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

No-Issue: Issues are disabled in this repository. This is the foundation dependency of Pinvou/pinvou3#2383.

做什么

给内部 TurnComplete 保留实际错误来源。模型传输、模型响应和 provider 等待超时归为 Model,本地请求准备、执行及流式时长和内容上限归为 Runtime。普通路由在获得准入后准备请求,也保留本地错误来源。重试、取消、首错选择和公开线协议沿用原有行为。

固定版本和验证

  • 修复提交 c8e05f61ef6b58640695e65fe7b217abd47fdf69,树 efcc3fff03b5c61a36f0acb910df917cfaf29781,目标基线 11cba403dc6bd6ca5c4dca55042785b03449ac8e。在评审者已解决冲突的 e8d00aa0b 上补齐了测试目标的 22 处构造。
  • 这批修复已通过 v3 独立裁决。Rust 格式检查、工作区 Clippy 和 7 项定向测试通过,覆盖实际终态分类、Anthropic 本地准备拒绝零 HTTP 请求、内容上限、幂等准备及公开线协议省略。
  • Windows 全工作区测试运行到 codewhale-app-server 后停止,105 项通过,1 项失败。失败用例使用 /tmp/codewhale-primary,路径校验器在 Windows 拒绝该路径。测试和校验代码与目标基线相同。Linux CI 正在验证最终提交。

宿主依赖

Pinvou/pinvou3#2383 仍固定在早期 CodeWhale 提交。之前的 121 项宿主定向测试对应旧组合。底座合入 pinvou3-clean 后,宿主需要对齐真实公开 gitlink、更新登记和指纹,并运行最终组合的门禁。

asto18089 added a commit to sky11023/CodeWhale that referenced this pull request Nov 5, 2026
In-range lock refresh closing all 4 open Dependabot alerts (4 high):

- undici 7.29.1 -> 7.30.1 via wrangler 4.150.0 -> 4.156.1 -> miniflare
  5.20261006.2-beta -> 5.20261101.0-beta in root, web/, and
  telemetry-ingest/: closes GHSA-xjj6-f33q-929c / CVE-2026-109869
  (alerts Pinvou#139, Pinvou#142, Pinvou#145; fetch DoS via unhandled error on excessive
  content-encodings). This closes the follow-up PR Pinvou#120 left open:
  wrangler 4.156.1 (latest stable) exact-pins miniflare
  5.20261101.0-beta (2026-11-01), which exact-pins undici 7.30.1, so a
  plain `npm update wrangler` per directory lands there. Collateral,
  all npm-canonical: workerd 1.20261006.1 -> 1.20261101.1 (+ its five
  platform binaries), tslib 2.8.1 -> 2.9.1 in root and
  telemetry-ingest, `libc` metadata added to the existing
  @img/sharp-* entries in root and telemetry-ingest, wrangler's
  @cloudflare/workers-types peer range following to ^5.20261101.1,
  web's nested miniflare ws 8.21.0 hoisting to the top level
  (content-identical), and the nested miniflare/node_modules/undici
  copies in web/ and telemetry-ingest/ moving to a single top-level
  7.30.1 entry (root's undici was already top-level). No other package
  versions change.
- @azure/msal-node 6.0.1 -> 7.1.2 in extensions/vscode/: closes
  GHSA-xcx6-jx8p-x3h5 / CVE-2026-109461 (alert Pinvou#136; auth-code flow
  origin validation; first patched 7.1.0) via the native in-range
  path: @azure/identity 4.14.1 (2026-10-29) requires @azure/msal-node
  ^7.1.0 and satisfies @vscode/vsce's declared ^4.13.2, so
  `npm update @azure/identity` moves identity 4.14.0 -> 4.14.1 and
  msal-node -> 7.1.2 with no manifest change. msal-common follows
  upstream's native shape (top-level 16.14.2 for msal-browser 5.25.1,
  nested 16.15.0 under msal-node; both exact pins). msal-node 7.x
  keeps the confidential-client surface identity uses (jsonwebtoken
  ^9, node >= 20); the path is dev-only (vsce).

Verified:
- npm ci green in root, web/, telemetry-ingest/, extensions/vscode/
- telemetry-ingest: npm run typecheck, npm run check (wrangler 4.156.1
  deploy --dry-run), npm test (171 passed)
- web: npx wrangler --version -> 4.156.1, npm run cf-typegen, npm test
  (408 passed in 47 files)
- extensions/vscode: npm run check (tsc), vsce package
  --no-dependencies (valid 13-file vsix), and an identity -> msal-node
  7 live smoke: ClientSecretCredential.getToken() builds and parses a
  real AAD protocol response through msal-node 7.1.2
- npm audit: root and telemetry-ingest report 0 vulnerabilities; web
  reports 20 moderate advisories (glob via @opennextjs/cloudflare
  1.20.9, minimatch via the eslint toolchain) ingested after this
  branch's initial verification; all devDependencies, none open GitHub
  alerts, left for a separate decision. No HIGH findings remain in any
  of the four refreshed manifests: the base audits carried 1 HIGH for
  undici in root, web/, and telemetry-ingest/, and 2 HIGH for
  msal-node in extensions/vscode (the advisory this bump closes)

Out of scope, unchanged: npm audit reports follow-redirects (high) in
integrations/{feishu,wecom}-bridge and qs + minimatch (moderate) in
extensions/vscode; none of these are open GitHub Dependabot alerts, so
they are left for a separate decision.

Signed-off-by: asto <asto18089@126.com>
@zhuowp
zhuowp requested a review from asto18089 November 6, 2026 09:22
@zhuowp
zhuowp marked this pull request as ready for review November 6, 2026 09:22
将模型与本地运行时错误的来源随最终选中错误一起传递,保留重试清空、本地覆盖与取消语义。
内部 TurnComplete 增加来源字段,同步仓内构造和消费方,现有线协议保持不变。

Signed-off-by: zhuowp <zwp-1989@qq.com>
@asto18089
asto18089 force-pushed the fix/terminal-error-source-20261105 branch from a4fc2e9 to e8d00aa Compare November 6, 2026 15:00

@asto18089 asto18089 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

从零复审:REQUEST_CHANGES — 唯一阻断 = 测试目标 E0063 编译断(实跑亲证)

本轮从零复审:9 个分片并行(失败出口清单 / turn_error 生命周期等价 / TurnComplete 全仓构造点 / 线协议泄漏 / 消费方适配 / 夹带审计 / 主线耦合间隙 / 覆盖与变异设计 / 宿主耦合核对)+ 本地门禁实跑 + 变异负控与正控。codewhale-hq#2383 评审发现的同类阻断在本 PR 上独立复现,文件清单逐位吻合。

已完成的 rebase(已推送)

已 rebase 到最新主线 11cba403dc 并推送(443 直推失败,改走 GitData API:树 d70da5f50663... 与本地 rebase 结果程序化断言全等;作者/提交信息/Signed-off-by 原样保留;新 head e8d00aa0b0)。冲突仅一处:turn_loop.rs 顶部 main #124 的 fresh_fallback_tool_id 与本 PR 的 TurnFailure 同位相邻,双方语义完整保全。rebase 保真双重验证:range-diff 仅 2 处冲突区上下文差异;排序后增删行多重集与原提交字节级一致(134/134 行)。

P0 阻断:cargo test 目标编译断 E0063(原 head ×20,rebase 后 ×22)

  • 原提交 a4fc2e91(树 db5b9d4b13)测试侧 Event::TurnComplete 构造漏补 error_source 共 20 处:runtime_api/tests.rs 7 + runtime_threads/tests.rs 13 —— 与 codewhale-hq#2383 评审公布清单逐位吻合。
  • rebase 后主线 #131 在 runtime_threads/tests.rs 新增 2 处构造,合计 22 处(15+7,rustc 实跑输出确认)。cargo check --workspace --all-targets rc=101,cargo test -p codewhale-tui 无法编译。
  • CW CI 无 cargo-test 门禁(原 head 仅 CodeQL/intake/analyze/credentials 绿),正文"配套底座树已通过 v3 完整审核"未覆盖测试目标编译层;正文虽披露"不是单独运行全部 CodeWhale 测试",但实际后果是测试目标从未编译。
  • 修复处方已实跑验证:22 处机械插入 error_source: None,(status: 行后,与本 PR 自身 36 处插入同型)后:cargo check --workspace --all-targets 绿、cargo fmt --check --all 绿、clippy 与 main 基线完全一致(8 项 too_many_arguments/collapsible_if 均为本 PR 未触碰函数、11cba403dc 基线同款,属本地工具链与 CI 的 lint 漂移)。请作者补齐该 22 处并按正文惯例重锚固定提交/树声明。

核心语义评审:全部通过

  • 分类正确:run_turn 全部 Failed 出口(预算/max-steps/上下文恢复耗尽/请求派发/终止 stop-reason/benchmark/REPL init+refresh/空流尾返回)逐一核对 Model/Runtime 无误;request_error_source 每次派发尝试按 prepare_stream_call(本地载荷构建,无网络)结果重算,prepare 失败=Runtime、provider 传输=Model,与契约一致;未知不猜测(Interrupted/合成失败/panic 恢复均 None)。
  • 行为等价:turn_error 旧 24 处 token 1:1 映射;重试清空(stream-retry/reasoning-only)消息+来源一并丢弃;首错 wins(or→or_else 等价);取消优先于尾错误消费;TurnContext 每回合新建(唯一生产构造点 engine.rs:5964),terminal_error_source 仅 finish() 写入且 finish() 必然立即返回——stale 泄漏结构性不可能;两处 expect 可证不可达。
  • 线协议零泄漏:crates/protocol/ 全目录零 error_source;protocol_parity 穷举解构显式丢弃(亦是未来字段守卫);SSE/REST/remote bridge/journal/telemetry/hooks 全不携带;Event 无 serde。
  • 无夹带:68 hunks 全分类为字段适配/分类逻辑/文档;零行为漂移、零格式噪音;无 Cargo.lock/CI/文档改动。
  • 主线耦合无洞:#123/#124/#129/#131/#133-135 在爆炸半径内无新增未分类失败路径(唯 #131 的 2 处测试构造,已计入上述阻断)。

P2(不阻断)

  1. 新行为零仓内断言覆盖:全仓无任何测试绑定 error_source 值(grep 证实仅生产代码 4 处触点)。变异实跑:M1(Model→Runtime 翻转)3 个失败回合测试全绿存活、M2(Runtime→Model 翻转)2 个测试全绿存活、M5(engine.rs:6494 传播改 None)2 个测试全绿存活——分类值可静默翻转而全绿;纯字段删除(M4 形态)会被编译器 dead_code 拦截,但传播截断(M5)不会。正控实证:在既有测试 injected_chat_content_filter_never_becomes_a_completed_answer(engine/tests.rs ~9263)解构处加 error_source 绑定 + assert_eq!(Some(TurnErrorSource::Model)) 后,M1 立即转红。处方:该测试与 max_steps_exhaustion_fails_as_budget_never_completed(~8908)各加 2-4 行断言(Model/Runtime 各一钉)。
  2. "线协议不变"缺负向钉:若未来 wire TurnComplete 误加 error_source,现无测试可拦;建议在 protocol_covers_engine_events 补 assert!(terminal.get("error_source").is_none())。

P3 / 备注

  • "远控桥接适配"实为纯测试构造:生产桥 observe_engine_event 上传 turn.completed{status,usage},不透传 error/error_source;远端宿主目前只能经 in-process 事件消费本字段(与 codewhale-hq#2383 实际消费方式一致,不构成缺陷)。
  • engine 级合成失败点(5119/6139/6656/6692/6884 等)按字段文档刻意保留 None;宿主若要区分预检类失败需逐点补分类。
  • exec_agent.rs:975-982 仍以文本启发式归类终态错误(如 REPL 失败文本含 connection → 误判 Network → retryable exit);本 PR 已提供权威来源,建议另行小 PR 接线。
  • 风格 nit:turn_loop.rs 六处全限定 crate::core::events::TurnErrorSource 可提为 import。

与 codewhale-hq#2383 的耦合(核对无误)

codewhale-hq#2383 经 in-process Event::TurnComplete.error_source 消费(forwarder.rs 比对 TurnErrorSource::Model),字段名/类型/语义与本 PR 完全一致;其 gitlink 现钉原 head a4fc2e91(即 E0063×20 之树,且落后主线 7 提交)。修复路径:本 PR 补齐 22 处 → 作者更新正文锚定 → codewhale-hq#2383 重钉新 gitlink 并同步登记/指纹/EXPECTED_HEAD,其 RC 唯一阻断即解除。

本地验证环境说明(诚实披露)

  • 最终全量证据:cargo test -p codewhale-tui(串行 --test-threads=1,RUST_MIN_STACK=16MB)= 12,656 passed / 0 failed(lib 12,407 + 集成 13 + 236,13+8 ignored),含本 PR 全部 touched 区域与 22 处补字段的 runtime_* 测试。
  • 环境坑(均与本 PR 无关,已在干净树/单独运行复核):默认栈下 runtime_api 两个测试栈溢出 SIGABRT(RUST_MIN_STACK=16MB 即消,属 debug 深帧环境问题);多线程并行套件在 ~2550 处于共享 engine 状态交互整组停滞(cwd 级 .codewhale/state/subagents.v1.lock,单独运行 0.15s 即绿),PR diff 零锁/状态代码且 patch 字节级等价,判定上游既有。

结论

方向正确、实现干净、根因到位、无夹带、线协议守住;唯一阻断是测试目标 22 处机械补字段(处方已验证)。请作者补齐并重锚正文后,本 PR 可转入可合并状态,并解锁 codewhale-hq#2383。

No-Issue: Issues are disabled in this repository.

Signed-off-by: zhuowp <zwp-1989@qq.com>

@asto18089 asto18089 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全新复审(2026-11-08,fresh 8 分片 + 3 突变实跑)

头 c8e05f61e 恰平 pinvou3-clean tip 11cba403dc(merge-base = base tip,零 rebase)。按四条标准独立复审:claim 完备性/根因、工程优雅、夹带、feature+performance 缺陷。

结论:可合并(APPROVE),以下均为非阻塞 MINOR/NIT

1. Claim 真实且完备,根因到位。 审计了 run_turn 全部失败路径(10 个终态 + 8 个 stream_error 生产点):TurnOutcomeStatus::Failed 在 turn_loop.rs 中只剩 TurnFailure::finish 一处字面量,即每个 Failed 终态必然携带 source,无分类洞。根因处理正确:普通路由本地准备失败之所以此前会被误判为 provider 错误,是因为字符串分类无从分辨——deferred preparation + DeferredStreamPreparationError 哨兵 downcast 是让 engine 知晓真实来源的最小机制(新测试 ordinary_route_preparation_failure_is_runtime_without_sending_http 实证了零 HTTP 请求 + Runtime)。首错选择、重试清除(2788/3510 处 turn_error = None)、取消路径均确认未变;error_source 无任何 serde 泄漏面(Event 不派生 Serialize,wire 侧 error_source: _ 且有 parity 测试钉死)。

2. 不属重复造轮。 error_taxonomy 分类的是展示内容(severity/可恢复性),TurnErrorSource 是选中终态错误的生产者溯源,二者关注点不同且后者刻意不上 wire;首错胜出机制要求 source 随 message 一起穿越重试,事后查表不可行。TurnFailure 让 10 个返回点删掉手工元组,过 rung-2。~60 处测试构造更新是本仓既有惯例(当年 base_url 字段同样方式落地,commit cef1632)。

3. 零夹带。 12 个文件全部在 crates/tui 内;无 protocol/persistence/CHANGELOG/CI/Cargo.lock 改动。commit 2 除 22 处测试构造(数目与正文一致)外含 3 处生产改动(client.rs 重构、哨兵类型、split_stream_open_error),但其 commit message("区分本地请求错误")已声明且都在 claim 内,client.rs 重构经逐步比对确认行为保持(同序 permit → 同一 prepare_outbound_request(request, true) → 同一 finish_prepared_stream,零双重准备)。commit 1 的 committer 是评审代 rebase 身份,作者与 DCO trailer 保留。

4. 无缺陷。 fmt 干净;clippy 8 个 deny 全部为既有问题(逐条比对不在本 PR hunk 内);engine 模块实测 test result: ok. 714 passed; 0 failed; 4 ignored,6 个定向 filter 全绿;3/3 突变全红(去哨兵 → ordinary-route 测试红;content-cap runtime→model → 红;max-steps runtime→model → 红);happy path 零新增开销(准备次数、permit 顺序、clone 数与 base 一致,TurnErrorSource 为 Copy)。

非阻塞发现

  1. [MINOR·测试强度] Model 侧分类基本未钉。 突变实跑:把 stream-open 错误默认翻成 Runtime,全仓没有任何测试变红——TurnErrorSource::Model 断言只覆盖了 in-stream content-filter 一条路径。中心 claim(传输类失败 = Model)只有 Runtime 半边有回归保护。建议补一个小测试:dead endpoint 打开流失败,断言 error_source == Model。同理 "Context remains above model limit" 恢复耗尽的 Runtime 分类也无测试钉(M4 突变无消费方)。
  2. [MINOR] 三处可零成本带 Some(Runtime) 却传了 None: reject_unstarted_submission(engine.rs:5119)、no-model-client 拒绝(engine.rs:6139,"API client not configured" 明显是本地)、panic catch 路径(engine.rs:~6422,catch_unwind 伪造成 Failed)。按字段契约 None 合法,但与本 PR 自己建立的本地区分精神不一致。另:route-mismatch 等内部不变量错误会落到 Model 默认(实践中不可达,仅记录)。
  3. [NIT] turn_loop.rs:2127/2479 的 turn_error = Some(..); return turn_error.expect("failure was assigned").finish(turn) 可直接 return(该变量此后无人读),2127/3534 各有一处可避免的 clone;DeferredStreamPreparationError 的哨兵契约只写在 DeepSeekClient 侧,trait default 文档未提示未来 impl 需包本地准备错误才能获得 Runtime 分类。

证据声明

以上测试/突变数字均为本机 /tmp/cw-pr142-review(CARGO_TARGET_DIR 共享缓存)实际运行输出,非转述。Windows 全量跑停在与本 PR 无关的 /tmp/codewhale-primary 路径校验用例与正文说明一致(该测试与校验代码在本 PR diff 外)。

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.

2 participants