Skip to content

fix(telemetry): Address main agent tracing edge cases - #9121

Merged
wenshao merged 8 commits into
QwenLM:mainfrom
doudouOUC:fix/main-agent-telemetry-followups
Aug 16, 2026
Merged

fix(telemetry): Address main agent tracing edge cases#9121
wenshao merged 8 commits into
QwenLM:mainfrom
doudouOUC:fix/main-agent-telemetry-followups

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

What this PR does

This follow-up to #9107 fixes review-confirmed edge cases in main-agent tracing. Budget-triggered aborts are classified before the core generator can close the interaction as a user cancellation; non-streaming calls and hanging streams now preserve cancellation semantics when providers swallow aborts; and deferred TUI tool batches retain their exact interaction owners so mixed main and legacy ?btw work cannot leak tool results into the wrong continuation.

It also keeps the headless JSON Schema verdict scoped correctly: user-origin invocations retain the structured-output contract across tool continuations, while automatic Cron, Notification, Teammate, and runtime Goal drain invocations are not individually mislabeled when they return plain text. Goal-finalization and headless terminal failures now retain their bounded diagnostic message instead of an unrelated generic phase label, and the Steer regression test now proves the continuation actually ran and wrote to the original owner.

Why it's needed

Without these fixes, traces could export a budget overrun as cancelled, export a swallowed user abort as ERROR, leave legacy ?btw interactions open until TTL, submit a secondary tool result under the main prompt, or report successful automatic drain work as structured_output_missing. The remaining phase-label issues made valid failures materially harder to diagnose even though the real error was already available to the bounded telemetry finalizer.

Reviewer Test Plan

How to verify

Confirm that a wall-time budget abort wins over the core cancellation finalizer, non-stream and idle-timeout provider paths remain UNSET/cancelled after a swallowed abort, and an abort that occurs only after provider completion does not rewrite success. In the TUI regression, keep a main stream active while a legacy ?btw tool batch completes, then verify the secondary interaction closes independently and only the main tool response is submitted under the main prompt. With a JSON Schema configured, verify plain-text automatic drain invocations end normally while a user-origin invocation still reports structured_output_missing, including after a tool continuation. Finally, force Goal finalization and headless terminal errors and confirm the owning span contains the accurate bounded message and stable low-cardinality error.type.

Evidence (Before & After)

N/A — telemetry and lifecycle behavior only; no user-visible UI change.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

Node.js v22.22.3, npm 10.9.8, local bundle with sandbox disabled for the GenAI telemetry integration test.

Risk & Scope

  • Main risk or tradeoff: The TUI now defers completed tool batches until all active model streams settle and fails closed for secondary interaction owners in a mixed batch, so exact owner selection and queue draining are the highest-risk paths.
  • Not validated / out of scope: Windows and Linux were not tested locally; workflow invocation and workflow dispatch tracing remain out of scope.
  • Breaking changes / migration notes: None. No configuration or exported API changes.

Linked Issues

Follow-up to #9107.

中文说明

本 PR 做了什么

这是 #9107 的后续修复,处理评审确认的主 Agent tracing 边界问题。预算触发的 abort 会在 core generator 将 interaction 关闭为用户取消之前完成分类;非流式调用和挂起流在 provider 吞掉 abort 时会保留 cancellation 语义;TUI 延迟工具批次会保留精确 interaction owner,避免 main 与旧版 ?btw 工作混合后把工具结果泄漏到错误 continuation。

同时修正 headless JSON Schema 判定的归属:用户来源 invocation 会跨工具 continuation 保留 structured-output contract,而自动 Cron、Notification、Teammate 与 runtime Goal drain invocation 返回纯文本时不会被单独误标。Goal finalization 和 headless 终止失败现在保留经过边界控制的真实诊断消息,不再写入无关的通用阶段标签;Steer 回归测试也会证明 continuation 确实执行并写回原 owner。

为什么需要

没有这些修复时,trace 可能把预算超限导出为 cancelled、把被 provider 吞掉的用户 abort 导出为 ERROR、让旧版 ?btw interaction 泄漏到 TTL、把 secondary 工具结果提交到 main prompt,或把成功的自动 drain 工作误报为 structured_output_missing。其余阶段标签问题虽然真实错误已经存在于作用域中,但仍会显著降低故障诊断质量。

Reviewer 测试计划

如何验证

确认 wall-time 预算 abort 优先于 core cancellation finalizer;non-stream 与 idle-timeout provider 路径在吞掉 abort 后保持 UNSET/cancelled;仅在 provider 完成后发生的 abort 不会改写成功状态。TUI 回归中保持 main stream 活跃,同时完成旧版 ?btw 工具批次,然后验证 secondary interaction 独立关闭,并且只有 main 工具响应以 main prompt 提交。配置 JSON Schema 后,验证纯文本自动 drain invocation 正常结束,而用户来源 invocation(包括工具 continuation 后)仍报告 structured_output_missing。最后强制 Goal finalization 与 headless 终止错误,确认 owner span 包含准确的有界消息和稳定低基数 error.type

证据(前后对比)

N/A——仅 telemetry 与生命周期行为变化,没有用户可见 UI 改动。

测试平台

OS 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境(可选)

Node.js v22.22.3、npm 10.9.8;使用本地 bundle,并在关闭 sandbox 的条件下运行 GenAI telemetry 集成测试。

风险与范围

  • 主要风险或取舍:TUI 现在会等待所有活跃 model stream 结束后再处理已完成工具批次,并对混合批次中的 secondary interaction owner 采取 fail-closed,因此精确 owner 选择与队列 drain 是最高风险路径。
  • 未验证 / 范围外:未在本地验证 Windows 与 Linux;workflow invocation 和 workflow dispatch tracing 仍不在本 PR 范围内。
  • 破坏性变更 / 迁移说明:无。没有配置或导出 API 变更。

关联问题

#9107 的后续修复。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC
doudouOUC force-pushed the fix/main-agent-telemetry-followups branch from 35d862c to 49ca217 Compare August 14, 2026 03:52
@github-actions

Copy link
Copy Markdown
Contributor

Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration.

中文

请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

E2E Test Report

Reused the main-agent tracing baseline established for #9107 and verified the follow-up against the locally built bundle.

  • Command: cd integration-tests && QWEN_SANDBOX=false npx vitest run cli/gen-ai-telemetry.test.ts --coverage.enabled=false
  • Result: 3/3 passed.
  • Verified: the complete two-LLM/one-tool trace remains correlated under one invoke_agent interaction; configured user attribution remains present when enabled and absent otherwise; default choice-count and sensitive tool payload omissions remain unchanged.
  • Artifact root: .integration-tests/1786679270418

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Re-run on the same head (c559f1dd) — gate results are unchanged from the last pass.

Template ✓ — all required sections present, bilingual body complete.

Problem: confirmed, not theoretical. Every fix traces to an observed problem — the maintainer review findings on #9107 (budget-abort vs. cancellation-finalizer race, swallowed aborts, mixed main/?btw batch ownership, JSON Schema verdict scoping) plus review-thread findings on this PR itself (arena error sanitization, provider-status normalization, ?btw admission fallback, scheduler rejection handling). No speculative hardening.

Direction: inherited from #9107. Main-agent invocation tracing was accepted when #9107 merged with maintainer approvals; this PR repairs defects flagged in that review rather than extending scope. The bounded arena-error reporting is a sanitization improvement, not a direction change.

Size: 684 production-logic lines vs 2037 test lines across 18 files (core telemetry + CLI lifecycle paths). Above the 500-line maintainer-awareness threshold — and the maintainer is now fully engaged: @wenshao ran an independent local A/B verification on real builds and approved this exact commit, so the awareness escalation has been answered rather than left pending.

Approach: every hunk maps to a named fix or a review-thread finding; the re-walk found no unrelated changes or drive-by refactors. One standing hygiene note: the PR body still doesn't mention the arena reporting or status normalization the review rounds added — worth a refresh before merge, not blocking.

Risk: no revert-correlated paths touched; no elevated risk signals.

Moving on to code review. 🔍

中文说明

同一 head(c559f1dd)上的 re-run——gate 结论与上一轮一致。

模板 ✓ —— 所有必需章节齐全,中英文正文完整。

问题:已确认,非理论性问题。 每个修复都对应已观测到的问题——#9107 维护者评审发现(预算 abort 与取消 finalizer 的竞争、被吞掉的 abort、main 与 ?btw 混合批次的 owner 归属、JSON Schema 判定范围),以及本 PR 评审线程中的发现(arena 错误 sanitization、provider 状态归一化、?btw admission 回退、scheduler 拒绝处理)。无投机性加固。

方向:继承自 #9107 主 Agent invocation tracing 已随 #9107 经维护者批准合并;本 PR 修复的是那次评审指出的缺陷而非扩大范围。有界的 arena 错误上报是 sanitization 改进,不构成方向变化。

规模:684 行生产逻辑 vs 2037 行测试,共 18 个文件(核心 telemetry + CLI 生命周期路径)。超过 500 行维护者关注阈值——而维护者现已深度介入:@wenshao 在真实构建上独立运行了本地 A/B 验证,并批准了这个 commit,关注度升级已得到回应而非悬置。

方案: 每个 hunk 都对应已列出的修复或评审线程发现;复查未发现无关改动或顺手重构。一条遗留卫生建议:PR 正文仍未提及评审轮次新增的 arena 上报与状态归一化——合并前值得更新,不构成阻断。

风险: 未触及与 revert 相关的路径;无升级风险信号。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at c559f1dd525703fbb48ba5a2358131f6152520d1 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Code review

Re-walked the full production diff at this head independently (budget-stamp listener ordering, abort-flag bookkeeping around the logging wrapper's completion flag, deferred-batch queue with per-call owners and fail-closed secondary dispatch, start-type-keyed contract). Same verdict as the last pass: no correctness blockers. The pieces that matter most, verified against the code:

Deferred tool-batch drain + continuation lifecycle. Batches completing while a model stream is active are pushed to pendingCompletedToolBatchesRef and drained when activeModelStreamsRef settles to 0; the drain splice(0)s the queue empty before dispatching, so a continuation's own settle cannot re-drain the same batch. Owner selection prefers a tool owned by the live active-interaction owner (liveness re-checked via getActiveInteractionSpan), secondary owners stay fail-closed — recorded and emitted via dual output, never submitted under the primary prompt, interaction ended cancelled only after a fresh liveness check. Duplicate-response batches are filtered to the owning prompt, deferred (non-deduped) tools keep their owner entries until drained, and await submitQuery plus the per-generation submission-activity lease (retainSubmissionActivity/settleSubmissionStateIfIdle) keeps isResponding true until delivery actually finishes. cancelOngoingRequest aborts detached continuation controllers even when the turn is already cancelled, so Esc reaches surviving ?btw continuations; detached continuations also lose the shared steer-input callback (the R6-3 fix).

Budget abort precedence. stampBudgetAbort registers on the run's abort controller with {once: true} before budgetEnforcer.start(), so it fires ahead of the generator's own abort listener; endActiveInteraction is first-wins, making the classification deterministic regardless of interleaving. Listener removed in finally.

Swallowed aborts. Non-stream path: responseCompleted flips before any post-response logging, so an abort arriving after legitimate completion cannot rewrite success; a swallowed abort yields cancelled with the bounded API call aborted message, skips success logging, and finalize(false). Idle-timeout path: span end now distinguishes cancelled / failed / genuinely-idle and carries errorType/errorStatusCode from lastError only when not cancelled; the abort listener is cleaned up inside the timeout callback.

JSON Schema verdict scoping. A WeakMap keyed by interaction owner records the start type once; the missing-output verdict only fires for UserQuery/Retry owners and follows the owner across tool continuations. session-tracing.ts additionally grants the gen_ai.output.type attribute to acp_prompt — the asymmetry is intentional (ACP interactions carry the attribute but aren't subject to the headless verdict), and the /verify run traced the wiring to confirm the two sites agree.

Error sanitization. Mid-turn error events report bounded arena categories derived from the normalized status (401/403 → auth, 429 → rate limit, 5xx → provider unavailable) instead of raw messages, and reportError failure is caught; the span classification moves to a constant low-cardinality api_error instead of leaking SDK constructor names via getErrorType; headless terminal failures keep the bounded span message with the raw message going only to console; the three friendly error classes gain fixed status fields, and turn.ts normalizes status shapes via getErrorStatus before yielding the structured error.

Standing non-blocking items (carried from the last pass, none blocking): the contract predicate is hand-maintained in two places (client.ts vs session-tracing.ts, R7-2 — a shared predicate would prevent drift); getErrorStatus(error) ?? originalStatus in turn.ts is a defensive no-op as written (R7-3); and per @wenshao's verification, the scheduler .catch() guard's test doesn't observe the rejection (passes against the unpatched file) and three new useGeminiStream blocks are characterization tests that also pass on base — worth a test-quality follow-up, not a merge blocker.

Flow: deferred batch drain and continuation submit
sequenceDiagram
    participant P1 as Tool batch completes
    participant P2 as handleCompletedTools
    participant P3 as Pending queue
    participant P4 as Last stream settles
    participant P5 as Owner dispatch
    participant P6 as Continuation submitQuery
    P1->>P2: batch done while streams active
    P2->>P3: defer batch, keep per-call owners
    P4->>P3: drain merged batches (queue spliced first)
    P3->>P2: re-enter dispatch
    P2->>P5: primary owner continues, secondary closed as cancelled
    P5->>P6: tool responses submitted under owning prompt
Loading
Files changed (18)
File What changed
docs/design/telemetry-main-agent-spans-design.md Documents the JSON Schema verdict ownership rule
packages/cli/src/nonInteractiveCli.ts Budget-abort stamping before the finalizer; bounded headless failure message
packages/cli/src/nonInteractiveCli.test.ts Budget-abort race simulation; secret-leak assertion on terminal failure
packages/cli/src/ui/AppContainer.tsx Queues a responding ?btw submission when concurrent admission fails
packages/cli/src/ui/AppContainer.test.tsx Admission-failure queueing tests
packages/cli/src/ui/hooks/useGeminiStream.ts Continuation-owner lifecycle, submission-activity accounting, deferred-batch queue and drain, fail-closed secondary dispatch
packages/cli/src/ui/hooks/useGeminiStream.test.tsx Bulk of the new coverage: deferred batches, mixed owners, detached continuations, Goal finalization
packages/cli/src/ui/hooks/useReactToolScheduler.ts Catches scheduler.schedule rejections instead of dropping them unhandled
packages/cli/src/ui/hooks/useReactToolScheduler.test.tsx Scheduler rejection tests
packages/core/src/core/client.ts Contract gated by interaction start type; Goal finalization message; bounded arena error reporting
packages/core/src/core/client.test.ts Contract scoping matrix; continuation ownership; arena reporting tests
packages/core/src/core/loggingContentGenerator/loggingContentGenerator.ts Swallowed-abort cancellation on non-stream and idle-timeout paths
packages/core/src/core/loggingContentGenerator/loggingContentGenerator.test.ts Swallowed-abort, post-completion-abort, and idle-timeout cancellation tests
packages/core/src/core/turn.ts Normalizes provider error status via getErrorStatus before yielding the structured error
packages/core/src/core/turn.test.ts Friendly forbidden errors keep status; provider status shape normalization
packages/core/src/telemetry/session-tracing.ts gen_ai.output.type only for user-origin interaction types
packages/core/src/telemetry/session-tracing.test.ts Automatic invocation types do not get the JSON contract attribute
packages/core/src/utils/errors.ts Forbidden/Unauthorized/BadRequest errors carry fixed status fields

Testing evidence

Unattended CI run — per the gate rules I do not build or execute PR code; the evidence below is the PR's own CI on the reviewed commit, fetched via API. All checks on this head are complete.

Check Conclusion
Classify PR ✅ success
Dependency CVE audit ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Secret scan (TruffleHog) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
Integration Tests (CLI, No Sandbox) ⏭️ skipped
Test (macos-latest, Node 22.x) ⏭️ skipped
Test (windows-latest, Node 22.x) ⏭️ skipped

The macOS/Windows unit legs and the CLI integration suite are skipped for this fork PR, so the green ubuntu suite alone would be partial evidence — but the behavioural claims no longer rest on it. Two independent verification lanes have since run on this exact head and both substantiate the change:

  • Sandboxed /verify (run 31890697119): ✅ merge-ready. A/B load-bearing proof — the PR's own test files run against the base tree turn 23 core + 11 cli cases red, each mapping 1:1 to a claimed fix; mutation matrix 5/5 killed (removing the budget stamp flips the classification back to cancelled, removing the batch deferral drops the continuations, disabling the secondary-owner split reproduces the ?btw leak); a mock-free headless process against a stalling provider exports the correct run_budget_exceeded span. 3427 scripted assertions, 0 failures.
  • Maintainer local verification by @wenshao (comment below, 2026-08-15): real base-vs-head builds from source with real OTel export. The JSON Schema scoping fix and the ?btw cancellation-ownership fix both reproduce end to end (base: cron drain mislabeled structured_output_missing, side-question span dangling 48.5 s; head: both correct). His probe also independently confirmed the author's "not taken" rationale — a mixed main + ?btw tool batch is not constructible from the TUI because GeminiChat serializes provider streams, so the fail-closed secondary path is defensive code for a production-blocked state. His remaining findings (a, b) are test-quality follow-ups, explicitly non-blocking.

Not verified: Windows behavior (CI leg skipped, /verify container is Linux, author tested macOS only) — same residual gap as #9107. The author's local integration-suite results remain the author's claim; they are corroborated but not replaced by the two lanes above.

中文说明

代码审查

在当前 head 上独立复查了全部生产 diff(预算戳监听器顺序、日志包装层完成标志周围的 abort 记账、按调用捕获 owner 并 fail-closed 分发 secondary 的延迟批次队列、以启动类型为键的 contract)。结论与上一轮一致:未发现正确性阻断问题。关键机制均已对照代码核实:延迟批次 drain 前先 splice(0) 清空队列,不会重复 drain;owner 选择优先存活的主 interaction owner 且二次校验存活;secondary owner 只记录输出、绝不提交到主 prompt;await submitQuery 加按代 submission 租约使 isResponding 到投递完成才释放;cancelOngoingRequest 在 turn 已取消时也 abort detached continuation controller;detached continuation 不再接收共享 steer 输入回调(R6-3 修复)。预算戳以 {once: true} 先于 budgetEnforcer.start() 注册,分类先到先得。被吞掉的 abort 在非流式与 idle-timeout 路径均产生 cancelled 与有界消息,正常完成后的 abort 不改写成功。JSON Schema 判定经 WeakMap 随 owner 跨 continuation,仅 UserQuery/Retry 受约束;acp_prompt 的属性不对称是有意的,/verify 已追线确认两处一致。错误上报全部有界化(arena 分类、常量 api_error、headless 有界消息),三个友好错误类带固定 status。

遗留非阻断项(沿用上轮,均不阻断):contract 谓词在 client.tssession-tracing.ts 两处手工维护(R7-2,共享谓词可防漂移);turn.ts?? originalStatus 是防御性空操作(R7-3);@wenshao 验证发现 scheduler .catch() 保护的测试未观察 rejection(对未打补丁文件同样通过),且三个新 useGeminiStream 块是对 base 也通过的刻画性测试——值得测试质量跟进,不构成合并阻断。

测试证据

无人值守 CI 运行——按 gate 规则不构建、不执行 PR 代码;以上为该 commit 自身 CI 信号(API 获取),所有检查已完成。macOS/Windows 测试腿与 CLI 集成套件对本 fork PR 跳过,仅凭 ubuntu 全绿只是部分证据——但行为性主张已不再依赖它:两条独立验证通道已在同一 head 上运行并证实改动:

  • 沙箱 /verify(run 31890697119):✅ 可合入。 A/B 承重证明——PR 的测试文件在 base 树上使 23 条 core + 11 条 cli 用例变红,与声称的修复一一对应;变异矩阵 5/5 全部击杀;无 mock 的 headless 进程对挂起 provider 导出正确的 run_budget_exceeded span。共 3427 条脚本断言,0 失败。
  • @wenshao 的维护者本地验证(下方评论,2026-08-15):真实源码构建 base 与 head 双臂、真实 OTel 导出。JSON Schema 范围修正与 ?btw 取消归属修复均端到端复现(base:cron drain 误标 structured_output_missing、侧问 span 悬空 48.5 秒;head:两者均正确)。他的探测还独立证实作者 "Not taken" 的论证——GeminiChat 串行化 provider 流,TUI 上构造不出 main + ?btw 混合工具批次,fail-closed secondary 路径是对生产当前不可达状态的防御代码。其余发现 (a)、(b) 为测试质量跟进项,明确非阻塞。

未验证:Windows 行为(CI 腿跳过、/verify 容器为 Linux、作者仅在 macOS 本地测试)——与 #9107 相同的遗留缺口。作者的本地集成套件结果仍为作者陈述,上述两条通道对其形成佐证但不替代。

Qwen Code · qwen3.8-max

Reviewed at c559f1dd525703fbb48ba5a2358131f6152520d1 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean review at this head and both questions the last pass deferred on are now settled by the maintainer; approving with a short list of non-blocking follow-ups.

Stepping back: the reason this run existed is that the last one deferred with two explicit questions for @wenshao — confirm the earlier Criticals about mixed-owner batches were resolved, and sign off the core/telemetry scope at 684 production lines. Both now have answers on the record:

  1. The mixed-owner Criticals are resolved in the author's favor, and not by assertion. @wenshao's local verification probed exactly the contested state from the TUI on both arms and could not construct a batch with two live owners — GeminiChat serializes provider streams, so the fail-closed secondary-owner path is defensive code for a state production blocks. That matches what the author argued in the review threads, and my own walk of the drain/dispatch code found the dangerous variants closed off regardless (no re-drain of the same batch, no submit under a foreign owner, no resurrection after cancel).
  2. The scope sign-off is explicit: a deep base-vs-head verification on real builds, then an approval pinned to this exact commit. The Stage 0 size escalation existed to make sure a maintainer's eye was on a 500+-line core change before this gate would act — that condition is met, so the cap no longer binds this run.

Everything else corroborates: the sandboxed /verify run is merge-ready with A/B load-bearing proof and a 5/5 mutation kill matrix on this head; the PR's own CI is fully green where it runs; my independent diff walk found no correctness blockers. The PR grew eight heads across review rounds but never drifted — every hunk still maps to a named fix or a review-thread finding.

Non-blocking follow-ups named for the record: the structured-output contract predicate is hand-maintained in two places (drift risk, R7-2); the ?? originalStatus fallback in turn.ts is dead weight (R7-3); the scheduler .catch() guard and three new useGeminiStream test blocks are not defended by their tests per the maintainer's verification; the PR body still lags the final diff slightly; and @wenshao's UI observation — a surviving ?btw answer lands in history but isn't rendered into the transcript — is real but out of scope here, worth its own issue.

Approving now, pinned to the reviewed commit — CI is settled on this head. ✅

中文说明

置信度:4/5 —— 当前 head 审查干净,上一轮 defer 的两个问题均已由维护者落定;带着一组非阻断跟进项批准。

退一步看:本轮之所以存在,是因为上一轮带着两个明确问题转交给 @wenshao——确认早前关于混合 owner 批次的 Critical 是否已解决,以及对 684 行生产规模的核心/telemetry 改动签核。两者现在都有在案的答案:

  1. 混合 owner 的 Critical 以有利于作者的方式结案,且不是凭断言。 @wenshao 的本地验证恰好从 TUI 在两臂上探测了争议状态,构造不出含两个存活 owner 的批次——GeminiChat 串行化 provider 流,因此 fail-closed 的 secondary-owner 路径是对生产当前封锁状态的防御代码。这与作者在评审线程中的论证一致;我自己对 drain/dispatch 代码的审视也发现危险变体均已封堵(不重复 drain 同一批、不提交到错误 owner、取消后不复活)。
  2. 范围签核是明确的:真实构建上 base 对 head 的深度验证,然后是钉在这个 commit 上的批准。Stage 0 规模升级的存在意义,就是确保 500+ 行核心改动在本 gate 行动前有维护者过目——该条件已满足,封顶不再约束本轮。

其余证据相互印证:沙箱 /verify 在该 head 上判定可合入,附 A/B 承重证明与 5/5 变异击杀矩阵;PR 自身 CI 在运行范围内全绿;我的独立 diff 复查未发现正确性阻断。PR 历经八轮评审 head 增长但从未漂移——每个 hunk 仍对应一个已列出的修复或评审线程发现。

非阻断跟进项留档:structured-output contract 谓词在两处手工维护(漂移风险,R7-2);turn.ts?? originalStatus 是死代码(R7-3);按维护者验证,scheduler 的 .catch() 保护与三个新 useGeminiStream 测试块未被其测试守住;PR 正文与最终 diff 仍有轻微脱节;@wenshao 的 UI 观察——存活的 ?btw 回答写入历史但未渲染进 transcript——真实存在但不在本 PR 范围,值得单开 issue。

现在批准,钉在所审 commit 上——该 head 的 CI 已尘埃落定。✅

Qwen Code · qwen3.8-max

Reviewed at c559f1dd525703fbb48ba5a2358131f6152520d1 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot 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.

LGTM, looks ready to ship — CI landed green after the review. ✅

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): chunk 2: none — all checks I intended completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": nothing significant. The only checks I did not perform: I did not run the test suite / verify by runtime that the btw-merge drain is reachable in a real session…; "You are review agent reverse-audit — Reverse audit agent…": did not trace whether cancelOngoingRequest aborts a concurrent /btw stream's own controller ( allowConcurrentBtwDuringResponse leaves abortControllerRef …; "You are review agent reverse-audit — Reverse audit agent…": none — all checks I opened were completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": none — all checks above completed within budget., and 3 more.

中文说明

仅完成部分审查,审查缺口已披露。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):chunk 2:none — all checks I intended completed within budget."You are review agent reverse-audit — Reverse audit agent…"nothing significant. The only checks I did not perform: I did not run the test suite / verify by runtime that the btw-merge drain is reachable in a real session…"You are review agent reverse-audit — Reverse audit agent…"did not trace whether cancelOngoingRequest aborts a concurrent /btw stream's own controller ( allowConcurrentBtwDuringResponse leaves abortControllerRef …"You are review agent reverse-audit — Reverse audit agent…"none — all checks I opened were completed within budget."You are review agent reverse-audit — Reverse audit agent…"none — all checks above completed within budget.,另有 3 条。

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): chunk 2: none — all checks I intended completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": nothing significant. The only checks I did not perform: I did not run the test suite / verify by runtime that the btw-merge drain is reachable in a real session…; "You are review agent reverse-audit — Reverse audit agent…": did not trace whether cancelOngoingRequest aborts a concurrent /btw stream's own controller ( allowConcurrentBtwDuringResponse leaves abortControllerRef …; "You are review agent reverse-audit — Reverse audit agent…": none — all checks I opened were completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": none — all checks above completed within budget., and 3 more.

中文说明

仅完成部分审查,审查缺口已披露。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):chunk 2:none — all checks I intended completed within budget."You are review agent reverse-audit — Reverse audit agent…"nothing significant. The only checks I did not perform: I did not run the test suite / verify by runtime that the btw-merge drain is reachable in a real session…"You are review agent reverse-audit — Reverse audit agent…"did not trace whether cancelOngoingRequest aborts a concurrent /btw stream's own controller ( allowConcurrentBtwDuringResponse leaves abortControllerRef …"You are review agent reverse-audit — Reverse audit agent…"none — all checks I opened were completed within budget."You are review agent reverse-audit — Reverse audit agent…"none — all checks above completed within budget.,另有 3 条。

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/nonInteractiveCli.ts Outdated
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts Outdated
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts Outdated
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review feedback batch c71b6a9

Review item Decision
Secondary-owner response history Not taken: production GeminiChat serializes provider streams and repairs a prior dangling call before this mixed-owner state can occur; appending after an intervening model turn would risk corrupting history order.
Raw headless exception in telemetry Fixed: export a stable generic description and retain detail locally.
Raw Goal finalization error in telemetry Fixed: export a stable generic description and retain detail in the local fail-closed path.
Deferred ToolResult admission race Fixed: keep Responding/global admission held through the drain and await the owned ToolResult continuation; plain UserQuery and legacy ?btw remain blocked until completion.

Verification: 343 CLI tests passed (1 skipped), CLI typecheck passed, focused lint passed, full build and bundle passed, and scheduler queue/finalization checks passed.

@wenshao wenshao 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.

Partially reviewed — gaps disclosed.

Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.

[Critical] R2-7 Idle timeout ends the span but leaves the abort listener attached for an abandoned stream. If a consumer leaves a pending next() on a hanging stream and never calls return(), the timeout ends telemetry but generator finally never runs, so a long-lived AbortSignal retains markStreamAborted and the generator state indefinitely. Witness: the PR-code probe ended the span with removeEventListener calls = 0; removing the listener in the timeout callback changed the count to 1. Remove markStreamAborted in the idle-timeout callback as well as in finally.

中文说明

仅完成部分审查,审查缺口已披露。

未审查:反向审计——在 5 轮的反审轮数上限内未收敛。

[Critical] R2-7 Idle timeout ends the span but leaves the abort listener attached for an abandoned stream. If a consumer leaves a pending next() on a hanging stream and never calls return(), the timeout ends telemetry but generator finally never runs, so a long-lived AbortSignal retains markStreamAborted and the generator state indefinitely. Witness: the PR-code probe ended the span with removeEventListener calls = 0; removing the listener in the timeout callback changed the count to 1. Remove markStreamAborted in the idle-timeout callback as well as in finally.

— gpt-5.6-sol via Qwen Code /review (v0.21.11)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/loggingContentGenerator/loggingContentGenerator.ts Outdated
Comment thread packages/core/src/core/client.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/nonInteractiveCli.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Addressed the latest review batch in 116cd7a510.

Item Decision Outcome
R1-1 secondary response persistence Not taken The mixed live-owner batch bypasses production GeminiChat serialization, dangling-call repair, and history dedup; appending the response would misorder shared history.
R2-1 dead/ownerless secondary classification Not taken The state is blocked by the same production invariants, and retaining it as primary would leak another owner into the main continuation.
R2-2 Arena error classification Fixed Arena now receives bounded secret-safe status categories; span metadata stays generic.
R2-3 secondary terminal status Not taken It depends on the production-unreachable mixed-owner branch; future support would require per-owner aggregation.
R2-4 swallowed-abort response telemetry Fixed Suppresses success response/OpenAI logs for both non-stream and streaming cancellation outcomes.
R2-5 automatic JSON output declaration Fixed Limits gen_ai.output.type=json to UserQuery, Retry, and ACP prompt contract owners.
R2-6 render-phase callback publication Fixed Publishes the callback ref from useLayoutEffect.
R2-7 idle-timeout abort-listener retention Fixed Removes the listener when the timeout closes an abandoned stream.
R2-8 provider error overwritten by abort Fixed Preserves the observed provider error and safe classification.
R2-9 Retry test expansion Deferred Test-only suggestion after the repository review-round threshold.
R2-10 Arena regression coverage Addressed Covers the safe Arena value, raw-secret exclusion, and sanitized span metadata.
R2-11 successful mixed-owner test Deferred Test-only suggestion for a mock-only state after the review-round threshold.
R2-12 non-Error rejection test Deferred Test-only suggestion after the review-round threshold.

Verification on the exact commit: build, bundle, full workspace typecheck, 594 affected core tests, and 217 useGeminiStream tests all passed. Independent test-engineer reproduction also confirmed the six accepted Critical findings and the production unreachability of the three secondary-owner findings.

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed.

Unresolved, please confirm:

  • [Critical] useGeminiStream.ts:4173 — dead-owner/ownerless secondary classification (this account's R1-1; ci-bot threads, posted twice): the non-live ownerForToolCall predicate is unchanged at HEAD and would drop a dead-owner or ownerless tool's successful result from a mixed batch; whether a mixed live/dead-owner batch is reachable in production (shared GeminiChat serialization + orphan-repair contracts, which this round's auditors could neither construct nor fully verify) could not be determined from the code
  • [Critical] useGeminiStream.ts:4173 — R2-1 (classify secondary tools by live owners): same predicate and same contested reachability as above; could not be determined
  • [Critical] useGeminiStream.ts:4194 — the secondary owner's real function response is marked submitted and filtered out before finalizeToolResponses/addHistory, leaving a dangling call that orphan repair replaces with a synthesized error; the author declines the change citing production serialization contracts that this round could not independently verify; could not be determined
  • [Critical] useGeminiStream.ts:4197 — R1-1 (persist the secondary owner's response before removal): same mechanism and contested reachability as the :4194 thread; could not be determined
  • [Critical] useGeminiStream.ts:4206 — R2-3 (every secondary interaction ends as 'cancelled' regardless of owned-tool status, where the primary path uses continuation_tool_error): the unconditional loop stands at HEAD, but it only fires inside the mixed-owner batch whose production reachability could not be determined

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks above completed within budget. The only thing I deliberately did not walk is the useGeminiStream.ts production hunks (diff lines 545–845: dr…; "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks I started were completed within budget (~20 tool calls).; "This PR is a follow-up to #9107 fixing review-confirmed…": did not trace the consumer-side UX of onAdmissionFailed / onDeliveryFailed for user-typed input rejected during the drain window (input restore semantics).; "This PR is a follow-up to #9107 fixing review-confirmed…": did not walk the notification/teammate batch-drain effect bodies at 5267/5448 (f1 references them; they sit outside my diff range).; "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks above completed within budget., and 3 more.

中文说明

仅完成部分审查,审查缺口已披露。

未决,请确认:共 5 条(原文未翻译,列表见上方英文部分)。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):"This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks above completed within budget. The only thing I deliberately did not walk is the useGeminiStream.ts production hunks (diff lines 545–845: dr…"This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks I started were completed within budget (~20 tool calls)."This PR is a follow-up to #9107 fixing review-confirmed…"did not trace the consumer-side UX of onAdmissionFailed / onDeliveryFailed for user-typed input rejected during the drain window (input restore semantics)."This PR is a follow-up to #9107 fixing review-confirmed…"did not walk the notification/teammate batch-drain effect bodies at 5267/5448 (f1 references them; they sit outside my diff range)."This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks above completed within budget.,另有 3 条。

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts Outdated
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.test.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Pushed review follow-up 002db6fe5d.

Review item Decision Resolution
R2-1 submission lease race Fixed Added generation-scoped activity ownership spanning scheduler completion, admission waits, model streams, and deferred finalization/drain. Stale cancelled generations cannot clear replacement state.
R2-3 abort-shaped timeout test Deferred Test-only suggestion; existing classification logic was verified and this PR is beyond the review-round threshold.
R2-4 mixed-owner duplicate test Deferred Test-only and depends on a production-blocked mixed physical-stream path.
R2-5 Arena category matrix Deferred Test-only; the mapping and secret-safe path were verified.

Validation: 222/222 hook tests, focused ESLint, CLI typecheck, repository build, and bundle all pass. Two consecutive broad diff audits found no further actionable correctness issue. Replied to and resolved 4/4 newly surfaced review threads.

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed.

Unresolved, please confirm:

  • [Critical] useGeminiStream.ts:4205 (thread 3781921163) — dead-owner/ownerless secondary-tool classification drops successful results from a mixed batch; the mechanism stands at HEAD, the author declines it as intentional fail-closed, and the production reachability of a mixed live/dead-owner batch (shared GeminiChat serialization + orphan-repair contracts) could not be determined from the code
  • [Critical] useGeminiStream.ts:4205 (thread 3781923797) — same classification claim (posted twice); reachability likewise could not be determined
  • [Critical] useGeminiStream.ts:4205 (thread 3783065821, R2-1) — classify secondary tools by live owners; same predicate and same contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4226 (thread 3782611141) — the secondary owner's real function response is marked submitted and filtered out before finalizeToolResponses/addHistory, leaving a dangling call that orphan repair replaces with a synthesized error; the author's production-serialization rationale could not be independently verified — could not be determined
  • [Critical] useGeminiStream.ts:4229 (thread 3783065817, R1-1) — persist the secondary owner's response before removal; same mechanism and contested reachability as the :4226 thread — could not be determined
  • [Critical] useGeminiStream.ts:4238 (thread 3783065832, R2-3) — every secondary interaction ends as 'cancelled' regardless of owned-tool status (the primary path uses continuation_tool_error); the unconditional loop stands at HEAD but only fires inside the mixed-owner batch whose production reachability could not be determined

Not reviewed: reverse audit — stopped at the 5-round cap without two consecutive dry rounds (round 4 reported findings; round 5 dry across all audited chunks; chunk 1 retired after dry rounds 3-4).

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks above completed within budget.; "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks above completed; I verified statically by tracing the mocks and source rather than executing the vitest suite.; "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks above completed within budget.; "This PR is a follow-up to #9107 fixing review-confirmed…": none — all checks above completed within budget.; chunk 1: none — all checks I intended completed within budget. I did not run a full npm run typecheck , but the only production-logic type surface in my chunk is the tr…, and 12 more.

中文说明

仅完成部分审查,审查缺口已披露。

未决,请确认:共 6 条(原文未翻译,列表见上方英文部分)。

未审查:reverse audit — stopped at the 5-round cap without two consecutive dry rounds (round 4 reported findings; round 5 dry across all audited chunks; chunk 1 retired after dry rounds 3-4)。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):"This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks above completed within budget."This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks above completed; I verified statically by tracing the mocks and source rather than executing the vitest suite."This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks above completed within budget."This PR is a follow-up to #9107 fixing review-confirmed…"none — all checks above completed within budget.;chunk 1:none — all checks I intended completed within budget. I did not run a full npm run typecheck , but the only production-logic type surface in my chunk is the tr…,另有 12 条。

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/nonInteractiveCli.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Handled the latest review batch in commit 2ede401949.

Review item Decision Result
R3-1: ?btw dropped during tool execution Fixed Failed concurrent admission now queues the message for idle delivery.
R3-2: Arena auth category unreachable for real 401/403 Fixed Friendly HTTP errors retain status; Unauthorized rethrows report the bounded authentication category without raw data.
R3-3: stale ?btw stream allows premature Idle Fixed Submission state settles only after current-generation activity and all model streams are both finished.
R3-6, R3-7, R3-8, R3-10, R3-11 Deferred Test-only coverage suggestions; deferred under the repository rule for PRs beyond roughly five review rounds.
R3-9 Deferred Non-Critical telemetry-attribution refinement; deferred under the late-round Critical-only rule.
R3-12 Deferred Comment-only clarification; runtime semantics were verified correct.

Validation: affected full unit files passed (CLI 367/367, core 379/379), focused ESLint passed, root typecheck passed, and build + bundle passed. Two post-change diff audits and independent test-engineer verification found no additional actionable correctness issue.

@wenshao wenshao 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.

Partially reviewed — gaps disclosed.

Unresolved, please confirm:

  • [Critical] comment 3781921163 (packages/cli/src/ui/hooks/useGeminiStream.ts:4213) — mixed live/dead-owner classification remains production-reachability disputed
  • [Critical] comment 3782611141 (packages/cli/src/ui/hooks/useGeminiStream.ts:4234) — successful-secondary response persistence remains production-reachability disputed
  • [Critical] comment 3783065832 (packages/cli/src/ui/hooks/useGeminiStream.ts:4246) — unconditional secondary cancellation telemetry remains production-reachability disputed

Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — local verification ran on Node 24.18.1 rather than the repository-required Node 22.

中文说明

仅完成部分审查,审查缺口已披露。

未决,请确认:共 3 条(原文未翻译,列表见上方英文部分)。

未审查:reverse audit — did not converge within the reverse-audit round cap of 5。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未审查:build-and-test — local verification ran on Node 24.18.1 rather than the repository-required Node 22。

— gpt-5.6-sol via Qwen Code /review (v0.21.11)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts Outdated
Comment thread packages/cli/src/ui/AppContainer.tsx
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts Outdated
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed. Suggestions are inline.

Unresolved, please confirm:

  • [Critical] useGeminiStream.ts:4213 (thread 3781921163) — dead-owner/ownerless secondary-tool classification drops successful results from a mixed batch; mechanism unchanged at HEAD, author declines as intentional fail-closed; production reachability of a mixed live/dead-owner batch could not be determined from the code
  • [Critical] useGeminiStream.ts:4213 (thread 3781923797) — same classification claim (posted twice); reachability likewise could not be determined
  • [Critical] useGeminiStream.ts:4213 (thread 3783065821, R2-1) — classify secondary tools by live owners; same predicate and same contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4234 (thread 3782611141) — the secondary owner's function response is filtered before finalizeToolResponses/addHistory leaving a dangling call; the author's production-serialization rationale could not be independently verified — could not be determined
  • [Critical] useGeminiStream.ts:4237 (thread 3783065817, R1-1) — persist the secondary owner's response before removal; same mechanism and contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4246 (thread 3783065832, R2-3) — every secondary interaction ends as 'cancelled' regardless of owned-tool status; the loop stands at HEAD but only fires inside the mixed-owner batch whose reachability could not be determined
  • [Critical] useGeminiStream.ts:4134 (thread 3786904525, R3-13 — posted after this review's context fetch) — deferred batches carry no submission-generation identity; this run's admission-gate enumeration blocks the claimed stale-generation interleaving (only /btw is admissible in the cancel window and it preserves the cancel refs), but the filed probe contests that — could not be determined
  • [Critical] useGeminiStream.ts:4213 (thread 3786904531, R3-14 — posted after this review's context fetch) — with telemetry disabled interactionOwner is absent, so concurrent main/?btw results are not partitioned; the mechanism is present but telemetry-off behavior matches pre-PR, and whether this PR must partition without telemetry is a maintainer scope call — could not be determined
  • [Critical] AppContainer.tsx:2606 (thread 3786904539, R3-16 — posted after this review's context fetch) — a re-queued ?btw drains as a plain UserQuery, losing the isolated BTW routing; this run independently traced the same behavior (it matches idle-time ?btw semantics) — whether that is acceptable is a design call — could not be determined
  • [Critical] client.ts:3434 (thread 3786904543, R3-17 — posted after this review's context fetch) — top-level-only .status extraction misses statusCode/response.status/SSE-text status shapes; the extraction is pre-existing in unchanged turn.ts and pre-PR arena received 'Unknown error' for all of these — whether this PR must normalize could not be determined
  • [Critical] useGeminiStream.ts:4134 (thread 3786904546, R4-18 — posted after this review's context fetch) — a completed deferred ?btw batch is discarded when only the main response is cancelled; the mechanism matches this run's trace of the cancel gate, and whether discarding completed side-interaction results is intended fail-closed behavior could not be determined
  • [Critical] client.ts:3445 (thread 3786904551, R4-19 — posted after this review's context fetch) — the awaited arena reportError in the Error-event path can turn a gracefully-yielded provider error into a thrown stream failure; this run's verifier determined the await predates the diff at the merge base — whether this PR must fix a pre-existing hazard could not be determined
  • [Critical] useGeminiStream.ts:3881 (thread 3786904554, R5-20 — posted after this review's context fetch) — the deferred drain and scheduler-driven dispatch can both enter handleCompletedTools for separate batches while zero streams are active; not independently probed by this review and GeminiChat send-serialization may or may not neutralize it — could not be determined

Not reviewed: reverse audit — stopped at the 5-round cap without two consecutive dry rounds (rounds 4 and 5 both reported findings).

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): "agent reverse-audit (round 3)": whether headless nonInteractiveCli.ts's structured_output_missing endActiveInteraction (~line 2978) can stamp a drain-item (Notification/Teammate/Cron) interact…; "agent reverse-audit (round 3)": whether headless endActiveInteraction('error', … 'structured_output_missing') at nonInteractiveCli.ts:~2978 can stamp a drain-item (Notification/Teammate/Cron….

中文说明

仅完成部分审查,审查缺口已披露。 建议见行内评论。

未决,请确认:共 13 条(原文未翻译,列表见上方英文部分)。

未审查:reverse audit — stopped at the 5-round cap without two consecutive dry rounds (rounds 4 and 5 both reported findings)。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 3)"whether headless nonInteractiveCli.ts's structured_output_missing endActiveInteraction (~line 2978) can stamp a drain-item (Notification/Teammate/Cron) interact…"agent reverse-audit (round 3)"whether headless endActiveInteraction('error', … 'structured_output_missing') at nonInteractiveCli.ts:~2978 can stamp a drain-item (Notification/Teammate/Cron…

— qwen3.8-max via Qwen Code /review (v0.21.12)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review follow-up pushed in 06ef3cb.

Review item Decision Action
R3-15 / R4-19 Arena reporting failures Fixed Made authentication and provider-error status reporting best-effort so persistence failures cannot replace the original UnauthorizedError or yielded provider Error outcome.
R3-17 provider status shapes Fixed Preserved shared getErrorStatus normalization across friendly-error conversion for statusCode, response.status, nested codes, and SSE HTTP_STATUS values.
R4-18 cancelled main vs legacy ?btw tool Fixed Added telemetry-independent physical-submission prompt/signal ownership; history repair/dedup now happens before selecting the surviving continuation owner, so the detached ?btw result continues under its own prompt.
R3-13 stale-generation drain Not taken The probe bypasses production GeminiChat send serialization and inline orphan repair/history dedup; a later send repairs the old function call before a stale result can be wired.
R3-14 telemetry-disabled mixed batch Not taken The same serialized-send and repair/dedup path prevents the claimed mixed live batch; adding telemetry-independent partition architecture for that mock-only state would be speculative.
R3-16 queued ?btw routing Not taken Legacy ?btw is intentionally the shared-chat UserQuery/model path; /btw is the separate tool-free fork, so the proposed reroute would change the established contract.
R5-20 concurrent non-dedup drains Not taken Production serialization plus inline repair means the two non-dedup batches required by the probe cannot coexist.
R4-2 / R4-3 / R4-4 test suggestions Deferred This PR is past the repository review-round limit; these are test-only suggestions without a demonstrated current production defect.
R4-5 fire-and-forget continuation Not taken Awaiting continuation delivery is required to retain the submission lease and propagate failures; recursive tool batches are safely queued by the scheduler.

Verification: CLI useGeminiStream 224/224; core Turn 41/41; core client 343/343; changed-file Prettier and ESLint; core and CLI typecheck; full build and bundle. The mixed Race A regression was also mutation-checked: capturing the main owner before dedup makes the new test fail at the missing ?btw ToolResult continuation.

Resolved-thread target: all 12 replied threads. Decisions needed: none.

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed.

Unresolved, please confirm:

  • [Critical] useGeminiStream.ts AppContainer queue path (thread 3786904539, R3-16) — a re-queued ?btw drains as a plain UserQuery, losing isolated BTW routing; whether that matches the intended legacy ?btw contract is a design call that could not be settled from the code
  • [Critical] useGeminiStream.ts:3923 (thread 3786904554, R5-20) — the deferred drain and scheduler-driven dispatch can both enter handleCompletedTools for separate batches while zero streams are active; production reachability under GeminiChat send serialization could not be determined
  • [Critical] useGeminiStream.ts:4176 (thread 3786904525, R3-13) — deferred batches carry no submission-generation identity; whether a stale-generation batch can be drained after a superseding turn could not be determined from the code
  • [Critical] useGeminiStream.ts:4272 (thread 3781921163) — dead-owner/ownerless secondary classification drops successful results from a mixed batch; production reachability of such a batch could not be determined
  • [Critical] useGeminiStream.ts:4272 (thread 3781923797) — same classification claim (posted twice); reachability likewise could not be determined
  • [Critical] useGeminiStream.ts:4272 (thread 3783065821, R2-1) — classify secondary tools by live owners; same predicate and contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4272 (thread 3786904531, R3-14) — with telemetry disabled, concurrent main/?btw results are not partitioned; whether this PR must partition without telemetry is a maintainer scope call — could not be determined
  • [Critical] useGeminiStream.ts:4293 (thread 3782611141) — the secondary owner's function response is filtered before finalizeToolResponses/addHistory leaving a dangling call; the author's production-serialization rationale could not be independently verified — could not be determined
  • [Critical] useGeminiStream.ts:4296 (thread 3783065817, R1-1) — persist the secondary owner's response before removal; same mechanism and contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4308 (thread 3783065832, R2-3) — every secondary interaction ends as 'cancelled' regardless of owned-tool status; the loop stands at HEAD but only fires inside the mixed-owner batch whose reachability could not be determined

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): "agent reverse-audit (round 2)": verifying whether the currently-running batch's allToolCallsComplete still fires after an abort-time splice rejects a queued request (coreToolScheduler's cancel…; "agent reverse-audit (round 2)": full cascade of a queue-abort reject in CoreToolScheduler (whether the in-flight batch's allToolCallsCompleteHandler still fires correctly afterward and wha…; "agent reverse-audit (round 2)": I did not trace every caller that passes options.abortController into runNonInteractive to rule out a pre-registered, span-ending abort listener beating st…; "agent reverse-audit (round 3)": none — I completed every check I started (full-chunk read, harness read, wiring verification, and the live test run) within budget..

中文说明

仅完成部分审查,审查缺口已披露。

未决,请确认:共 10 条(原文未翻译,列表见上方英文部分)。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 2)"verifying whether the currently-running batch's allToolCallsComplete still fires after an abort-time splice rejects a queued request (coreToolScheduler's cancel…"agent reverse-audit (round 2)"full cascade of a queue-abort reject in CoreToolScheduler (whether the in-flight batch's allToolCallsCompleteHandler still fires correctly afterward and wha…"agent reverse-audit (round 2)"I did not trace every caller that passes options.abortController into runNonInteractive to rule out a pre-registered, span-ending abort listener beating st…"agent reverse-audit (round 3)"none — I completed every check I started (full-chunk read, harness read, wiring verification, and the live test run) within budget.

— qwen3.8-max via Qwen Code /review (v0.21.12)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/core/src/core/client.ts
Comment thread packages/core/src/core/client.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Implemented the actionable Critical feedback in ffd462fe5e.

Discovered issues and fixes:

  • Queued tool scheduling cancellations could reject a fire-and-forget Promise and surface as an unhandled rejection. The React scheduler boundary now consumes expected abort rejections and logs unexpected scheduling failures.
  • A legacy ?btw tool chain that survived the first foreground cancellation could not be cancelled by a later Esc. Detached cancellation ownership now follows recursive tool continuations, while foreground and client-initiated tool scopes are retained and released at their exact terminal or repair-dedup boundary.
  • Broad post-fix audit also closed two reachable symmetric windows: a completed replacement no longer consumes the Esc intended for a surviving detached tool, and client-initiated schedule_tool work remains cancellable after prompt preprocessing returns.

Rejected or deferred:

  • Deferred the secondary-owner dual-output Suggestion because the mixed physical completion batch is not production-reachable under GeminiChat serialization plus inline orphan repair/history dedup.
  • Deferred the Hook/Steer test-only Suggestion under the late-round Critical-only rule.
  • Rejected adding raw Arena rejection causes because they can expose credentials, request URLs, or prompt data; the bounded diagnostic is intentional.

Verification:

  • Complete changed CLI test files: 232/232 passed.
  • Additional combined Critical regressions and Core finalization queue tests passed under independent test-engineer verification.
  • Changed-file ESLint, full workspace typecheck, build, bundle, formatting, and git diff --check passed.
  • Two consecutive final self-audit passes and an independent open-ended audit found no remaining production-reachable correctness issue.

Decisions needed: none.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Resolved 5/5 handled review threads for this fix batch.

@doudouOUC
doudouOUC requested a review from wenshao August 15, 2026 03:07

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed.

Unresolved, please confirm:

  • [Critical] useGeminiStream.ts:4176 (thread 3786904525, R3-13) — deferred batches carry no submission-generation identity; whether a stale-generation batch can be drained after a superseding turn could not be determined from the code (author: the probe bypasses production GeminiChat send serialization and orphan repair)
  • [Critical] useGeminiStream.ts:3923 (thread 3786904554, R5-20) — the deferred drain and scheduler-driven dispatch can both enter handleCompletedTools for separate batches while zero streams are active; production reachability under GeminiChat send serialization could not be determined (author declined)
  • [Critical] useGeminiStream.ts:4272 (thread 3781921163) — dead-owner/ownerless secondary classification drops successful results from a mixed batch; production reachability of such a batch could not be determined (author: intentional fail-closed)
  • [Critical] useGeminiStream.ts:4272 (thread 3781923797) — same classification claim (posted twice); reachability likewise could not be determined
  • [Critical] useGeminiStream.ts:4272 (thread 3783065821, R2-1) — classify secondary tools by live owners; same predicate and contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4272 (thread 3786904531, R3-14) — with telemetry disabled, concurrent main/?btw results are not partitioned; whether this PR must partition without telemetry is a maintainer scope call — could not be determined
  • [Critical] useGeminiStream.ts:4293 (thread 3782611141) — the secondary owner's function response is filtered before finalizeToolResponses/addHistory leaving a dangling call; the author's production-serialization rationale could not be independently verified — could not be determined
  • [Critical] useGeminiStream.ts:4296 (thread 3783065817, R1-1) — persist the secondary owner's response before removal; same mechanism and contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4308 (thread 3783065832, R2-3) — every secondary interaction ends as 'cancelled' regardless of owned-tool status; the loop stands at HEAD but only fires inside the mixed-owner batch whose reachability could not be determined
  • [Critical] AppContainer.tsx:2606 (thread 3786904539, R3-16) — a re-queued ?btw drains as a plain UserQuery, losing isolated BTW routing; whether that matches the intended legacy ?btw contract is a design call that could not be settled from the code

Not reviewed: reverse audit — stopped at the 5-round cap without two consecutive dry rounds (round 5 reported findings; all nine chunks audited every round, chunk 3 cold-checked in round 4).

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

仅完成部分审查,审查缺口已披露。

未决,请确认:共 10 条(原文未翻译,列表见上方英文部分)。

未审查:reverse audit — stopped at the 5-round cap without two consecutive dry rounds (round 5 reported findings; all nine chunks audited every round, chunk 3 cold-checked in round 4)。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max via Qwen Code /review (v0.21.12)

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/hooks/useReactToolScheduler.ts
Comment thread packages/cli/src/ui/hooks/useReactToolScheduler.test.tsx
Comment thread packages/core/src/core/client.test.ts
Comment thread packages/core/src/core/client.test.ts
Comment thread packages/core/src/core/client.test.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

doudouOUC commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Review follow-up for c559f1d:

Item Decision Action
R6-1 mixed-owner continuation selection Pushed back Production sends on the shared GeminiChat are serialized, and inline orphan repair plus history dedup removes the earlier owner before this mocked mixed state can reach continuation selection.
R6-2 parked duplicate response after sibling dedup Pushed back Real duplicate calls use distinct internal IDs, and inline repair responds to both IDs in the model turn; flushing the parked response afterward would duplicate an already-repaired ToolResult.
R6-3 detached continuation steer drain Fixed Detached ToolResult streams no longer receive the shared getSteerInput callback, so side-question continuations cannot consume main-turn queued input.
R6-4 non-abort scheduler rejection recovery Deferred Defensive-only behavior expansion; not a demonstrated production Critical after more than five review rounds.
R6-5 and R6-6 test-strengthening suggestions Deferred Test-only Suggestions are left for follow-up under the late-round Critical-only rule.

Validation: full useGeminiStream.test.tsx (226/226), CLI typecheck, CLI build, changed-file ESLint, root build, and bundle all passed. The new detached-steer test is load-bearing: removing the guard makes it fail deterministically.

@qwen-code-ci-bot qwen-code-ci-bot 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.

Partially reviewed — gaps disclosed. Suggestions are inline.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not reviewed: reverse audit — stopped at the 5-round cap without two consecutive dry rounds (round 5 reported findings; all nine chunks audited every round).

中文说明

仅完成部分审查,审查缺口已披露。 建议见行内评论。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

未审查:reverse audit — stopped at the 5-round cap without two consecutive dry rounds (round 5 reported findings; all nine chunks audited every round)。

— qwen3.8-max via Qwen Code /review (v0.21.12)

Comment thread packages/core/src/core/turn.ts
Comment thread packages/core/src/telemetry/session-tracing.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 3427 passed · 0 failed · 3427 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:3427 通过 · 0 失败 · 3427 总计

Verification report

PR 9121 Deep Verification — fix(telemetry): Address main agent tracing edge cases

Verdict: merge-ready — 3427 scripted assertions executed, 3427 pass, 0 fail.
Verified head: c559f1dd525703fbb48ba5a2358131f6152520d1 (git rev-parse HEAD^2), merged over base tip c396fe3d12db4ee0683209578d9fce2b3a96b94f (HEAD^1).
Environment: CI merge-ref checkout (depth 2), node:22-bookworm container, Node v22.23.2. All runs on Linux.

中文摘要

结论:merge-ready — 共执行 3427 条脚本化断言,全部通过,0 失败。

A/B 结论:在 base worktree(HEAD^1 = c396fe3)中运行本 PR 的全部新增/修改测试:core 侧 23 个用例变红(head 侧 638 全绿),cli 侧 11 个用例变红(head 侧 503 绿 + 1 个预期 skip)。变红的用例与 PR 声称修复的行为一一对应:预算 abort 分类(run_budget_exceeded vs cancelled)、被 provider 吞掉 abort 后的 cancellation 语义、自动 invocation(cron/notification/teammate/goal)不再误标 structured_output_missing、TUI 延迟工具批次的精确 owner 归属。base 自身测试在 base 上全绿(A/A 对照,core 616 + cli 490),证明环境干净。

进程级验证:用真实 headless 进程 + 本地"永不响应"的 OpenAI 兼容假服务 + 6 秒墙钟预算,验证导出的遥测 span:head 侧 interaction span 以 ERROR 状态、run_budget_exceeded(低基数 error.type)和完整的预算诊断消息结束,退出码 55。base 侧对照单元在实际进程里落入了无竞争交错(routeAbort 先行盖章),未能复现误分类——该竞争由单测通过同步 abort listener 确定性地复现(M3 变异验证)。

变异矩阵(5/5 全部击杀):删除 JSON Schema 契约门控 → 5 红且 userQuery 对照保持绿;关闭非流式 abort 跟踪 → 恰好 1 红(后续 abort 与流侧测试仍绿,粒度精确);删除预算盖章 listener → 1 红且展示 cancelled vs error+run_budget_exceeded 的完整翻转;删除批次延迟入队 → 5 红(批次被丢弃、continuation 永不提交);禁用 secondary-owner 拆分 → 恰好混合 ?btw 泄漏测试变红。

Findings:无阻塞项。两条信息性说明见正文(baseRefOid 漂移与浅克隆按 commit 归因不可达;live 进程交错无法复现该竞争)。

未覆盖:仓库级套件/lint/typecheck(PR 自身 CI 覆盖);Windows 行为(本容器为 Linux,全部绿色可作为 Linux 证据);workflow invocation/dispatch tracing(PR 明示范围外);浅克隆下 8 个 commit 的逐一归因(仅聚合 diff 可达)。

Scope selection

  • Central claim (C1) — abort/budget classification in tracing: wall-time budget aborts are stamped error + run_budget_exceeded before the core generator can close the interaction as a user cancellation; swallowed aborts (non-stream calls and idle-timed-out hanging streams) keep cancellation semantics; aborts that fire only after provider completion do not rewrite success; error telemetry stays bounded/low-cardinality.
  • Secondary (C2) — TUI deferred tool batches: batches completing while another model stream is active are deferred until all streams settle, retain their exact interaction owners, and mixed main + legacy ?btw batches cannot leak tool results into the wrong continuation.
  • Secondary (C3) — headless JSON Schema verdict scoping: the structured-output contract belongs to user-origin (UserQuery/Retry) invocations only; automatic Cron/Notification/Teammate/Goal invocations completing with plain text are not mislabeled structured_output_missing.

A/B load-bearing proof

Identical test files executed against head sources and against a base worktree at HEAD^1; the base side was A/A-controlled first (base's own tests on base: green). The lockfile is untouched by this PR, so shared node_modules is a clean control; internal @qwen-code/* resolution was kept on the base tree's own sources (cli vitest aliases map @qwen-code/qwen-code-core and all internal subpaths relative to the invoking tree; readlink -f confirmed the nested node_modules/ajv etc. resolve to the shared third-party store, and the base cli run resolves core through its own tree — see Methodology).

cell tree oracle result
A/A control, core base tests on base vitest exit 616 pass, 0 fail
A/A control, cli base tests on base vitest exit 490 pass (+1 skip), 0 fail
A/B core PR tests on base vitest exit 615 pass, 23 RED (head: 638 pass, 0 fail)
A/B cli PR tests on base vitest exit 492 pass (+1 skip), 11 RED (head: 503 pass +1 skip, 0 fail)

Every base-side red is a behavioral assertion mismatch (spy-call / object-shape), not an import or collection error, and maps 1:1 onto a claimed fix:

  • core, client.test.ts (9): structured-output contract not assigned to cron/notification/teammate invocations, Goal starts fresh without the contract, notification ownership preserved across a tool continuation, safe Arena error categories, Arena-report-failure resilience, auth-failure reporting to Arena, Goal-finalization vs hook-failure attribution. Base e.g. reports "Unknown error" to Arena where head reports "Rate limit exceeded".
  • core, loggingContentGenerator.test.ts (6): swallowed-abort cancellation for non-stream and hanging streams, post-completion abort not rewriting success, success-log skip on idle timeout, abort-races-error-logging preservation. Base e.g. ends the stream span with "Stream span timed out (idle)" where head ends it cancelled + API call aborted.
  • core, turn.test.ts (4): provider status-shape normalization ×3, friendly-forbidden status preservation (base loses the 403 once toFriendlyError rewraps).
  • core, session-tracing.test.ts (4): cron/notification/teammate/goal interactions get no gen_ai.output.type=json.
  • cli, useGeminiStream.test.tsx (8): deferred main-tool-batch continuation without leaking the mixed ?btw owner, legacy ?btw tool continuation after a repaired cancelled main batch, submission-lease survival across four deferred-drain interleavings, responding-state survival for a surviving ?btw stream, steer-queue isolation for detached continuations. Base fails these with expected "spy" to be called once, but got 0 times — the batch is dropped.
  • cli, nonInteractiveCli.test.ts (1) + AppContainer.test.tsx (2): budget-error stamping when the in-flight stream rejects after abort; /btw admission-failure requeue.

Witnesses: evidence/02-ab-core-pr-tests-on-base-23-red.png, evidence/03-ab-cli-pr-tests-on-base-11-red.png.

Wire-oracle harness H1 — budget abort in a real headless process

Mock-free process harness (harness/budget-abort-harness.mjs + harness/stall-server.mjs): the real CLI runs from source (scripts/dev.js) against a loopback OpenAI-compatible server that accepts the chat-completion request and never responds; --max-wall-time 6 aborts the in-flight request; the oracle is the exported telemetry span file (FileSpanExporter JSON), not a mock.

arm exit interaction span status span message error.type
head 55 ERROR (code 2) Run aborted: wall-clock budget of 6s exceeded (--max-wall-time). run_budget_exceeded
base 55 observed stamped too — see note

Head assertions (6/6): exit 55, span exported, status ERROR, bounded diagnostic message present, low-cardinality error.type. Validity controls on both arms: the stalling server received ≥2 requests and ≥2 stalls; base arm ran and exported spans and reaches exit 55. Witness: evidence/01-wire-harness-budget-abort-head-vs-base.png (8/8 assertions, logs/harness-h1-final.log).

Note on the base control cell: the live base arm stamped the span correctly because the real fetch rejection propagates slowly enough that routeAbort lands first — this live scenario does not deterministically force the race the PR fixes. The deterministic reproduction is the unit cell, which registers the core-side cancelled finalizer as an abort listener ahead of the stamp (base result there: ('cancelled', {promptId}) instead of ('error', {errorMessage: …budget…, errorType: 'run_budget_exceeded'}) — see M3 below). The harness proves end-to-end reachability and correct head classification; the unit A/B proves the flip.

Mutation matrix (vacuity) at head — 5/5 killed

# mutation (head sources, scratch worktree) suite filter result
M1 remove ownsStructuredOutputContract gate in client.ts client.test.ts -t structured-output 5 red / 1 green — cron/notification/teammate/goal + notification-continuation all fail with structured_output_missing fired where 'ok' was expected; the userQuery-ownership test stays green (test discriminates, not blanket-red). Witness: evidence/04-mutation-m1-gate-removed-5-red.png
M2 force cancelled = false in non-stream path (loggingContentGenerator.ts) -t abort 1 red / 8 green — exactly records cancellation when a non-stream provider resolves after swallowing an abort; the post-completion-abort test and all stream-side tests stay green (mutation is path-scoped and the tests discriminate)
M3 delete the stampBudgetAbort listener registration (nonInteractiveCli.ts) -t budget 1 red / 3 green — the race test flips to ('cancelled', {promptId}) where ('error', {errorMessage: 'Run aborted: wall-clock budget of 0.01s exceeded (--max-wall-time).', errorType: 'run_budget_exceeded'}) was expected: the exact misclassification the PR's central claim names
M4 delete the pending-batch deferral push (useGeminiStream.ts) -t deferred 5 red / 0 green — all deferred-drain tests time out (~1 s each) waiting for a continuation submission that never happens: batches are dropped, matching base behavior
M5 disable the secondary-owner split (geminiTools.filter(() => false)) -t btw 1 red / 6 green — exactly continues a deferred main tool batch without leaking its mixed ?btw owner fails: the ?btw interaction span never ends 'cancelled' (leaks until TTL); all other ?btw behavior unaffected

Positive control: M1's first run is the caught-mutation proof quoted beside the survivors; there are no survivors — every guard the PR introduces is pinned by at least one test, and each mutation failed the intended behavioral assertion (expected-vs-actual quoted in logs/), not an import/compile break.

Corrections

None — no inaccurate claims from earlier review rounds were encountered in the metadata snapshot, and every mechanism checked matched the PR description (including the design-doc sentence that automatic drain invocations may complete with plain text while the headless runner stays the session-level authority).

Findings

No blocking findings.

  1. (info) Snapshot baseRefOid drift. The metadata snapshot lists base 53a7f2f…; the local merge-ref base tip is c396fe3d… (main advanced after the snapshot). Verification used the local HEAD^1, which is what the merge ref was actually built against.
  2. (info) Live interleaving of the budget race. The process-level base arm did not reproduce the misclassification (see note above). This bounds the bug: it requires the core finalizer to close the interaction inside the abort dispatch before routeAbort runs — deterministic in the unit wiring, timing-dependent in the live process. The PR's synchronous abort-listener stamp is exactly the shape that wins regardless of ordering.
  3. (info, verified consistent) Two-site contract predicate. session-tracing.ts grants gen_ai.output.type=json to userQuery/retry/acp_prompt, while client.ts's missing-output gate allows UserQuery/Retry (there is no ACP member in SendMessageType). Verified reachable wiring: ACP sessions start their own interaction spans with messageType: 'acp_prompt' (packages/cli/src/acp-integration/session/Session.ts:3983); when that interaction is driven through sendMessageStream, the client gate records the driving enum type, so the two sites agree on who owns the contract. No action needed.
  4. (info) Linux evidence. The PR table marks Linux ⚠️ (not locally tested by the author); every gate, A/B cell, mutation, and the live harness in this round ran on Linux and were green.

Not covered

  • Per-commit attribution. Metadata lists 8 commits; the depth-2 checkout makes only the head commit reachable (git rev-list HEAD^1..HEAD^2 = 1, shallow boundary). The aggregate HEAD^1..HEAD diff was verified; individual commit claims were not.
  • Repo-wide test suite, lint, and typecheck — the PR's own CI covers these; targeted gates only, per budget discipline.
  • Windows behavior (container is Linux).
  • Workflow invocation and workflow dispatch tracing — declared out of scope by the PR itself.
  • The live process race interleaving for the budget abort (see Findings Where is the config saved? #2) — not deterministically reachable from outside the unit wiring; the deterministic cells cover it.
  • Base-arm process harness resolves non-core internal packages (acp-bridge, sdk, …) through the shared root node_modules links into the head tree; these packages are untouched by this PR and carry no code path exercised by the harness oracle, but the skew is noted for completeness.

Methodology

Environment: CI verify container (node:22-bookworm), Node v22.23.2, merge-ref checkout at depth 2; npm ci + npm run build completed before this round. Base A/B used git worktree add tmp/base-tree HEAD^1 with symlinks for the nested per-package node_modules (the root hoist lacks ajv, which lives in packages/core/node_modules — an A/A run first exposed this) plus the generated git-commit.ts files (gitignored build artifacts); cli test runs resolve @qwen-code/qwen-code-core through the worktree-relative vitest alias, and readlink -f confirmed third-party deps resolve to the shared store while core sources come from the base tree itself. Mutations ran in a second scratch worktree at HEAD and were restored with git checkout after each cell; both worktrees were removed after capture. Gates used npx vitest run <files> --coverage.enabled=false; raw per-cell logs are in logs/ (ab-base-core.clean.log, ab-base-cli.clean.log, harness-h1-final.log). The wire harness drove scripts/dev.js (tsx from source) headless against a stalling loopback provider (harness/stall-server.mjs, patched from the e2e-testing skill template) with QWEN_TELEMETRY_* env pointing the FileSpanExporter at a per-arm file; spans were parsed from the exported JSON. Evidence images were produced with scripts/verify-capture.mjs. Assertion accounting (assertions.json): head gates 1141 + A/A controls 1106 + A/B cells 1141 (base reds counted with encoded expectations, per the expected-control rule) + mutation matrix 31 + harness H1 8 = 3427 pass / 0 fail.

Evidence images

01-wire-harness-budget-abort-head-vs-base

02-ab-core-pr-tests-on-base-23-red

03-ab-cli-pr-tests-on-base-11-red

04-mutation-m1-gate-removed-5-red

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

⚠️ The bot's only review on c559f1dd525703fbb48ba5a2358131f6152520d1 is a COMMENTED one, which carries no vote — so it has no verdict of its own on this commit, and main needs two approving reviews: an approval left by another account is a separate vote and does not count as the bot's own. Two different things look like this, and the stage-3 comment above says which: the triage skill deferring on purpose at 3/5 — a fork refactor hitting the approval guardrail, or a core change escalated for maintainer awareness, both normal outcomes — or an earlier approval that a push dismissed, leaving only the comment behind, which needs a fresh review.

⚠️ 机器人在 c559f1dd525703fbb48ba5a2358131f6152520d1 上唯一的评审是 COMMENTED不带票 —— 因此它在该 commit 上没有自己的裁决,而 main 需要两个批准(其他账号的批准是另一张票)。有两种情况长这样,上方的 stage-3 评论会说明是哪一种:triage skill 在 3/5 时有意 defer(fork refactor 命中审批护栏,或核心改动被升级交由维护者把关,两者都是正常结果);或者更早的批准被一次推送作废、只剩下这条评论,此时需要重新评审。

The stage comments above were updated with the latest result. View workflow run.

上方各阶段评论已更新为最新结果。查看工作流运行

@wenshao

wenshao commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Maintainer local verification — real build, real spans, base vs. head A/B

I built both sides from source in isolated worktrees and drove them against a scriptable OpenAI-compatible provider with real OpenTelemetry export, to see how much of this PR is observable outside unit tests.

Setup — base 53a7f2fd1b (merge-base) and head c559f1dd52, each npm run build && npm run bundle in its own worktree; isolated HOME/QWEN_HOME; telemetry.enabled=true, target=local, outfile=<run>/telemetry.log so the production NodeSDK + FileSpanExporter path produces the spans; a scripted mock provider that records every request it receives to a ledger. macOS 15.6, Node v24.18.1. Same script, same key sequence, same ports-per-arm for both sides.

1. Structured-output verdict scoping — reproduced end to end ✅

A headless run with --json-schema where the model schedules a one-shot cron job, then both the user turn and the cron drain turn answer in plain text.

fig1

Base charges the automatic cron interaction with gen_ai.output.type=json and exports it ERROR / structured_output_missing. Head leaves it ok / UNSET with the attribute absent. The userQuery row is byte-identical on both sides — including the fact that it is still flagged after a tool continuation — so this is a scoping change, not a relaxation. This is the clearest win in the PR.

2. ?btw continuation ownership under cancellation — reproduced end to end ✅

Real TUI in tmux. A ?btw side question is submitted while the main model stream is still open (so it gets its own interaction span and its own abort controller), then ESC cancels the main turn.

fig2

On base the main turn's cancel flag leaks into the side question: the ?btw tool executes, its result is never submitted to the model, the persisted chat history ends on a dangling tool result, and the ?btw interaction span stays open 48.5 s — closed only when the process exits. On head the continuation is keyed to its own owner: the tool result is submitted under the ?btw prompt, the model answers, and the span closes in 5.1 s with turn_status=ok. This is the strongest behavioural evidence in the PR and it lands exactly where the PR claims.

3. On the mixed main + ?btw batch (the "highest-risk path")

Worth recording because it supports the author's "Not taken" rationale rather than contradicting it. In both arms the ?btw interaction span really does open while the main one is still running (base: ##1 opens at t+9.05 s while ##0 runs to t+14.09 s), so concurrent admission is real — but the provider request for the side question does not go out until the main stream ends, because GeminiChat serializes provider streams. I could not construct a batch containing tool calls from two live owners from the TUI in either arm. So the fail-closed secondary-owner path is defensive code for a state production currently blocks, which matches what the author argued in the review threads.

4. What I could not drive from outside, and what I did instead

  • Budget abort vs. the cancellation finalizer. I tried both --max-wall-time 2 (abort lands mid-stream) and --max-tool-calls 1 (abort lands at a tool boundary). Both arms exported turn_status=error, error.type=run_budget_exceeded and the exact budget message — i.e. I could not make base lose the race from the outside. The race is real at the seam though: reverting nonInteractiveCli.ts to base makes "records a budget error when an in-flight model stream rejects after abort" fail. Treating this fix as insurance against an interleaving rather than a routinely-hit bug seems right.
  • Providers that swallow aborts (non-stream completion, and the 5-minute stream idle timeout). Not producible with an HTTP mock — aborting the fetch does propagate. Pinned by 6 tests in loggingContentGenerator.test.ts instead.
  • Arena error categories and the new status on Unauthorized/Forbidden/BadRequest. Not reachable without arena mode. I did check for regressions: real 401 and 403 responses produce identical llm_request and interaction spans and an identical --output-format json result payload on both arms.

5. Tests and mutation matrix

fig3

Baseline on unmodified head across the 8 suites this PR touches: core 638/638, cli 503/503 (1 skipped). Then, keeping the PR's test files and reverting one production file at a time to the merge base, 8 of 9 hunks are pinned by at least one failing test.

Findings — non-blocking, but worth a follow-up

a) The scheduler .catch() guard is not defended by its test. useReactToolScheduler.test.tsx's new case asserts only expect(scheduleSpy).toHaveBeenCalledOnce() and never observes the rejection, so it passes 7/7 against the unpatched file, and the run emits no unhandled-rejection diagnostic either. The production change is right; the test just does not protect it. Asserting the debugLogger.error call (or that no unhandledRejection fires) would close it.

b) Three of the eight newly added useGeminiStream.test.tsx blocks also pass against the base file: records the Goal finalization error on the owning interaction, releases a detached ?btw controller after history repair deduplicates its tool, and does not re-arm a cancelled submission lease while the stream unwinds. The first matches a hunk that is a pure refactor (hoisting getErrorMessage(error) into a local), so that is expected; the other two read as characterization tests. Not a problem, just don't count them as regression coverage.

c) UI observation. In scenario 2 the two TUI frames are pixel-identical. Head's recovered ?btw answer reaches the wire and is written to session history, but is never painted in the transcript — the user sees Request cancelled. plus the tool output, and never the answer that a second model call was paid for. Not a telemetry issue and out of scope here, but it is a visible gap created by making the continuation survive.

Verdict

Both behaviours I could exercise end to end do what the PR says, with no regression on the user-origin path, on error classification, or on the JSON output contract. From a verification standpoint this looks good to merge; (a) and (b) are test-quality items for a follow-up, not blockers.

中文版

维护者本地验证 —— 真实构建、真实 span、base/head 双臂对照

我在隔离 worktree 里从源码分别构建了两侧,接一个可编排的 OpenAI 兼容 mock provider,开真实 OpenTelemetry 导出,看这个 PR 有多少行为在单测之外可观测。

环境 —— base 53a7f2fd1b(merge-base)与 head c559f1dd52,各自 npm run build && npm run bundle;隔离 HOME/QWEN_HOMEtelemetry.enabled=true, target=local, outfile=<run>/telemetry.log,走生产的 NodeSDK + FileSpanExporter 产出 span;mock provider 把收到的每个请求写进台账。macOS 15.6、Node v24.18.1。两臂脚本、按键序列完全一致。

1. 结构化输出判定的归属收窄 —— 端到端复现 ✅

headless 跑 --json-schema:模型先建一个一次性 cron 任务,随后用户轮和 cron drain 轮都以纯文本作答(见上方 fig1)。

base 把自动 cron interaction 也打上 gen_ai.output.type=json,并导出为 ERROR / structured_output_missing;head 则是 ok / UNSET 且不带该属性。两臂的 userQuery 行逐字一致——包括经过工具 continuation 之后仍然被标记——所以这是范围收窄,不是放宽。这是本 PR 最干净的一处改进。

2. 取消时 ?btw continuation 的归属 —— 端到端复现 ✅

tmux 里跑真实 TUI。在 main 模型流仍然打开时提交 ?btw 侧问(因此它拥有独立 interaction span 和独立 abort controller),随后按 ESC 取消 main 轮(见上方 fig2)。

base 上 main 轮的取消标记泄漏到了侧问:?btw 的工具执行了,结果却从未提交给模型,持久化会话历史停在一个悬空 tool result,?btw 的 interaction span 保持打开 48.5 秒,只在进程退出时才关闭。head 上 continuation 绑定在自己的 owner 上:工具结果以 ?btw 的 prompt 提交,模型给出回答,span 在 5.1 秒 内以 turn_status=ok 关闭。这是本 PR 最有力的行为证据,且与描述完全吻合。

3. 关于 main + ?btw 混合批次("最高风险路径")

这条记录是支持作者 "Not taken" 论证的。两臂中 ?btw 的 interaction span 确实在 main 仍在运行时就打开(base:##1 在 t+9.05s 打开,##0 跑到 t+14.09s),并发准入是真的;但侧问的provider 请求要等 main 流结束才发出——因为 GeminiChat 会串行化 provider 流。两臂都无法从 TUI 造出同时含两个存活 owner 的工具批次。所以 secondary owner 的 fail-closed 分支是针对生产环境当前不可达状态的防御性代码,与作者在评审线程里的说法一致。

4. 无法从外部驱动的部分,以及替代验证

  • 预算 abort 与取消 finalizer 的竞态:我试了 --max-wall-time 2(abort 落在流中间)和 --max-tool-calls 1(abort 落在工具边界)。两臂都导出 turn_status=errorerror.type=run_budget_exceeded 和准确的预算消息——从外部造不出 base 输掉竞态的交错。但竞态在接缝处是真实的:把 nonInteractiveCli.ts 回退到 base 后,"records a budget error when an in-flight model stream rejects after abort" 会失败。把这个修复当成对某种交错的保险、而非常发 bug,是恰当的。
  • provider 吞掉 abort(非流式调用与 5 分钟流空闲超时):HTTP mock 造不出来——abort fetch 是会传播的。改由 loggingContentGenerator.test.ts 的 6 个测试兜住。
  • Arena 错误分类与 Unauthorized/Forbidden/BadRequest 上新增的 status:不开 arena 模式不可达。我验了无回归:真实 401 与 403 响应在两臂产生完全相同的 llm_request/interaction span 和完全相同的 --output-format json 结果负载。

5. 测试与变异矩阵

未改动的 head 上,本 PR 涉及的 8 个套件基线:core 638/638、cli 503/503(1 skipped)。随后保留 PR 的测试文件、每次只把一个生产文件回退到 merge base,9 个生产文件中 8 个被至少一个失败测试钉住(见上方 fig3)。

发现 —— 非阻塞,但值得跟进

a) scheduler 的 .catch() 保护没有被测试守住。 useReactToolScheduler.test.tsx 的新用例只断言 expect(scheduleSpy).toHaveBeenCalledOnce(),从不观察那个 rejection,因此对未打补丁的文件同样 7/7 通过,运行期也不产生任何 unhandled-rejection 诊断。生产改动本身没问题,只是测试没有保护它。断言 debugLogger.error 被调用(或断言没有 unhandledRejection)即可闭合。

b) useGeminiStream.test.tsx 新增的 8 个块中有 3 个对 base 文件同样通过: records the Goal finalization error on the owning interactionreleases a detached ?btw controller after history repair deduplicates its tooldoes not re-arm a cancelled submission lease while the stream unwinds。第一个对应的 hunk 是纯重构(把 getErrorMessage(error) 提到局部变量),符合预期;另外两个读起来是刻画性测试。不算问题,只是别把它们计入回归覆盖。

c) UI 观察。 场景 2 里两臂的 TUI 帧逐像素一致。head 恢复出来的 ?btw 回答上了线也写进了会话历史,但从未渲染进 transcript——用户只看到 Request cancelled. 和工具输出,看不到那次额外模型调用换来的答案。这不是 telemetry 问题、也不在本 PR 范围内,但确实是"让 continuation 存活"带来的一个可见缺口。

结论

我能端到端跑通的两处行为都与 PR 描述一致,且在用户来源路径、错误分类、JSON 输出契约上都无回归。从验证角度看可以合入;(a) 和 (b) 是测试质量的跟进项,不构成阻塞。

@wenshao

wenshao commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao
wenshao enabled auto-merge August 16, 2026 00:01
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ❌ not passed — the PR could not be built - workflow run

The PR could not be built because npm ci failed twice in a row before any verification started. This is treated as a PR failure verdict rather than an infrastructure failure.

中文 — 判定:❌ 不通过 · PR 构建失败

由于 npm ci 在验证开始前失败(连续两次),无法构建该 PR。判定为 PR 问题而非基础设施故障;安装日志见下方折叠块。

Install/build log

$ npm ci --prefer-offline --no-audit --progress=false --cache "$RUNNER_TEMP/npm-cache"
npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead
npm warn deprecated @humanwhocodes/config-array@0.13.0: Use @eslint/config-array instead
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.

> @qwen-code/qwen-code@0.21.11 postinstall
> patch-package

patch-package 8.0.1
Applying patches...
ink@7.0.3 ✔

> @qwen-code/qwen-code@0.21.11 prepare
> node scripts/prepare.js


> @qwen-code/qwen-code@0.21.11 build
> cross-env NODE_OPTIONS="--max-old-space-size=3072" node scripts/build.js


> @qwen-code/qwen-code@0.21.11 generate
> node scripts/generate-git-commit-info.js


> @qwen-code/qwen-code-core@0.21.11 build
> node ../../scripts/build_package.js

Successfully copied files.

> @qwen-code/web-templates@0.21.11 build
> node build.mjs && tsc --build --clean && tsc

Building web-templates...
Building insight assets with Vite...
�[36mvite v5.4.21 �[32mbuilding for production...�[36m�[39m
transforming...
Browserslist: browsers data (caniuse-lite) is 9 months old. Please run:
  npx update-browserslist-db@latest
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
�[32m✓�[39m 8 modules transformed.
rendering chunks...
computing gzip size...
�[2mdist/�[22m�[35mmain.css  �[39m�[1m�[2m17.77 kB�[22m�[1m�[22m�[2m │ gzip: 4.28 kB�[22m
�[2mdist/�[22m�[36mmain.js   �[39m�[1m�[2m32.80 kB�[22m�[1m�[22m�[2m │ gzip: 9.07 kB�[22m
�[32m✓ built in 432ms�[39m
Reading generated files...
Successfully generated /__w/qwen-code/qwen-code/packages/web-templates/src/generated/insightTemplate.ts
Successfully built all web-templates.

> @qwen-code/channel-base@0.21.11 build
> tsc --build


> @qwen-code/channel-telegram@0.21.11 build
> tsc --build


> @qwen-code/channel-weixin@0.21.11 build
> tsc --build


> @qwen-code/channel-dingtalk@0.21.11 build
> tsc --build


> @qwen-code/channel-wecom@0.21.11 build
> tsc --build


> @qwen-code/channel-feishu@0.21.11 build
> tsc --build


> @qwen-code/channel-qqbot@0.21.11 build
> tsc --build


> @qwen-code/channel-github@0.21.11 build
> tsc --build


> @qwen-code/channel-plugin-example@0.21.11 build
> tsc --build


> @qwen-code/audio-capture@0.21.11 build:ts
> tsc --build


> @qwen-code/acp-bridge@0.21.11 build
> node ../../scripts/build_package.js

Successfully copied files.

> @qwen-code/sdk@0.1.8 build
> node scripts/build.js

Compiling input files...
Processing src/index.ts
Writing src/index.ts -> dist/index.d.ts
Checking generated files...
�[1;33mCompiler option "skipLibCheck" is disabled to properly check generated output�[0m
Done in 28.40s
Compiling input files...
Processing src/daemon/transcript.ts
Writing src/daemon/transcript.ts -> dist/daemon/transcript.d.ts
Checking generated files...
�[1;33mCompiler option "skipLibCheck" is disabled to properly check generated output�[0m
Done in 4.21s
file:///__w/qwen-code/qwen-code/packages/sdk-typescript/scripts/build.js:316
    throw new Error(
          ^

Error: Browser daemon SDK bundle is 194607 bytes; expected <= 194560
    at assertBrowserSafeBundle (file:///__w/qwen-code/qwen-code/packages/sdk-typescript/scripts/build.js:316:11)
    at file:///__w/qwen-code/qwen-code/packages/sdk-typescript/scripts/build.js:193:1
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)

Node.js v22.23.2
npm error Lifecycle script `build` failed with error:
npm error code 1
npm error path /__w/qwen-code/qwen-code/packages/sdk-typescript
npm error workspace @qwen-code/sdk@0.1.8
npm error location /__w/qwen-code/qwen-code/packages/sdk-typescript
npm error command failed
npm error command sh -c node scripts/build.js
node:internal/errors:983
  const err = new Error(message);
              ^

Error: Command failed: npm run build --workspace=packages/sdk-typescript
    at genericNodeError (node:internal/errors:983:15)
    at wrappedFn (node:internal/errors:537:14)
    at checkExecSyncError (node:child_process:916:11)
    at execSync (node:child_process:988:15)
    at file:///__w/qwen-code/qwen-code/scripts/build.js:85:3
    at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) {
  status: 1,
  signal: null,
  output: [ null, null, null ],
  pid: 7588,
  stdout: null,
  stderr: null
}

Node.js v22.23.2
prepare: npm run build exited with status 1
npm error code 1
npm error path /__w/qwen-code/qwen-code
npm error command failed
npm error command sh -c node scripts/prepare.js
npm error A complete log of this run can be found in: /__w/_temp/npm-cache/_logs/2026-08-16T00_02_34_155Z-debug-0.log

npm ci failed with exit code 1; retrying once.
$ npm ci --prefer-offline --no-audit --progress=false --cache "$RUNNER_TEMP/npm-cache"
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.

> @qwen-code/qwen-code@0.21.11 postinstall
> patch-package

patch-package 8.0.1
Applying patches...
ink@7.0.3 ✔

> @qwen-code/qwen-code@0.21.11 prepare
> node scripts/prepare.js


> @qwen-code/qwen-code@0.21.11 build
> cross-env NODE_OPTIONS="--max-old-space-size=3072" node scripts/build.js


> @qwen-code/qwen-code@0.21.11 generate
> node scripts/generate-git-commit-info.js


> @qwen-code/qwen-code-core@0.21.11 build
> node ../../scripts/build_package.js

Successfully copied files.

> @qwen-code/web-templates@0.21.11 build
> node build.mjs && tsc --build --clean && tsc

Building web-templates...
Building insight assets with Vite...
�[36mvite v5.4.21 �[32mbuilding for production...�[36m�[39m
transforming...
Browserslist: browsers data (caniuse-lite) is 9 months old. Please run:
  npx update-browserslist-db@latest
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
�[32m✓�[39m 8 modules transformed.
rendering chunks...
computing gzip size...
�[2mdist/�[22m�[35mmain.css  �[39m�[1m�[2m17.77 kB�[22m�[1m�[22m�[2m │ gzip: 4.28 kB�[22m
�[2mdist/�[22m�[36mmain.js   �[39m�[1m�[2m32.80 kB�[22m�[1m�[22m�[2m │ gzip: 9.07 kB�[22m
�[32m✓ built in 342ms�[39m
Reading generated files...
Successfully generated /__w/qwen-code/qwen-code/packages/web-templates/src/generated/insightTemplate.ts
Successfully built all web-templates.

> @qwen-code/channel-base@0.21.11 build
> tsc --build


> @qwen-code/channel-telegram@0.21.11 build
> tsc --build


> @qwen-code/channel-weixin@0.21.11 build
> tsc --build


> @qwen-code/channel-dingtalk@0.21.11 build
> tsc --build


> @qwen-code/channel-wecom@0.21.11 build
> tsc --build


> @qwen-code/channel-feishu@0.21.11 build
> tsc --build


> @qwen-code/channel-qqbot@0.21.11 build
> tsc --build


> @qwen-code/channel-github@0.21.11 build
> tsc --build


> @qwen-code/channel-plugin-example@0.21.11 build
> tsc --build


> @qwen-code/audio-capture@0.21.11 build:ts
> tsc --build


> @qwen-code/acp-bridge@0.21.11 build
> node ../../scripts/build_package.js

Successfully copied files.

> @qwen-code/sdk@0.1.8 build
> node scripts/build.js

Compiling input files...
Processing src/index.ts
Writing src/index.ts -> dist/index.d.ts
Checking generated files...
�[1;33mCompiler option "skipLibCheck" is disabled to properly check generated output�[0m
Done in 27.17s
Compiling input files...
Processing src/daemon/transcript.ts
Writing src/daemon/transcript.ts -> dist/daemon/transcript.d.ts
Checking generated files...
�[1;33mCompiler option "skipLibCheck" is disabled to properly check generated output�[0m
Done in 4.40s
file:///__w/qwen-code/qwen-code/packages/sdk-typescript/scripts/build.js:316
    throw new Error(
          ^

Error: Browser daemon SDK bundle is 194607 bytes; expected <= 194560
    at assertBrowserSafeBundle (file:///__w/qwen-code/qwen-code/packages/sdk-typescript/scripts/build.js:316:11)
    at file:///__w/qwen-code/qwen-code/packages/sdk-typescript/scripts/build.js:193:1
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)

Node.js v22.23.2
npm error Lifecycle script `build` failed with error:
npm error code 1
npm error path /__w/qwen-code/qwen-code/packages/sdk-typescript
npm error workspace @qwen-code/sdk@0.1.8
npm error location /__w/qwen-code/qwen-code/packages/sdk-typescript
npm error command failed
npm error command sh -c node scripts/build.js
node:internal/errors:983
  const err = new Error(message);
              ^

Error: Command failed: npm run build --workspace=packages/sdk-typescript
    at genericNodeError (node:internal/errors:983:15)
    at wrappedFn (node:internal/errors:537:14)
    at checkExecSyncError (node:child_process:916:11)
    at execSync (node:child_process:988:15)
    at file:///__w/qwen-code/qwen-code/scripts/build.js:85:3
    at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) {
  status: 1,
  signal: null,
  output: [ null, null, null ],
  pid: 8285,
  stdout: null,
  stderr: null
}

Node.js v22.23.2
prepare: npm run build exited with status 1
npm error code 1
npm error path /__w/qwen-code/qwen-code
npm error command failed
npm error command sh -c node scripts/prepare.js
npm error A complete log of this run can be found in: /__w/_temp/npm-cache/_logs/2026-08-16T00_06_01_783Z-debug-0.log

npm ci failed with exit code 1 after 2 attempts.

Qwen Code · sandboxed verification

@qwen-code-ci-bot qwen-code-ci-bot 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.

LGTM — clean re-review at this head, both deferred questions settled by the maintainer verification and approval. Approving pinned to the reviewed commit. ✅

@wenshao
wenshao added this pull request to the merge queue Aug 16, 2026
Merged via the queue into QwenLM:main with commit 43b0779 Aug 16, 2026
361 of 370 checks passed

@wenshao wenshao 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.

Partially reviewed — gaps disclosed.

5 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:

  • scheduler.schedule non-abort rejection handling — already reported (comment 3788410400, R6-4), deferred by the author
  • Retry-branch structured-output ownership coverage — already reported (comments 3781921174 / 3783065854, R2-9), deferred by the author
  • Arena error-category mapping untested branches — already reported (comment 3783650489, R2-5), deferred by the author
  • turn.ts ?? originalStatus unreachable fallback — already reported (comment 3788922560, R7-3), deferred by the author
  • steer-isolation test pins only the sendOptions guard, not the completion-boundary drain guard — already reported (comment 3788922570, R7-1), deferred by the author

Unresolved, please confirm:

  • [Critical] R3-13 (useGeminiStream.ts:4278, thread 3786904525) — deferred batches carry no submission-generation identity; the admission-gate enumeration vs. the filed probe contest was not settled this round
  • [Critical] R3-14 (useGeminiStream.ts:4374, thread 3786904531) — partitioning with telemetry disabled; a maintainer scope call, mechanism matches pre-PR behavior — could not be determined
  • [Critical] R3-16 (AppContainer.tsx:2606, thread 3786904539) — re-queued ?btw drains as a plain UserQuery; author declined as the intended legacy contract; this round's probe confirmed the behavior matches the tested R3-1 design; maintainer approved an…
  • [Critical] R5-20 (useGeminiStream.ts:3979, thread 3786904554) — deferred drain and scheduler-driven dispatch can both enter handleCompletedTools; production reachability under GeminiChat send serialization contested — could not be determined
  • [Critical] useGeminiStream.ts:4374 (thread 3781921163) — dead-owner/ownerless secondary classification drops successful results from a mixed batch; production reachability contested — could not be determined
  • [Critical] useGeminiStream.ts:4374 (thread 3781923797) — same classification claim (posted twice) — could not be determined
  • [Critical] useGeminiStream.ts:4374 (thread 3783065821, R2-1) — classify secondary tools by live owners; same contested reachability — could not be determined
  • [Critical] useGeminiStream.ts:4395 (thread 3782611141) — secondary owner's response filtered before finalizeToolResponses/addHistory; the author's serialization rationale could not be independently verified — could not be determined
  • [Critical] useGeminiStream.ts:4398 (thread 3783065817, R1-1) — persist the secondary owner's response before removal; same contested mechanism — could not be determined
  • [Critical] useGeminiStream.ts:4410 (thread 3783065832, R2-3) — unconditional secondary 'cancelled' telemetry; the loop stands but only fires inside the contested mixed batch — could not be determined
  • [Critical] useGeminiStream.ts:4603 (thread 3788410385, R6-2) — parked duplicate-response batch readiness; author declined; this round's auditors judged repair covers the wire but the duplicate-provider shape was not probed — could not be determined

Not reviewed: build-and-test — packages/cli and packages/core full-suite runs exceeded the 300s harness deadline (infrastructure timeout, not a defect); the PR's 8 changed test files ran individually green (core 638/638, cli 503 passed + 1 pre-existing skip); dependent-workspace suites not run: acp-bridge, sdk-typescript, vscode-ide-companion, web-shell, webui; CI's Test (macos-latest / windows-latest, Node 22.x) matrix jobs were skipped at this commit.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:

  • packages/cli/src/ui/hooks/useGeminiStream.test.tsx:13671 — [probe] D4-1 — the mixed main+?btw drain tests cannot regress against the R4-18 root cause (both pass at the defect commit)
中文说明

仅完成部分审查,审查缺口已披露。

本轮确认的 5 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。

未决,请确认:共 11 条(原文未翻译,列表见上方英文部分)。

未审查:build-and-test — packages/cli and packages/core full-suite runs exceeded the 300s harness deadline (infrastructure timeout, not a defect); the PR's 8 changed test files ran individually green (core 638/638, cli 503 passed + 1 pre-existing skip); dependent-workspace suites not run: acp-bridge, sdk-typescript, vscode-ide-companion, web-shell, webui; CI's Test (macos-latest / windows-latest, Node 22.x) matrix jobs were skipped at this commit。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment on lines +4283 to +4298
const continuationOwner = completedAndReadyToSubmitTools
.filter(
(toolCall) =>
!historyCallIdsWithResponse.has(toolCall.request.callId),
)
.map((toolCall) =>
continuationOwnersByToolCallIdRef.current.get(
toolCall.request.callId,
),
)
.find((owner) => owner !== undefined);
const continuationWasCancelled = () =>
continuationOwner
? continuationOwner.signal.aborted
: turnCancelledRef.current ||
abortControllerRef.current?.signal.aborted === true;

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.

[Critical] R4-18: continuationOwner is selected over the WHOLE drain-merged batch in completion order, before the secondary-owner split — so both the batch's cancellation verdict (continuationWasCancelled) and the owner identity propagated into the next submitQuery (toolContinuationOwner) can be taken from an unrelated or already-aborted continuation instead of the interaction the batch actually drives. This re-demonstrates R4-18: the 06ef3cb fix only covered the history-repaired shape (the PR's own regression test mocks getHistoryFunctionResponseIds to Set(['main-tool'])); in the unrepaired shape — no send occurs between Ctrl+C and the drain, so no repair has run — the surviving result is still dropped.

— Failure scenario (both probe-verified on this commit): (1) the main tool completes while a concurrent ?btw stream is active → deferred; Ctrl+C aborts only the foreground controller; the ?btw tool completes → deferred; the drain merges [mainTool, btwTool]; continuationOwner = the main owner (first .find match) whose signal is aborted → continuationWasCancelled() is true even though secondary filtering has already narrowed the batch to the surviving ?btw tool — its successful result is marked submitted and dropped without ever being sent, and its interaction is ended cancelled. (2) With [btwTool, mainTool] order, the MAIN continuation instead runs detached under the ?btw identity (no foreground controller, processingSignal embeds the ?btw signal), and a first Ctrl+C fails to cancel it.

Witness (probes on the unmodified PR):

probe 1: sendMessageStream call count 2 (no continuation for the surviving btw tool);
  endInteractionSpan [['cancelled',{main}],['cancelled',{btw}]];
  recomputing the owner from the surviving tools -> call count 3, btw functionResponse sent
probe 2: {abortedOnFirstPress:false, abortedOnSecondPress:true, mainSignalAfterFirstPress:true};
  preferring the owner whose promptId matches the active interaction -> abortedOnFirstPress:true

Suggested fix: compute continuationOwner after the secondary-owner split from the surviving geminiTools (falling back to the current pre-split scan), or prefer an owner whose promptId matches the driving interaction (ownerToolCall.request.prompt_id / activeInteractionPromptIdRef), so the cancellation verdict and the toolContinuationOwner metadata both describe the interaction the batch actually drives.

中文说明

[Critical] R4-18:continuationOwner 在 secondary-owner 拆分之前按完成顺序在整个 drain 合并批次上选取——因此批次的取消判定(continuationWasCancelled)与传给下一次 submitQuery 的 owner 身份(toolContinuationOwner)都可能来自一个无关的、甚至已被 abort 的 continuation,而不是批次实际驱动的 interaction。这再次验证了 R4-18:06ef3cbaf5 的修复只覆盖了 history 已修复的形状(PR 自带的回归测试把 getHistoryFunctionResponseIds mock 成 Set(['main-tool']));在未修复形状下——Ctrl+C 到 drain 之间没有任何 send,repair 根本不会运行——幸存结果仍会被丢弃。

— 失败场景(均已在本 commit 上用探针验证):(1) main 工具在活跃的 ?btw stream 期间完成 → 被延迟;Ctrl+C 只 abort 前台 controller;?btw 工具完成 → 被延迟;drain 合并为 [mainTool, btwTool]continuationOwner = main owner(.find 首个命中),其 signal 已被 abort → 即使 secondary 过滤已把批次缩小到幸存的 ?btw 工具,continuationWasCancelled() 仍为 true——其成功结果被标记为已提交并直接丢弃,该 interaction 被记为 cancelled。(2) 当顺序为 [btwTool, mainTool] 时,MAIN continuation 反而以 ?btw 身份 detached 运行(不安装前台 controller,processingSignal 内嵌 ?btw signal),第一次 Ctrl+C 无法取消它。

建议修复:在 secondary-owner 拆分之后从幸存的 geminiTools 计算 continuationOwner(回退到当前的 pre-split 扫描),或优先选择 promptId 与驱动 interaction 匹配的 owner,使取消判定与 toolContinuationOwner 元数据都描述批次实际驱动的 interaction。

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment on lines +1131 to +1137
const foregroundAbortController = abortControllerRef.current;
if (
foregroundAbortController &&
!foregroundAbortController.signal.aborted
) {
foregroundAbortController.abort();
} else {

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.

[Critical] R4-1: A first Ctrl+C deliberately leaves detached ?btw streams alive (this two-stage branch), but cancelOngoingRequest's tail finally still unconditionally runs setIsResponding(false) (~line 1231, unchanged from base), collapsing streamingState to Idle while the surviving stream is still active (activeModelStreamsRef > 0). The R3-3 fix's settleSubmissionStateIfIdle guard only covers the activity-release path, not this direct write — the author's own stated R3-3 invariant ("submission state settles only when the global model-stream count is zero") is exactly what the cancel tail violates in the survivor scenario.

— Failure scenario (probe-verified on this commit): after one Ctrl+C with a concurrent ?btw stream still live: (a) a ?btw typed in this window fails allowConcurrentBtwDuringResponse (requires Responding) and AppContainer's btw fast-path, and is admitted as an ordinary FOREGROUND turn — it seizes abortControllerRef and resets turnCancelledRef, so a first press no longer reaches the original survivor; (b) with YOLO approval mode the ConversationFinishedEvent fires prematurely in the window; (c) the notification/teammate batch-drain effects (guarded only by Idle && !isSubmittingQueryRef) can auto-start new turns concurrent with the survivor.

Witness (probe):

BASE: {window_streamingState:'idle', conversation_finished_events_in_window:1,
       third ?btw admitted as foreground, survivor_aborted_by_cancel2:false}
gating the cancel-tail write on activeModelStreamsRef===0 flips every observable

Note (tested by the verifier): the naive one-line guard breaks the PR's own replacement-turn regression test — a correct fix must key btw-admission, the drain guards, and the YOLO effect on activeModelStreamsRef rather than streamingState alone.

Suggested fix: key the affected consumers on the live stream count — allowConcurrentBtwDuringResponse / the AppContainer btw fast-path, the YOLO ConversationFinishedEvent effect, and the notification/teammate drain effects should require activeModelStreamsRef.current === 0 before treating the session as idle.

中文说明

[Critical] R4-1:第一次 Ctrl+C 通过这里的两阶段分支故意让 detached ?btw stream 存活,但 cancelOngoingRequest 末尾的 finally 仍无条件执行 setIsResponding(false)(约 1231 行,与 base 相同),在幸存 stream 仍然活跃(activeModelStreamsRef > 0)时把 streamingState 塌缩为 Idle。R3-3 修复的 settleSubmissionStateIfIdle guard 只覆盖 activity-release 路径,管不到这个直接写入——R3-3 自己声明的不变量("只有全局 model-stream 计数为零时才结算 submission 状态")正是被这行在幸存场景下违反。

— 失败场景(已在本 commit 上用探针验证):在并发 ?btw stream 存活时按一次 Ctrl+C 后:(a) 此窗口内输入的 ?btw 不满足 allowConcurrentBtwDuringResponse(要求 Responding)与 AppContainer 的 btw 快路径,会作为普通前台 turn 被接纳——它夺取 abortControllerRef 并重置 turnCancelledRef,第一次按键再也无法触达原幸存者;(b) YOLO 模式下 ConversationFinishedEvent 在窗口内提前触发;(c) notification/teammate batch-drain effect(仅以 Idle && !isSubmittingQueryRef 为门)可在幸存者旁边自动启动新 turn。

注意(验证者已实测):简单的一行 guard 会破坏 PR 自带的 replacement-turn 回归测试——正确修复应把 btw 准入、drain 门与 YOLO effect 改为以 activeModelStreamsRef 为准,而不是只看 streamingState

— qwen3.8-max via Qwen Code /review (v0.21.11)

Comment on lines +3433 to 3436
if (!allowConcurrentBtwDuringResponse && !isDetachedToolContinuation) {
abortControllerRef.current = abortController;
turnCancelledRef.current = false;
}

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.

[Critical] R4-2: Surviving detached ?btw continuations run with turnCancelledRef.current === true — the reset is reserved for non-btw non-detached foreground submissions (anchored block) — and the shared output gates in handleContentEvent (~line 1590), handleThoughtEvent (~line 1862) and the image path (~line 2401) drop EVERY event while that flag is set. Net effect: the stream this PR deliberately keeps alive renders no visible output at all — tokens are consumed, tools execute, and the model conversation continues invisibly until the model stops or a second Ctrl+C kills the detached controller. No diff comment, doc, or test argues survivors are meant to render silently; pre-diff nothing survived a cancel, so these gates only ever swallowed the brief abort-propagation tail.

— Failure scenario (probe-verified on this commit): main turn streaming; the user submits ?btw concurrently; one Ctrl+C aborts only the foreground controller; the survivor's model keeps producing, its tool completes, and the continuation re-submits (passing the ~1347 guard via preserveTurnOwnership) — but every content/thought/image event hits the turnCancelledRef gate and is discarded: the remainder of the btw answer is silently dropped. A NEW ?btw admitted while the survivor keeps streamingState Responding is likewise muted.

Witness (probe on the unmodified PR):

continuation ran: a third sendMessageStream carries the btw promptId and yields
Content('PROBE_INVISIBLE_ANSWER') + Finished, yet addItem count=4, answerRendered=false;
reverting the anchored condition flips the same probe to addItem count=5, answerRendered=true

Suggested fix: make the output gates stream-aware — pass the stream's own abort signal (or the allowConcurrentBtwDuringResponse / isDetachedToolContinuation context already available in processStreamChanges) into the gates and suppress only when THAT stream's signal is aborted; or reset turnCancelledRef for detached/concurrent submissions that are admitted by design after a cancel.

中文说明

[Critical] R4-2:幸存的 detached ?btw continuation 运行时 turnCancelledRef.current === true——重置只保留给非 btw、非 detached 的前台提交(锚点代码块)——而 handleContentEvent(约 1590 行)、handleThoughtEvent(约 1862 行)与 image 路径(约 2401 行)的共享输出门在该标志为真时丢弃所有事件。结果是:这条 PR 故意保活的 stream 完全渲染不出任何输出——token 照烧、工具照跑、模型会话不可见地继续,直到模型停止或第二次 Ctrl+C 杀掉 detached controller。diff 的注释、文档或测试都没有说幸存者应该静默渲染;diff 之前没有任何东西能在 cancel 后存活,这些门过去只吞掉 abort 传播的短暂尾部。

— 失败场景(已在本 commit 上用探针验证):main turn 流式中;用户并发提交 ?btw;一次 Ctrl+C 只 abort 前台 controller;幸存者的模型继续输出、其工具完成、continuation 重新提交(经 preserveTurnOwnership 通过 ~1347 的 guard)——但每个 content/thought/image 事件都撞上 turnCancelledRef 门被丢弃:btw 回答的剩余部分被静默丢弃。幸存者维持 Responding 期间新接纳的 ?btw 同样被静音。

建议修复:让输出门改为 stream-aware——把 stream 自己的 abort signal(或 processStreamChanges 中已有的 allowConcurrentBtwDuringResponse / isDetachedToolContinuation 上下文)传进门里,只在 stream 的 signal 被 abort 时抑制;或者对 cancel 后按设计接纳的 detached/concurrent 提交重置 turnCancelledRef

— qwen3.8-max via Qwen Code /review (v0.21.11)

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.

3 participants