Skip to content

fix(web-shell): Replace undefined mock in split rerender tests (#11404) - #11406

Merged
wenshao merged 2 commits into
mainfrom
autofix/issue-11404
Sep 9, 2026
Merged

wenshao merged 2 commits into
mainfrom
autofix/issue-11404

Conversation

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

What this PR does

Repairs two Web Shell unit tests that fail on every run. The split-view navigation change (#11250) added two does not rerender App for other split sessions test variants whose assertions reference a mock identifier that does not exist anywhere in the test file, so both tests throw a ReferenceError before completing. The fix points those assertions at the render spy the file already defines and wires into its module mock — the hook the App component calls on every render — restoring the intended "foreign split sessions must not rerender App" coverage with a three-line, test-only edit.

Why it's needed

This is the cause of the main-branch CI failure tracked in #11404: the Test (ubuntu-latest, Node 22.x) job fails in Run tests and generate reports at commit 70cf363395 because the web-shell vitest suite fails deterministically. The failure reproduces on every run and every retry, so CI's test-retry setting cannot absorb it, and every subsequent main run stays red until the reference is fixed.

Reviewer Test Plan

How to verify

On the base commit, run the web-shell suite and observe the failure; on this branch, observe it pass:

  1. cd packages/web-shell && npx vitest run --config vitest.config.ts App.test.tsx -t "does not rerender App for other split sessions"
  2. Before: both variants fail with ReferenceError: mockUseDaemonActivePromptBridge is not defined at App.test.tsx:28940.
  3. After: both variants pass, and the full web-shell suite passes (288 files / 6712 tests).

The test intent is unchanged: after an initial pending-panes report, reports containing only other sessions' ids must not rerender App. A mutation probe confirms the assertions still bite — forcing a rerender on every report makes both tests fail again.

Evidence (Before & After)

N/A (test-only change, no UI behavior affected)

Tested on

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

Environment (optional)

N/A — vitest unit tests only (Node 22, same as the failing CI lane).

Risk & Scope

  • Main risk or tradeoff: Minimal; the edit touches three assertion lines in one test file and changes no production code. The replacement spy is the file's established render-call witness, so the assertions keep their original meaning.
  • Not validated / out of scope: The CI run logs were not directly readable from the authoring sandbox (no GitHub credentials); the failure was reproduced locally at the exact failing commit with the same command chain the CI step runs. Whether the red run also contained unrelated flaky failures beyond these two tests cannot be determined from here — this fix removes the deterministic failure.
  • Breaking changes / migration notes: None.

Linked Issues

Fixes #11404

中文说明

本 PR 做了什么

修复两个必定失败的 Web Shell 单元测试。split-view 导航改动(#11250)新增了两个 does not rerender App for other split sessions 测试变体,其断言引用了一个在整个测试文件中根本不存在的 mock 标识符,导致两个测试在完成前抛出 ReferenceError。本次修复把这些断言指向该文件早已定义并接入模块 mock 的渲染探针——即 App 组件每次渲染都会调用的那个 hook——仅用三行、仅测试文件的改动恢复了"外部 split 会话不应触发 App rerender"这一原有覆盖意图。

为什么需要

这正是 #11404 跟踪的主分支 CI 失败原因:Test (ubuntu-latest, Node 22.x) 任务在提交 70cf363395 上的 Run tests and generate reports 步骤失败,因为 web-shell 的 vitest 套件确定性失败。该失败每次运行、每次重试都会复现,CI 的测试重试设置无法吸收;在修复该引用之前,后续每次 main 运行都会保持红色。

评审验证计划

如何验证

在基线提交上运行 web-shell 套件可观察到失败;切到本分支后可观察到通过:

  1. cd packages/web-shell && npx vitest run --config vitest.config.ts App.test.tsx -t "does not rerender App for other split sessions"
  2. 修复前:两个变体都失败,报错 ReferenceError: mockUseDaemonActivePromptBridge is not defined,位置在 App.test.tsx:28940。
  3. 修复后:两个变体通过,且整个 web-shell 套件通过(288 个文件 / 6712 个测试)。

测试意图不变:在首次 pending-panes 上报之后,仅包含其他会话 id 的上报不应触发 App rerender。变异探针确认断言仍然有效——强制每次上报都 rerender 会让这两个测试再次失败。

前后对比证据

N/A(仅测试改动,不影响任何 UI 行为)

已测试平台

系统 状态
🍏 macOS ⚠️
🪟 Windows ⚠️
🐧 Linux ✅

环境(可选)

N/A —— 仅 vitest 单元测试(Node 22,与失败的 CI 车道一致)。

风险与范围

  • 主要风险或权衡:极小;改动只涉及一个测试文件中的三行断言,不触碰任何生产代码。替换后的探针是该文件既有的渲染调用见证,断言保持原有语义。
  • 未验证 / 超出范围:编写沙箱内无法直接读取 CI 运行日志(无 GitHub 凭据);已在出问题的确切提交上、用 CI 步骤实际运行的同一命令链完成本地复现。红色运行中是否还包含这两个测试之外的无关 flaky 失败,从这里无法判定——本修复消除的是其中的确定性失败。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

修复 #11404

The two "does not rerender App for other split sessions" tests referenced
mockUseDaemonActivePromptBridge, which is not defined anywhere in
App.test.tsx — a deterministic ReferenceError that failed the main-branch
CI Test job at 70cf363. The intended render spy is
mockUseDaemonSessionActivityBridge: App calls useDaemonSessionActivityBridge
on every render, and this file already uses that mock as the render-call
witness elsewhere.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

E2E / Verification Report — Issue #11404

Summary

Main-branch CI failed at 70cf363395 in job Test (ubuntu-latest, Node 22.x), step Run tests and generate reports (which runs npm run test:ci:workspaces then npm run test:scripts). Reproduced locally at the exact failing commit: the packages/web-shell vitest suite fails deterministically with 2 failing tests.

Root cause

Commit 70cf363395 (feat(web-shell): Improve split-view session navigation, #11250) added two it.each variants of does not rerender App for other split sessions in packages/web-shell/client/App.test.tsx. Both reference mockUseDaemonActivePromptBridge, an identifier that is not defined anywhere in the file — App.tsx never calls useDaemonActivePromptBridge, and the file's ./session-catalog/session-catalog-hooks module mock does not export a mock for it. Every run throws ReferenceError: mockUseDaemonActivePromptBridge is not defined at App.test.tsx:28940, so the failure is deterministic and CI's VITEST_RETRY=2 cannot mask it.

Fix

Point the three references at the intended, existing render spy mockUseDaemonSessionActivityBridge (packages/web-shell/client/App.test.tsx). App.tsx calls useDaemonSessionActivityBridge on every render, the file already wires this mock into the session-catalog-hooks module mock, and it is already used elsewhere in the file as the render-call witness. The test semantics hold: after the initial report + forced rerender the spy has been called; subsequent reports that contain only foreign session ids never change outerSplitPanePending, React bails out of the state update, App does not rerender, and the spy stays silent. Three lines changed, test file only, no production code touched.

Mutation probe

Temporarily changed App.tsx so every pending-panes report forces a rerender (setOuterSplitPanePending((prev) => !prev)): both fixed tests FAIL (2 failed). Restored App.tsx and re-ran: both tests PASS (2 passed). This confirms the repaired tests genuinely witness the "does not rerender App for other split sessions" behavior rather than passing vacuously.

Environment note

The sandbox has no GitHub credentials, so the CI run logs for run 34257817936 could not be fetched; the failure was reproduced locally at the exact failing commit (70cf363395, the checked-out HEAD) using the same command chain the CI step runs. The QWEN_CI_COVERAGE=1 push-lane flag was not set locally; the ReferenceError is coverage-independent.

Verification

  • npm run test:scripts — passed (82 test files, 2310 tests) — covers the scripts/tests/* side of the commit
  • cd packages/web-shell && npx vitest run --config vitest.config.ts — pre-fix: 2 failed / 6710 passed; post-fix: 288 files, 6712 tests, all passed
  • cd packages/web-shell && npx vitest run --config vitest.config.ts App.test.tsx -t "does not rerender App for other split sessions" — 2 passed (post-fix)
  • Mutation probe: same focused run with App.tsx mutated — 2 failed (as expected); after restore — 2 passed
  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
中文说明

E2E / 验证报告 — Issue #11404

摘要

主分支 CI 在提交 70cf363395 上失败,失败任务为 Test (ubuntu-latest, Node 22.x),失败步骤为 Run tests and generate reports(该步骤依次运行 npm run test:ci:workspaces 和 npm run test:scripts)。已在出问题的提交上本地复现:packages/web-shell 的 vitest 测试套件确定性失败,有 2 个测试失败。

根因

提交 70cf363395(feat(web-shell): Improve split-view session navigation,#11250)在 packages/web-shell/client/App.test.tsx 中新增了两个 it.each 版本的 does not rerender App for other split sessions 测试。这两个测试引用了 mockUseDaemonActivePromptBridge,而该标识符在整个文件中从未定义——App.tsx 从未调用 useDaemonActivePromptBridge,文件中对 ./session-catalog/session-catalog-hooks 的模块 mock 也没有导出对应的 mock。每次运行都会在 App.test.tsx:28940 抛出 ReferenceError: mockUseDaemonActivePromptBridge is not defined,因此该失败是确定性的,CI 的 VITEST_RETRY=2 重试无法掩盖。

修复

将这三处引用指向本来就打算使用的、已存在的渲染探针 mockUseDaemonSessionActivityBridge(packages/web-shell/client/App.test.tsx)。App.tsx 每次渲染都会调用 useDaemonSessionActivityBridge,该文件已将此 mock 接入 session-catalog-hooks 的模块 mock,并且文件内其他地方也已将它用作渲染调用的见证。测试语义成立:初始上报加强制 rerender 之后,探针已被调用;随后只包含外部会话 id 的上报不会改变 outerSplitPanePending,React 对相同值的状态更新直接跳过渲染,App 不再 rerender,探针保持静默。仅改动 3 行,只涉及测试文件,未触碰生产代码。

变异探针

临时修改 App.tsx,让每次 pending-panes 上报都强制触发 rerender(setOuterSplitPanePending((prev) => !prev)):两个修复后的测试如预期失败(2 failed)。还原 App.tsx 后重跑:两个测试通过(2 passed)。这证明修复后的测试真实地见证了"其他 split 会话不触发 App rerender"这一行为,而非空转通过。

环境说明

沙箱内没有 GitHub 凭据,无法拉取 run 34257817936 的 CI 日志;改为在出问题的确切提交(70cf363395,即当前检出的 HEAD)上,用 CI 步骤实际运行的同一命令链本地复现。本地未设置推送车道的 QWEN_CI_COVERAGE=1;该 ReferenceError 与覆盖率无关。

验证

  • npm run test:scripts — 通过(82 个测试文件,2310 个测试)—— 覆盖该提交涉及的 scripts/tests/* 一侧
  • cd packages/web-shell && npx vitest run --config vitest.config.ts — 修复前:2 失败 / 6710 通过;修复后:288 个文件、6712 个测试全部通过
  • cd packages/web-shell && npx vitest run --config vitest.config.ts App.test.tsx -t "does not rerender App for other split sessions" — 2 通过(修复后)
  • 变异探针:在 App.tsx 被变异的情况下运行同一组聚焦测试 — 2 失败(符合预期);还原后 — 2 通过
  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过

🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.23.1

@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Sep 8, 2026
@qwen-code-dev-bot

qwen-code-dev-bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator Author

✅ AutoFix round 2 finished — view run. See this round's report below.

中文说明

✅ AutoFix 第 2 轮已完成 —— 查看运行。本轮报告见下方。

)

The "does not rerender App for other split sessions" tests asserted
mockUseDaemonSessionActivityBridge toHaveBeenCalled() after rerender(),
but nothing cleared the spy since the top-level beforeEach mockReset(),
so it already held setup-time calls from renderApp()/flush()/the split
click and the assertion passed whether or not the rerender rendered App.
Clearing it immediately before rerender() makes the guard measure that
render only; the trailing mockClear() stays to isolate the loop below.

Mutation-verified: with the clear in place, deleting rerender() or the
useDaemonSessionActivityBridge() call in App.tsx turns both
parameterized cases red at the guard (both previously stayed green).

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-dev-bot
qwen-code-dev-bot dismissed a stale review via daf3d6d September 8, 2026 21:22
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

🤖 Addressed the latest review feedback (round 1/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/10 轮)。改动内容与我反驳保留之处如下:

Address review round — PR #11406

Feedback dispositions

[rc:3962062547] — [Suggestion] Vacuous toHaveBeenCalled() guard in split rerender tests — Implemented

Claim (checkable): In App session callbacks > does not rerender App for other split sessions (outer pending: %s), the expect(mockUseDaemonSessionActivityBridge).toHaveBeenCalled() guard cannot fail: nothing clears the spy between the top-level beforeEach mockReset() (App.test.tsx:9393) and the assertion, so it already holds ~5 setup-time calls from renderApp(), the two flush() calls, and the split-view click. The assertion passes whether or not the rerender() two lines above actually rendered App, and a future refactor that stops re-invoking the hook on rerender would ship green.

Reproduction before implementing (probe A, unmodified HEAD): deleted rerender(); at App.test.tsx:28938 and ran the two parameterized cases → Tests 1 failed | 1 passed; the outer pending: false case passed entirely and the only failure (outer pending: true) was the loop's not.toHaveBeenCalled() — the line-28940 guard passed in both cases. The vacuous-guard claim is reproduced.

Fix applied: added mockUseDaemonSessionActivityBridge.mockClear(); immediately before rerender(); (with a one-line why-comment), so the guard measures only the render it precedes. The trailing mockClear() is kept (it isolates the not.toHaveBeenCalled() loop), both clears are mockClear() (never mockReset() — the implementation installed in beforeEach at 9394-9397 must survive), and the shared assertion at App.test.tsx:11045 is untouched. Net change: +2 lines in one test file.

Acceptance mutations (with the fix in place):

  • Probe B — deleted rerender(); → both cases red at the guard: AssertionError: expected "spy" to be called at least once (App.test.tsx:28941:50 in the mutated file), Tests 2 failed. Previously the same mutation left the guard green in both cases.
  • Probe C — replaced the useDaemonSessionActivityBridge(...) call in App.tsx:3304 with a static value → both cases red with the same assertion, Tests 2 failed.
  • Both mutations reverted after probing; the committed tree contains only the test-side fix.

Review-body note [rv:5146791177]: "Partially reviewed — reverse audit stopped by the review time budget." Nothing actionable beyond the inline suggestion above; disclosed gap noted.

Issue-level comment [ic:5590748595]: automated web-shell visual preview (base vs head screenshots). Informational only; no action.

Conflict notes

--conflict false; no merge performed, none needed.

Verification

  • npm run build — passed (run once before the fix to provision missing workspace dist/ prerequisites for the test runner, and re-run after the fix — both exit 0)
  • npm run typecheck — passed
  • npm run lint — passed (repo-wide; the changed file also passes npx eslint individually)
  • npx vitest run --config vitest.config.ts client/App.test.tsx (packages/web-shell, full file) — 796 passed
  • npx vitest run --config vitest.config.ts client/App.test.tsx -t "does not rerender App for other split sessions" — 2 passed | 794 skipped (baseline before fix: identical counts)
  • Mutation probe A (pre-fix, rerender() deleted) — guard stayed green in both cases, reproducing the reported defect
  • Mutation probe B (post-fix, rerender() deleted) — both cases red at the guard
  • Mutation probe C (post-fix, hook call deleted in App.tsx) — both cases red at the guard
  • No settings source changed (settingsSchema.ts/settings.ts untouched) — npm run generate:settings-schema not required
  • Change is test-only inside an existing unit-test file — no bundled-CLI or integration-harness behavior affected, integration tests not required
中文说明

评审处理轮次 — PR #11406

反馈处置

[rc:3962062547] — [Suggestion] split 重渲染测试中的 toHaveBeenCalled() 守卫恒真 —— 已实施

主张(可验证): 在 App session callbacks > does not rerender App for other split sessions (outer pending: %s) 中,expect(mockUseDaemonSessionActivityBridge).toHaveBeenCalled() 守卫不可能失败:从顶层 beforeEach 的 mockReset()(App.test.tsx:9393)到该断言之间没有任何地方清空 spy,因此它已累积约 5 次准备期调用(来自 renderApp()、两次 flush() 和打开 split view 的点击)。无论上方两行的 rerender() 是否真的渲染了 App,该断言都会通过;若后续重构使该 hook 不再随重渲染被调用,回归会带着绿灯合入。

实施前的复现(探针 A,未修改的 HEAD): 删除 App.test.tsx:28938 的 rerender(); 并运行两个参数化用例 → Tests 1 failed | 1 passed;outer pending: false 用例整体通过,唯一失败(outer pending: true)来自循环中的 not.toHaveBeenCalled() —— 第 28940 行的守卫在两个用例中都保持绿色。恒真守卫的主张得到复现。

已应用的修复: 在 rerender(); 之前紧邻处新增 mockUseDaemonSessionActivityBridge.mockClear();(附一行说明性注释),使守卫只度量它前面那一次渲染。保留原有的后置 mockClear()(它为下方的 not.toHaveBeenCalled() 循环做隔离);两处清空均为 mockClear()(绝不用 mockReset() —— beforeEach 在 9394-9397 行安装的实现必须保留);App.test.tsx:11045 处共用的断言未改动。净改动:单个测试文件 +2 行。

验收变异(在修复之上):

  • 探针 B —— 删除 rerender(); → 两个用例都在守卫处变红:AssertionError: expected "spy" to be called at least once(变异后文件的 App.test.tsx:28941:50),Tests 2 failed。此前同样的变异让守卫在两个用例中保持绿色。
  • 探针 C —— 将 App.tsx:3304 的 useDaemonSessionActivityBridge(...) 调用替换为静态值 → 两个用例同样以该断言变红,Tests 2 failed。
  • 两个变异在验证后均已还原;提交的代码树只包含测试侧的修复。

评审正文说明 [rv:5146791177]: "部分审查 —— 反向审计因评审时间预算停止。" 除上述行内建议外无其他可执行项;已披露的审查缺口已记录。

Issue 级评论 [ic:5590748595]: 自动化的 web-shell 视觉预览(base 与 head 的截图对比)。仅供参考,无需处理。

冲突说明

--conflict false;未执行合并,也不需要。

验证

  • npm run build —— 通过(修复前运行一次,用于补齐测试运行器缺失的工作区 dist/ 前置构建;修复后再次运行 —— 两次均退出码 0)
  • npm run typecheck —— 通过
  • npm run lint —— 通过(全仓库;改动文件单独跑 npx eslint 也通过)
  • npx vitest run --config vitest.config.ts client/App.test.tsx(packages/web-shell,整个文件)—— 796 通过
  • npx vitest run --config vitest.config.ts client/App.test.tsx -t "does not rerender App for other split sessions" —— 2 通过 | 794 跳过(修复前基线计数完全相同)
  • 变异探针 A(修复前,删除 rerender())—— 守卫在两个用例中保持绿色,复现了所报告的缺陷
  • 变异探针 B(修复后,删除 rerender())—— 两个用例都在守卫处变红
  • 变异探针 C(修复后,删除 App.tsx 中的 hook 调用)—— 两个用例都在守卫处变红
  • 未改动 settings 源文件(settingsSchema.ts/settings.ts 未触碰)—— 无需运行 npm run generate:settings-schema
  • 改动仅为既有单元测试文件内的测试改动 —— 不影响打包 CLI 或集成测试行为,无需运行集成测试

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

🧵 Resolved all 1 selected review thread(s). · 已关闭全部选中的 1 条评审线程。

Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.23.1

@wenshao

wenshao commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix round — no action required

Feedback triage

Every item in this round's feedback was classified; none is actionable:

  • [rv:5147795250] @qwen-code-ci-bot review (COMMENTED) — "Partially reviewed — gaps disclosed." The review ledger records "findings": [] and "posted": 0, and the feedback contains no inline comments. The two candidate findings it mentions still carried the — [unverified] tag when the review loop ended and were never posted, so there is no checkable claim (no named input, state, or wrong outcome) to reproduce or fix. Disposition: nothing actionable — informational review status.
  • [ic:5593017724] @qwen-code-ci-bot issue-level comment — a status notice that sandboxed verification is running. It requests no change. Disposition: nothing actionable.
  • Failed checks / still-red checks — none listed.
  • Diff growth this window — source 0 / test 2 net lines, within budget; no brake engaged.

Prior inline finding re-verified against the current head

The PR's only inline comment, [rc:3962062547] (Suggestion, from the previous evaluation window), was addressed by commit daf3d6d97d ("test(web-shell): Clear spy before rerender so the guard can fail"), which added mockUseDaemonSessionActivityBridge.mockClear() immediately before rerender() in packages/web-shell/client/App.test.tsx and kept the trailing clear. I re-ran its acceptance criterion as a mutation probe on the current head (daf3d6d97d):

  • Probe: neutralized the rerender(); call (App.test.tsx:28940) and ran the focused test. Both parameterized cases went red exactly at the guard line: AssertionError: expected "spy" to be called at least once at App.test.tsx:28942 — Tests 2 failed | 794 skipped, matching the reviewer's "ARM D2" expectation.
  • Restore: git checkout -- packages/web-shell/client/App.test.tsx; git status clean; reran the focused test — Tests 2 passed | 794 skipped.

The previously landed fix therefore provably holds: the positive guard now measures the rerender alone, and the three not.toHaveBeenCalled() assertions in the loop are no longer vacuous.

Outcome

No commit was made; the working tree is unchanged. The round contains no verified finding, no failing check, and no maintainer request — nothing to implement, decline, defer, or escalate.

Verification

  • npm run build (repo root) — passed. Required first: this runner had several workspace packages unbuilt (acp-bridge, sdk-typescript, web-shell, …), so the focused vitest run initially failed at import resolution for @qwen-code/sdk/daemon. (A direct npm run build -w @qwen-code/sdk attempt failed on missing @qwen-code/acp-bridge/* type declarations — a dependency-ordering artifact; the root build builds in dependency order and succeeded.)
  • npx vitest run --config vitest.config.ts client/App.test.tsx -t "does not rerender App for other split sessions" (from packages/web-shell) — passed: Test Files 1 passed (1), Tests 2 passed | 794 skipped (796).
  • Mutation probe (described above) — guard fails red without rerender() in both parameterized cases; file restored byte-identical and rerun green.
中文说明

Autofix 本轮——无需改动

反馈分诊

本轮反馈中的每一项都已分类;均不具备可执行性:

  • [rv:5147795250] @qwen-code-ci-bot 评审(COMMENTED)——"部分完成审查,缺口已披露"。评审账本记录 "findings": [] 且 "posted": 0,反馈中没有任何行内评论。它提到的两条候选发现在评审循环结束时仍带有 — [unverified] 标记,且从未被发布,因此不存在可复现或可修复的可检验断言(没有指明输入、状态或错误结果)。处置:无可执行项——仅为信息性评审状态。
  • [ic:5593017724] @qwen-code-ci-bot issue 级评论——一条"沙箱验证正在运行"的状态通知,不要求任何改动。处置:无可执行项。
  • 失败检查 / 持续红色检查——均未列出。
  • 本窗口 diff 增长——源码净增 0 行 / 测试净增 2 行,在预算内;未触发刹车。

针对当前 head 复核了此前的行内发现

本 PR 唯一的行内评论 [rc:3962062547](Suggestion,来自上一个评估窗口)已由提交 daf3d6d97d("test(web-shell): Clear spy before rerender so the guard can fail")处理:该提交在 packages/web-shell/client/App.test.tsx 的 rerender() 之前立即加入了 mockUseDaemonSessionActivityBridge.mockClear(),并保留了原有的后置清空。我在当前 head(daf3d6d97d)上把它的验收标准作为变异探针重新跑了一遍:

  • 探针: 注释掉 rerender(); 调用(App.test.tsx:28940)后运行聚焦测试。两个参数化用例都精确地在守卫行变红:App.test.tsx:28942 处报 AssertionError: expected "spy" to be called at least once——Tests 2 failed | 794 skipped,与评审者"ARM D2"的预期完全一致。
  • 还原: 执行 git checkout -- packages/web-shell/client/App.test.tsx;git status 干净;重新运行聚焦测试——Tests 2 passed | 794 skipped。

因此,此前落地的修复可被证明仍然有效:正向守卫现在只度量本次 rerender,循环中的三个 not.toHaveBeenCalled() 断言也不再是永真空断言。

结论

本轮未创建任何提交;工作树保持不变。本轮没有已验证的发现、没有失败的检查、也没有维护者的要求——没有需要实现、拒绝、推迟或升级处理的事项。

验证

  • npm run build(仓库根目录)——通过。必须先执行:该运行器上有多个 workspace 包尚未构建(acp-bridge、sdk-typescript、web-shell 等),因此聚焦 vitest 最初在解析 @qwen-code/sdk/daemon 导入时失败。(直接执行 npm run build -w @qwen-code/sdk 的尝试因缺少 @qwen-code/acp-bridge/* 类型声明而失败——这是依赖顺序问题;根目录构建按依赖顺序执行并取得成功。)
  • npx vitest run --config vitest.config.ts client/App.test.tsx -t "does not rerender App for other split sessions"(在 packages/web-shell 下执行)——通过:Test Files 1 passed (1),Tests 2 passed | 794 skipped (796)。
  • 变异探针(如上所述)——去掉 rerender() 后,守卫在两个参数化用例中均变红;文件已逐字节还原并复测为绿。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.23.1

@wenshao

wenshao commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Maintainer verification — local build + real test execution

Verified on a dedicated worktree at the PR head daf3d6d97d, fresh npm ci (Linux, Node v22.22.2, npm 10.9.7) — same Node major as the failing CI lane. Every number below comes from a real run on this machine; nothing is taken from the description.

Verdict: recommend merge. The fix is correct, minimal, and — with the second commit — non-vacuous. Two documentation nits below, neither blocking.


1. The failure reproduces exactly, and it is the whole failure

Restoring only App.test.tsx from 70cf363395 (the commit #11404 reports) and running the two tests reproduces the CI error verbatim at App.test.tsx:28940:

base red

Running the whole web-shell suite at that content gives numbers byte-identical to CI job 102182712398:

CI @ 70cf363395 Local, same content Local, PR head
Test Files 1 failed | 287 passed (288) 1 failed | 287 passed (288) 288 passed (288)
Tests 2 failed | 6710 passed (6712) 2 failed | 6710 passed (6712) 6712 passed (6712)

parity

This closes the PR's own "not validated" caveat: the red run contained no other failures — the two does not rerender App for other split sessions cases were the only red tests in the only red suite of the only red job. Fixes #11404 is exact, not approximate.

It is also still live. Five other open PRs currently have a red Test (ubuntu-latest, Node 22.x) for this same ReferenceError (#11397, #11396, #11392, #11369, #11360), so this is blocking merge signal repo-wide, not just main's own lane.

2. PR head is green

pr green

3. The assertions bite — 7 mutants, all behaving as predicted

mutants

Mutant Result Where it reds What it proves
(control) PR head, unmutated 2 passed — baseline
delete rerender() 2 failed the toHaveBeenCalled() guard the guard now measures that render — the acceptance criterion from round 1 is met
delete rerender(), on commit 1 only 1 failed / 1 passed the loop, guard stays green reproduces the round-1 finding: without commit 2 the guard cannot fail
commit 1 only, unmutated 2 passed — commit 1 alone already clears the red; commit 2 is a strictness fix, not a correctness prerequisite
delete the trailing mockClear() 2 failed the loop the trailing clear is load-bearing, not redundant
added clear → mockReset() 2 failed TypeError: Cannot destructure property 'hasActivePrompt' it must be mockClear, never mockReset
production: drop useDaemonSessionActivityBridge(...) from App's render body 2 failed the guard the guard is bound to the real hook App calls, not to test scaffolding
production: make every pending-panes report toggle outerSplitPanePending 2 failed the loop the negative assertions genuinely detect an App rerender

Instrumenting the spy confirms the mechanism numerically. Before commit 2 the guard was reading 6–7 accumulated setup-time calls; after it, exactly the 1 call made by the render it claims to observe:

census

4. Post-merge safety

packages/web-shell is unchanged between this PR's base and current main (1f890086f1), the merge is conflict-free, and the merged App.test.tsx blob hashes identically to the file I tested — so the local green above is the post-merge state, not an approximation of it.

5. How this reached main (context, not a defect in this PR)

Worth recording, because the PR is right that its own sandbox could not determine it:

escape

  1. feat(web-shell): Improve split-view session navigation #11250's branch was green and correct: at its head 52484fecf3 the mock was declared, wired into the module mock, and named mockUseDaemonActivePromptBridge.
  2. 51 minutes later fix(web-shell): show session active work #11267 landed on main and renamed the hook and its mock to useDaemonSessionActivityBridge / mockUseDaemonSessionActivityBridge.
  3. The squash-merge took main's renames and feat(web-shell): Improve split-view session navigation #11250's three new lines. Textually clean, semantically broken — a classic semantic conflict.
  4. No static gate can see it: packages/web-shell/tsconfig.json excludes client/**/*.test.tsx, so npm run typecheck exits 0 on the broken file, and eslint exits 0 too (no-undef is explicitly off for this file — verified with eslint --print-config). Only running the test catches it.
  5. ci.yml has no push trigger and — per its own header comment — the merge queue is not enabled, so nothing re-validated the squashed tree — the after-the-fact main-failure watcher (Main CI failed: Qwen Code CI on 70cf3633950b #11404) was the first thing to notice.

The systemic fix (a merge queue, or typechecking test files) is out of scope here; this PR is the right immediate repair.

6. Non-blocking nits before squash

  1. The description is stale relative to the second commit. It still says "a three-line, test-only edit" and describes the fix purely as repointing three assertions; the diff is +5/−3 and the added mockClear() — the change that actually makes the guard capable of failing — is not mentioned anywhere in the body. Since this squashes into one commit message, the body is worth a one-line update.
  2. The "Not validated / out of scope" bullet can be struck. It says whether the red run contained unrelated flaky failures "cannot be determined from here". It can: it did not — §1 above.
中文说明

维护者验证 —— 本地构建 + 真实运行

在 PR head daf3d6d97d 的独立 worktree 上、全新 npm ci(Linux,Node v22.22.2,npm 10.9.7,与失败的 CI 车道同一 Node 大版本)完成验证。下列所有数字均来自本机真实运行,未采信 PR 描述。

结论:建议合并。 修复正确、最小,且在第二个提交加入后不再是空断言。文末两条仅为文档层面的小问题,均不阻塞。


1. 失败可精确复现,且它就是全部失败

仅把 App.test.tsx 还原到 70cf363395(#11404 所报的提交)再运行这两个测试,逐字复现 CI 的报错,位置 App.test.tsx:28940(见上方第一张截图)。

在该内容上运行整个 web-shell 套件,结果与 CI job 102182712398 完全一致:

CI @ 70cf363395 本地同内容 本地 PR head
Test Files 1 failed | 287 passed (288) 1 failed | 287 passed (288) 288 passed (288)
Tests 2 failed | 6710 passed (6712) 2 failed | 6710 passed (6712) 6712 passed (6712)

这填补了 PR 自述的"未验证"缺口:那次红色运行中没有其他失败——两个 does not rerender App for other split sessions 用例是唯一红色 job 里唯一红色套件中唯一红色的测试。Fixes #11404 是精确的,不是近似的。

问题目前仍在扩散:另有 5 个其他开放 PR(#11397、#11396、#11392、#11369、#11360)的 Test (ubuntu-latest, Node 22.x) 正因同一个 ReferenceError 变红。它阻塞的是全仓库的合并信号,而不只是 main 自己的车道。

2. PR head 全绿

两个目标测试通过;整套 web-shell 套件 288/288 文件、6712/6712 测试通过(见上方第二张截图)。

3. 断言确实有效 —— 7 个变异体,全部如预期

变异体 结果 变红位置 说明
(对照) PR head 未变异 2 passed — 基线
删除 rerender() 2 failed toHaveBeenCalled() 守卫 守卫现在确实度量该次渲染——第 1 轮评审提出的验收标准已满足
删除 rerender(),仅在第 1 个提交上 1 failed / 1 passed 循环处,守卫仍绿 复现第 1 轮的发现:没有第 2 个提交,守卫不可能失败
仅第 1 个提交,未变异 2 passed — 仅靠第 1 个提交即可消除红色;第 2 个提交是严格性修复,而非正确性前提
删除后置的 mockClear() 2 failed 循环处 后置清空是承重的,并非冗余
新增的清空改为 mockReset() 2 failed TypeError: Cannot destructure property 'hasActivePrompt' 必须是 mockClear,绝不能是 mockReset
生产代码: 从 App 渲染体中移除 useDaemonSessionActivityBridge(...) 2 failed 守卫处 守卫绑定的是 App 真正调用的 hook,而非测试脚手架
生产代码: 让每次 pending-panes 上报都翻转 outerSplitPanePending 2 failed 循环处 负向断言确实能检测到 App 的重渲染

对 spy 的插桩计数从数值上印证了机制:第 2 个提交之前,守卫读到的是 6–7 次挂载期累积调用;加入之后,恰好只有它声称观察的那次渲染所产生的 1 次调用(见上方第四张截图)。

4. 合并后的安全性

本 PR 的 base 与当前 main(1f890086f1)之间,packages/web-shell 没有任何改动;合并无冲突;合并树中 App.test.tsx 的 blob 哈希与我实测的文件完全一致——因此上面的本地绿色结果就是合并后的状态,而不是对它的近似。

5. 它是怎么进入 main 的(背景信息,非本 PR 的缺陷)

值得记录,因为 PR 确实说明了它自己的沙箱无法判定这一点:

  1. feat(web-shell): Improve split-view session navigation #11250 的分支本身是绿色且正确的:在其 head 52484fecf3 上,该 mock 已声明、已接入模块 mock,名字是 mockUseDaemonActivePromptBridge。
  2. 51 分钟后 fix(web-shell): show session active work #11267 合入 main,把该 hook 及其 mock 重命名为 useDaemonSessionActivityBridge / mockUseDaemonSessionActivityBridge。
  3. squash 合并同时采纳了 main 的重命名和 feat(web-shell): Improve split-view session navigation #11250 新增的三行。文本上无冲突,语义上已损坏——典型的语义冲突。
  4. 没有任何静态门禁能发现它:packages/web-shell/tsconfig.json 排除了 client/**/*.test.tsx,因此 npm run typecheck 在损坏文件上退出码为 0,eslint 同样为 0(该文件的 no-undef 被显式关闭——已用 eslint --print-config 核实)。只有真正运行测试才能捕获。
  5. ci.yml 没有 push 触发器,且据其文件头注释合并队列亦未启用,因此没有任何环节复验 squash 后的树——最先发现的是事后的主干失败监测(Main CI failed: Qwen Code CI on 70cf3633950b #11404)。

系统性修复(启用合并队列,或把测试文件纳入类型检查)不属于本 PR 范围;本 PR 是正确的即时修复。

6. squash 前的两条非阻塞小问题

  1. 描述相对第 2 个提交已经过时。 正文仍写着"仅三行、仅测试文件的改动",并把修复完整描述为"重新指向三处断言";而实际 diff 是 +5/−3,且新增的 mockClear()——真正让守卫具备失败能力的那处改动——在正文中完全没有提及。由于这会 squash 成单条提交信息,正文值得补一句。
  2. "未验证 / 超出范围"那一条可以删掉。 它写的是"红色运行中是否还包含无关 flaky 失败,从这里无法判定"。是可以判定的:没有——见上文第 1 节。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix round — no code change

Triaged all feedback newer than the last evaluation. There is exactly one new
item: the maintainer verification comment [ic:5593269567] (@wenshao), which
recommends merge and raises two non-blocking nits. No inline comments, no
reviews requesting changes, and no failed checks are pending. The head commit
daf3d6d97d needs no code change, so nothing was committed.

Feedback disposition

[ic:5593269567] — maintainer verification, recommend merge — acknowledged

The comment verifies the fix end-to-end at the PR head: the original
ReferenceError reproduces at the base content, the full web-shell suite is
green at head (288/288 files, 6712/6712 tests, byte-identical to CI), and 7
mutation probes confirm the assertions are non-vacuous — including that the
round-1 finding (the toHaveBeenCalled() guard that could not fail) is now
load-bearing. This closes the loop on the earlier inline finding
(rc:3962062547), which head commit daf3d6d97d resolved; nothing in the
comment asks for a code change.

Nit 1 — PR body is stale relative to the second commit — valid, not actionable from this mode (maintainer to apply at squash)

Verified against the real diff: it is +5/−3, and the added mockClear() —
the change that makes the guard capable of failing — is indeed not described
in the PR body. The nit is about the PR description, a GitHub-side
artifact. This mode cannot edit it: the agent has no GitHub credentials, and
the address-review path never updates a PR body (pr-body.md is consumed only
at PR creation in the develop-issue path — .github/workflows/qwen-autofix.yml).
Suggested resolution for the merging maintainer: when squashing, add one line
to the message noting that the second commit also clears the activity-bridge
spy before the rerender so the guard measures only that render.

Nit 2 — "Not validated / out of scope" bullet can be struck — valid, same disposition

The maintainer's §1 supplies the missing evidence: the red CI run contained no
other failures, so the caveat is resolvable rather than undeterminable. Same
constraint as Nit 1 — the bullet lives in the PR body, which this mode cannot
edit. Suggested resolution for the merging maintainer: strike that bullet when
finalizing the squash message.

Why no commit

Every actionable code finding from prior rounds is already resolved at the
head commit, the two remaining nits concern PR metadata rather than code, and
all required checks are green. Committing anything here would add diff without
addressing any feedback.

Verification

No build or test commands were run this round because no file was modified;
read-only verification performed:

  • git diff origin/main...HEAD — confirmed the full PR diff is the +5/−3 test-only change in packages/web-shell/client/App.test.tsx, matching the maintainer's description.
  • git status --short — working tree clean; nothing to commit.
  • checks.json scan — no failing checks (only the superseded review-scan job shows CANCELLED).
  • Grep of App.test.tsx — mockUseDaemonSessionActivityBridge is declared (line 723), wired into the module mock (line 1625), and referenced by the fixed assertions (lines 28939–28946); the base-branch ReferenceError cannot recur in this file.
  • .github/workflows/qwen-autofix.yml inspection — confirmed the address-review path has no PR-body update mechanism, so the two nits are escalated to the merging maintainer rather than silently dropped.

Independent test evidence at this exact head comes from the maintainer's
verification run (full suite green, 7/7 mutants behave as predicted), and the
two approving reviews.

中文说明

Autofix 本轮 —— 无代码改动

已分拣上一轮评估之后的全部新反馈。新反馈只有一条:维护者验证评论 [ic:5593269567](@wenshao),结论是建议合并,并提出两条不阻塞的小问题。没有待处理的内联评论,没有要求修改的评审,也没有失败的检查。头提交 daf3d6d97d 无需任何代码改动,因此本轮没有提交。

反馈处置

[ic:5593269567] —— 维护者验证,建议合并 —— 已知悉

该评论在 PR head 上对修复做了端到端验证:原始 ReferenceError 在 base 内容上可复现;整个 web-shell 套件在 head 上全绿(288/288 个文件、6712/6712 个测试,与 CI 逐字节一致);7 个变异探针确认断言并非空转——包括第 1 轮发现的问题(toHaveBeenCalled() 守卫此前不可能失败)现已真正承重。这也闭环了此前的内联发现(rc:3962062547),该发现已由头提交 daf3d6d97d 解决;评论中没有任何要求改动代码的内容。

小问题 1 —— PR 描述相对第二个提交已过时 —— 属实,但本模式无法操作(由合并维护者在 squash 时处理)

已与真实 diff 核对:diff 为 +5/−3,且新增的 mockClear()——真正让守卫具备失败能力的改动——确实未在 PR 正文中提及。该问题针对的是 PR 描述,属于 GitHub 侧的产物。本模式无法编辑它:agent 没有 GitHub 凭据,且 address-review 路径从不更新 PR 正文(pr-body.md 仅在 develop-issue 路径创建 PR 时被消费——见 .github/workflows/qwen-autofix.yml)。给合并维护者的建议:squash 时在提交信息中补一句,说明第二个提交还在 rerender 前清空了 activity-bridge 的 spy,使守卫只度量那一次渲染。

小问题 2 —— "Not validated / out of scope" 项目符号可删除 —— 属实,处置同上

维护者评论的第 1 节补上了缺失的证据:红色 CI 运行中没有其他失败,因此该保留条款是可以判定的,而非无法判定。约束与小问题 1 相同——该项目符号在 PR 正文中,本模式无法编辑。给合并维护者的建议:在最终确定 squash 提交信息时删掉该项目符号。

为什么不提交

此前各轮所有可操作的代码发现均已在头提交中解决;剩余的两条小问题针对的是 PR 元数据而非代码;所有必需检查均为绿色。此时提交任何内容只会增加 diff,而不解决任何反馈。

验证

本轮未修改任何文件,因此未运行构建或测试命令;已完成的只读验证如下:

  • git diff origin/main...HEAD —— 确认完整 PR diff 就是 packages/web-shell/client/App.test.tsx 中 +5/−3 的纯测试改动,与维护者描述一致。
  • git status --short —— 工作树干净,无待提交内容。
  • checks.json 扫描 —— 无失败检查(仅被取代的 review-scan 任务显示为 CANCELLED)。
  • 对 App.test.tsx 的检索 —— mockUseDaemonSessionActivityBridge 已声明(第 723 行)、已接入模块 mock(第 1625 行),并被修复后的断言引用(第 28939–28946 行);base 分支的 ReferenceError 不会在该文件复发。
  • 检查 .github/workflows/qwen-autofix.yml —— 确认 address-review 路径没有 PR 正文更新机制,因此两条小问题升级为交给合并维护者处理,而非被静默丢弃。

该 head 上的独立测试证据来自维护者的验证运行(全套件绿色,7/7 变异体行为均如预期)以及两份批准评审。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.23.1

@wenshao
wenshao added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 3a75f37 Sep 9, 2026
81 of 82 checks passed
wenshao pushed a commit that referenced this pull request Sep 9, 2026
… (#11406)

* fix(web-shell): Replace undefined mock in split rerender tests (#11404)

The two "does not rerender App for other split sessions" tests referenced
mockUseDaemonActivePromptBridge, which is not defined anywhere in
App.test.tsx — a deterministic ReferenceError that failed the main-branch
CI Test job at 70cf363. The intended render spy is
mockUseDaemonSessionActivityBridge: App calls useDaemonSessionActivityBridge
on every render, and this file already uses that mock as the render-call
witness elsewhere.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(web-shell): Clear spy before rerender so the guard can fail (#11406)

The "does not rerender App for other split sessions" tests asserted
mockUseDaemonSessionActivityBridge toHaveBeenCalled() after rerender(),
but nothing cleared the spy since the top-level beforeEach mockReset(),
so it already held setup-time calls from renderApp()/flush()/the split
click and the assertion passed whether or not the rerender rendered App.
Clearing it immediately before rerender() makes the guard measure that
render only; the trailing mockClear() stays to isolate the loop below.

Mutation-verified: with the clear in place, deleting rerender() or the
useDaemonSessionActivityBridge() call in App.tsx turns both
parameterized cases red at the guard (both previously stayed green).

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
pull Bot pushed a commit to mcx/qwen-code that referenced this pull request Sep 11, 2026
…#11276)

* feat(web-shell): add web previews with saved delivery history

* fix(serve): register saved preview telemetry route

* test(cli): update telemetry route drift count

* fix(web-shell): reclaim snapshot files and address preview findings (QwenLM#11276)

* fix(web-shell): Replace undefined mock in split rerender tests (QwenLM#11404) (QwenLM#11406)

* fix(web-shell): Replace undefined mock in split rerender tests (QwenLM#11404)

The two "does not rerender App for other split sessions" tests referenced
mockUseDaemonActivePromptBridge, which is not defined anywhere in
App.test.tsx — a deterministic ReferenceError that failed the main-branch
CI Test job at 70cf363. The intended render spy is
mockUseDaemonSessionActivityBridge: App calls useDaemonSessionActivityBridge
on every render, and this file already uses that mock as the render-call
witness elsewhere.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(web-shell): Clear spy before rerender so the guard can fail (QwenLM#11406)

The "does not rerender App for other split sessions" tests asserted
mockUseDaemonSessionActivityBridge toHaveBeenCalled() after rerender(),
but nothing cleared the spy since the top-level beforeEach mockReset(),
so it already held setup-time calls from renderApp()/flush()/the split
click and the assertion passed whether or not the rerender rendered App.
Clearing it immediately before rerender() makes the guard measure that
render only; the trailing mockClear() stays to isolate the loop below.

Mutation-verified: with the clear in place, deleting rerender() or the
useDaemonSessionActivityBridge() call in App.tsx turns both
parameterized cases red at the guard (both previously stayed green).

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(cli): complete worktree ACP config fixture

* fix(acp-bridge): keep restored saved-webpage records when snapshot retain fails (QwenLM#11276)

* test(cli): make dangling-include screen test host-independent (QwenLM#11276)

The test asserts runOneMutant's detail names vitest, which only holds
when findVitestBin throws "not found" from the bare fixture — but Node
resolution walks up from the probe tree, so a node_modules above the
runner's TMPDIR (observed on self-hosted CI, where jobs share one /tmp)
resolves a vitest, the probe then runs for real, and the detail carries
no 'vitest'. Plant a shadow vitest that declares no bin in the fixture:
the innermost node_modules wins resolution on every host, so the throw
is deterministic. Same pattern as the runControlMutant shadow in this
file.

* test(web-shell): align branding fixtures with workspace and sidebar behavior

* fix(acp): preserve non-strict ingestion on snapshot reference errors

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Main CI failed: Qwen Code CI on 70cf3633950b

2 participants