Skip to content

feat(review): emit the Step 3A fan-out as a generated workflow script - #10119

Merged
wenshao merged 14 commits into
QwenLM:mainfrom
qqqys:feat/review-emit-workflow-v2
Aug 28, 2026
Merged

feat(review): emit the Step 3A fan-out as a generated workflow script#10119
wenshao merged 14 commits into
QwenLM:mainfrom
qqqys:feat/review-emit-workflow-v2

Conversation

@qqqys

@qqqys qqqys commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds qwen review emit-workflow, a deterministic subcommand that writes Review Step 3A's complete agent fan-out as a runnable workflow script. It builds the roster exactly the way agent-prompt --roster does — same plan, same brief builder, same prompts, same recorded delivery evidence — and then, instead of printing one block per agent for the orchestrator to copy into thirteen separate agent calls, bakes those prompts into a script whose fixed body dispatches every entry with parallel(), pins each agent to the review worktree, sets the review subagent type, and reports by roster key any agent that returned nothing. The script is written under the generated-scripts root that the Workflow loader trusts since #9987 ($QWEN_CODE_PROJECT_DIR/workflows/generated/review/<session>/), so it is loadable by path, never a slash command, and needs no cleanup sweep. The command prints one line the orchestrator would act on: the absolute scriptPath.

Nothing routes through the command yet. The review skill is untouched and still builds its roster with agent-prompt --roster; teaching the skill to ask for the workflow path, and the environment gates that decide when it does, are the next change. This one exists so the generated dispatch can be evaluated on its own.

Why it's needed

Step 3's fragile layer is the orchestrator: a model held to conventions — issue every agent call in one response, use the CLI's blocks verbatim, never shrink the roster — each of which has been broken in a real run and each of which grew an after-the-fact gate. Emitting the fan-out as executable data makes roster width and prompt fidelity properties of a file rather than of model behavior, which is the direction #8769 sets out. The first attempt (#8943) carried this plus routing plus a cleanup subsystem forced by the loader's old boundary, and closed under its own weight; this is the emitter alone, re-landed on the root #9987 added, with the cleanup half gone because the location no longer needs it.

Reviewer Test Plan

How to verify

  • Inside a qwen session (so the harness has exported QWEN_CODE_PROJECT_DIR and QWEN_CODE_SESSION_ID), take any Step 1 plan for a small source change and run qwen review emit-workflow --plan <plan> [--rules <rules>]. Expected: stderr carries the same worktree-residue warnings agent-prompt --roster would print; stdout ends with scriptPath: <absolute path> under <project dir>/workflows/generated/review/<session>/qwen-review-<digest>.js. The script parses (node --check), opens with a pure-literal meta, contains one AGENTS entry per role the plan requires, and its check-coverage prompt records match agent-prompt --roster's byte for byte.
  • Run it against a territory-sized plan (over 500 source lines or 3,200 diff lines) or one whose size fields are missing. Expected: refused with a message naming the one-tool-result delivery bound, and nothing written — no script, no briefs, no records.
  • Run it outside a session (no QWEN_CODE_PROJECT_DIR). Expected: refused before any brief or record is written, naming the missing env contract.
  • Unit tests: cd packages/cli && npx vitest run src/commands/review/emit-workflow.test.ts src/commands/review/workflow-script.test.ts src/commands/review.test.ts — 43 passed (23 emitter, 15 script, 5 command registry). The script tests execute the generator's real output through an analogue of the sandbox, including prompts containing backticks, ${, backslashes and newlines. The whole src/commands/review/ directory: 104 files, 5,119 passed, 15 skipped.
  • End-to-end on the built CLI (Linux): a medium-effort local plan produced a 10-agent script; core's resolveSavedWorkflowScript({scriptPath}) loaded it, listSavedWorkflows did not list it, and compileWorkflowScript from the real workflow sandbox compiled it with meta.name = review-step-3a.

Evidence (Before & After)

N/A — new subcommand, nothing user-visible changes until the skill routes to it.

Tested on

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

Environment (optional)

Unit tests (vitest, Node 22) plus the built CLI run locally.

Risk & Scope

  • Main risk or tradeoff: the generated script returns every agent's text inside one Workflow tool result, where the hand-launched path gets one result per agent. That is why territory fan-outs are refused at the emitter rather than routed; the refusal is keyed on roster growth, so it lifts when the runtime gives a workflow result a fan-out-sized budget. Two functions in agent-prompt become exported so the emitter shares the builder instead of copying it; no behavior change for existing callers.
  • Not validated / out of scope: no routing (the skill does not call this), no env gates, no removal of a session's generated scripts (they sit in the runtime dir, one small file per plan, under a per-session directory a later change can sweep as a unit), no live-model A/B — that needs routing first.
  • Breaking changes / migration notes: none.

Linked Issues

Part of #8769 — Phase 1, the emitter half. Builds on #9987 (generated-scripts root), #8971 and #8972. Re-lands the emitter from #8943 without its routing and cleanup halves.

中文说明

本 PR 做了什么

新增确定性子命令 qwen review emit-workflow,把 Review Step 3A 的完整 agent 扇出写成一个可运行的 workflow 脚本。它构建 roster 的方式与 agent-prompt --roster 完全一致——同一份 plan、同一个 brief 构建函数、同样的 prompt、同样的投递记录——只是不再为每个 agent 打印一个 block 让 orchestrator 复制成十三次独立的 agent 调用,而是把这些 prompt 烘进一个脚本:脚本的固定主体用 parallel() 分派每一个条目,把每个 agent 钉在 review worktree 上,设置 review 专用的 subagent 类型,并按 roster key 报告任何没有返回内容的 agent。脚本写到 #9987 之后 Workflow loader 信任的生成脚本根目录下($QWEN_CODE_PROJECT_DIR/workflows/generated/review/<session>/),因此可以按路径加载、永远不会成为 slash command、也不需要清理扫描。命令只打印一行 orchestrator 需要的内容:绝对 scriptPath

目前没有任何东西路由到这个命令。review skill 原样未动,仍然用 agent-prompt --roster 构建 roster;教 skill 去请求 workflow 路径、以及决定何时这么做的环境开关,是下一个改动。本 PR 的目的是让生成的分派可以被独立评估。

为什么需要

Step 3 最脆弱的一层是 orchestrator:一个被约定束缚的模型——单条 response 发出全部 agent 调用、逐字使用 CLI 的 block、不得缩编 roster——每一条都在真实运行中被打破过,每一条都长出了一个事后门禁。把扇出写成可执行的数据,使 roster 宽度和 prompt 保真度成为文件的属性而不是模型行为,这正是 #8769 设定的方向。第一次尝试(#8943)把这部分和路由、以及 loader 旧边界逼出来的整套清理子系统放在一起,最终被自身重量压垮;本 PR 只包含 emitter 本身,重新落在 #9987 增加的根目录上,因为位置不再需要清理,那一半整个消失了。

评审验证计划

如何验证

  • 在 qwen 会话内(harness 已导出 QWEN_CODE_PROJECT_DIRQWEN_CODE_SESSION_ID),拿任意一个小型源码改动的 Step 1 plan,运行 qwen review emit-workflow --plan <plan> [--rules <rules>]。预期:stderr 打印与 agent-prompt --roster 相同的 worktree 残留警告;stdout 以 scriptPath: <绝对路径> 结尾,位于 <project dir>/workflows/generated/review/<session>/qwen-review-<digest>.js。脚本可解析(node --check),以纯字面量 meta 开头,AGENTS 中每个 plan 要求的角色各一条,其 check-coverage prompt 记录与 agent-prompt --roster 逐字节一致。
  • 对 territory 规模的 plan(源码超过 500 行或 diff 超过 3200 行)或缺少 size 字段的 plan 运行。预期:被拒绝,错误信息点名单结果投递的上限,且什么都不写——没有脚本、没有 brief、没有记录。
  • 在会话外运行(无 QWEN_CODE_PROJECT_DIR)。预期:在写入任何 brief 或记录之前被拒绝,并点名缺失的环境契约。
  • 单元测试:cd packages/cli && npx vitest run src/commands/review/emit-workflow.test.ts src/commands/review/workflow-script.test.ts src/commands/review.test.ts —— 43 通过(emitter 23、脚本 15、命令注册 5)。脚本测试通过沙箱的等价模拟执行生成器的真实输出,包括含反引号、${、反斜杠和换行的 prompt。整个 src/commands/review/ 目录:104 个文件,5119 通过,15 跳过。
  • 用构建出的 CLI 端到端(Linux):一个 medium 强度的本地 plan 产出了 10 个 agent 的脚本;core 的 resolveSavedWorkflowScript({scriptPath}) 成功加载,listSavedWorkflows 没有列出它,真实 workflow 沙箱的 compileWorkflowScript 编译通过,meta.name = review-step-3a

证据(前后对比)

N/A —— 新子命令,在 skill 路由到它之前没有用户可见的变化。

测试平台

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

环境(可选)

单元测试(vitest,Node 22)加本地构建的 CLI 运行。

风险与范围

  • 主要风险或取舍:生成的脚本把所有 agent 的文本放在一个 Workflow 工具结果里返回,而手工路径每个 agent 一个结果。这就是 territory 扇出在 emitter 处被拒绝而不是被路由的原因;拒绝以 roster 增长为依据,一旦 runtime 给 workflow 结果分配扇出规模的预算,它就会解除。agent-prompt 中的两个函数改为导出,以便 emitter 共享构建器而不是复制它;现有调用方行为不变。
  • 未验证 / 范围之外:不做路由(skill 不调用它)、不做环境开关、不删除会话的生成脚本(它们位于 runtime 目录,每个 plan 一个小文件,放在按会话划分的目录下,后续改动可以整体清扫)、不做真实模型 A/B——那需要先有路由。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

Part of #8769 —— Phase 1 的 emitter 一半。基于 #9987(生成脚本根目录)、#8971#8972。从 #8943 重新落地 emitter,去掉其路由和清理两部分。

https://claude.ai/code/session_017cUwuTey4APA8wAyAM6ScS

`qwen review emit-workflow` builds the roster the same way
`agent-prompt --roster` does — same plan, same `buildLaunch`, same briefs,
same prompts, same recorded delivery evidence — and writes those prompts into
a runnable workflow script instead of printing thirteen blocks for the
orchestrator to copy. The script lives under the generated-scripts root the
Workflow loader trusts since QwenLM#9987 (`$QWEN_CODE_PROJECT_DIR/workflows/
generated/review/<session>/`), so it is never a slash command and needs no
cleanup sweep.

The generated file is a fixed body plus three literals — the roster, the
worktree pin, the subagent type. No logic is generated, only data, and the
tests execute the generator's real output. A territory fan-out (Step 3B) and
an unsized plan are refused before anything is written, because a workflow
returns every agent through one tool result and a roster that grows with
the diff is silently truncated there.

Nothing routes through the command yet: the skill still builds its roster
with `agent-prompt --roster`. Routing is its own change.

Part of QwenLM#8769.

Claude-Session: https://claude.ai/code/session_017cUwuTey4APA8wAyAM6ScS
@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

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

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Gate re-check for the moved head — nine commits since the last pass (891b6ac3c88b60bd): one fix round answering the maintainer's live-stack verification, two test-hermeticity commits, and merges of main (the last one resolving a subcommand-registration conflict).

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

Problem / direction: unchanged from the first pass — the motivation is the observed orchestrator failure class documented in #8769 (fan-out conventions broken in real runs), and the direction issue specs this subcommand; the foundations (#9987, #8971, #8972) are merged. Since then the maintainer verified the emitter against a real session with real subagent dispatch and confirmed it does what it says.

Size: the one core file the previous increment carried (client.telemetrySwap.test.ts, a branch unblock for main's TS1117 duplicate) is no longer in the diff — main fixed itself via #10252, so the two converged. All ten files sit under packages/cli/src/commands/review/: ~690 production lines vs ~1,189 test lines, under the 1000-line advisory. Nothing to escalate.

Approach: the increment is exactly the response to the maintainer verification — his §B and §C fixed at the root, the empty-directory nit fixed for every refusal class, the last untested guard pinned, plus hermetic git fixtures for the probe tests and a keep-both-sides merge resolution in review.ts. Scope stays minimal: still no routing, no cleanup, no drive-by edits.

Risk: no high-risk path matches in the revert-correlation scan. No elevated risk signals.

Moving on to the incremental code review. 🔍

中文说明

对移动后的头部重新过门禁 —— 上一轮(891b6ac3c88b60bd)之后新增 9 个提交:一轮针对维护者实机验证的修复、两个测试密封性提交,以及若干次 main 合并(最后一次解决了子命令注册冲突)。

模板 ✓ 未变 —— 各必填部分齐全,中英文对照完整。

问题 / 方向: 与首轮一致 —— 动机是 #8769 记录的已观测 orchestrator 失败类别(扇出约定在真实运行中被打破),方向 issue 明确写明了这个子命令;地基(#9987#8971#8972)已合入。此后维护者已在真实会话 + 真实 subagent 分派下验证过 emitter,确认它做到了声称的事。

规模: 上一轮增量中唯一的核心文件(client.telemetrySwap.test.ts,为绕开 main 的 TS1117 重复属性而做的分支解阻)已不在 diff 中 —— main 自身经 #10252 修复,两边收敛。全部 10 个文件均位于 packages/cli/src/commands/review/ 下:生产约 690 行、测试约 1189 行,低于 1000 行大 PR 提示线。无需升级。

方案: 增量恰是对维护者验证的回应 —— 其 §B、§C 从根上修复,空目录问题在所有拒绝类别上修复,最后一个无测试的守卫补上测试,另有 probe 测试的密封 git 夹具与 review.ts 中一次"两边都保留"的合并解决。范围保持最小:仍无路由、无清理、无顺手改动。

风险: revert 相关性扫描未命中高风险路径。无升级风险信号。

进入增量代码审查。🔍

Qwen Code · qwen3.8-max

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

@qqqys

qqqys commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 26, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This is a fork PR, so the first round comes from the next scheduled scan (usually within minutes). Remove the autofix/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review — the increment since 891b6ac3

The fix round this pass reviews was written in response to the maintainer's live-stack verification, and he has already re-run his whole matrix one commit later (at 6962311): §B, §C and both nits confirmed fixed, nothing regressed, 16/16 reachable mutations caught. I re-verified each item against the final code at the reviewed head rather than trusting either summary:

  • §B — the all-failed message no longer prescribes a loop. FAN_OUT_BODY now branches on missingRoles.length === AGENTS.length: the all-missing error names the dispatch itself (the pin, the runtime), says re-emitting writes the identical script, and tells the operator to fix what the dispatch reads and dispatch this same script again. The partial-failure message — where a re-dispatch genuinely can succeed — is unchanged, and so is the empty-roster one, whose class a corrected plan does change. The \\' escapes live inside the host template literal and land in the generated script as valid \'; the vm harness executes the generator's real output, and the maintainer's live run fired the new branch with a bad pin.
  • §C — the territory refusal states the measured reason. The "silently truncated away" claim is gone; the refusal and its comment now name the run's end-to-end wall-clock cap, the per-attempt turn/minute caps whose breach becomes a null the fail-closed guard reads as a missing agent, and the fact that large results are persisted and handed over as pointers. I checked each claim against core: workflow.ts documents the 30-minute run cap and the 50-turn / 10-minute attempt caps with exactly that null degradation, and coreToolScheduler.ts carries the truncateToolOutputThreshold + GATE_HEADROOM persistence path. Message and runtime agree.
  • Empty-directory nit — fixed for every refusal class. refuseBlockedFanOut now runs in the handler before ensureWritableReviewWorkflowsDir creates the session directory, and the builder repeats the check for direct callers; the new test asserts nothing exists after a refusal.
  • M6 — the roster-key mismatch guard has its witness. A delegation mock stands in a buildLaunch return whose key disagrees with the roster — the one shape no plan can produce — and pins both the refusal and that nothing is recorded as handed out.
  • Test hermeticity. Fixture git calls now run with sanitizedGitEnv() (an ambient GIT_INDEX_FILE reproduces the exact persistent-runner failure the gate hit), and a module-level isolateHostGitConfig() covers the whole file; the one remaining per-test isolation nests safely under it (LIFO dispose restores each layer). Redundant there, but harmless.
  • The review.ts merge resolution keeps both sides. emit-workflow stays registered after agent-prompt; main's ab-drive and revert-hunk keep their places; the demandCommand enumeration matches registration order, and review.test.ts pins the exact array plus the "message names every subcommand" check — the resolution is witnessed by tests.
  • The core-test unblock left the diff. The duplicate getToolRegistry that broke packages/core's build was fixed on main itself by fix(core): remove duplicate getToolRegistry in telemetry-swap test mock #10252, so the branch's temporary removal converged with main and the effective diff is back to zero core files.

Findings: no blockers, nothing to request. Conventions hold: ESM, strict types, kebab-case files, colocated vitest tests.

Testing evidence

Unattended run — static review only; PR code was never built or executed here, per the gate's rules. The evidence below is the PR's own CI on the reviewed commit, fetched through the API:

Check Conclusion
Test (ubuntu-latest, Node 22.x) ✅ success
Classify PR ✅ success
Dependency CVE audit ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Secret scan (TruffleHog) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
route ✅ success

Everything that ran is green on the reviewed head — the Qwen Code CI and Security Checks workflow runs completed successfully too. The skipped macOS/Windows legs and integration tests are this repo's normal CI shape, not a signal about this PR.

What this static pass cannot settle on its own, named plainly: the built-CLI leg on the exact head (real plan → generated script → core loads and compiles it → real dispatch). That leg is already substantiated one commit back by the maintainer's macOS live-stack re-verification — byte-parity, live 14-agent dispatch, all guards, 16/16 mutations — and the delta since is test files plus merges only. Sandboxed verification would close the remainder: a /verify run on this exact head is already in flight (triggered alongside this re-triage), and its report will land as a separate comment. Real-scenario (tmux) testing: N/A — nothing user-visible changes until the skill routes to this command.

中文说明

代码审查 —— 891b6ac3 之后的增量

本轮审查的修复轮正是针对维护者实机验证写出的那一轮,且他已在其后一个提交(6962311)上重跑完整矩阵:§B、§C 与两个小问题确认修复,无回归,16/16 可达变异全部被抓。我未采信任何一方的总结,而是逐条对照被审头部的最终代码重新核实:

  • §B —— 全失败消息不再开出循环药方。 FAN_OUT_BODY 现按 missingRoles.length === AGENTS.length 分支:全缺席错误点名 dispatch 本身(pin、runtime),说明重新生成只会写出相同脚本,并要求操作者修好 dispatch 所读取的内容后重新分派同一个脚本。部分失败消息(重新分派确可成功)不变,空 roster 消息亦不变(修正 plan 确实会改变该类)。\\' 转义位于宿主模板字面量内,落入生成脚本即为合法的 \';vm 装置执行的是生成器的真实产物,维护者的实机运行已用坏 pin 触发过新分支。
  • §C —— territory 拒绝陈述实测理由。 "静默截断"说法已移除;拒绝文案与注释现点名 run 端到端墙钟上限、单轮次/分钟上限(触顶即变为 null,被失败关闭守卫读作缺席),以及大结果会被落盘并以指针交付。我对照核心逐条核实:workflow.ts 文档载明 30 分钟 run 上限与 50 轮 / 10 分钟单次上限及同样的 null 降级,coreToolScheduler.ts 携带 truncateToolOutputThreshold + GATE_HEADROOM 落盘路径。文案与 runtime 一致。
  • 空目录问题 —— 所有拒绝类别均修复。 refuseBlockedFanOut 现于 handler 中在会话目录创建之前执行,builder 对直接调用方复查同一检查;新测试断言拒绝后什么都不存在。
  • M6 —— roster-key 不匹配守卫补上见证。 委托式 mock 顶替 buildLaunch,返回与 roster 不一致的 key —— 任何 plan 都无法产生的形态 —— 并钉死拒绝本身与"无任何内容被记录为已发出"。
  • 测试密封性。 夹具 git 调用改用 sanitizedGitEnv()(环境中的 GIT_INDEX_FILE 可精确复现门禁遇到的常驻 runner 失败),全文件加模块级 isolateHostGitConfig();残留的一处测试内隔离在其下安全嵌套(LIFO 释放逐层还原)。在那里冗余,但无害。
  • review.ts 合并解决保留双方。 emit-workflow 仍注册于 agent-prompt 之后;main 的 ab-driverevert-hunk 各就其位;demandCommand 枚举与注册顺序一致,review.test.ts 以精确数组与"消息点名每个子命令"检查钉死 —— 该解决有测试见证。
  • 核心测试解阻离开 diff。 打断 packages/core 构建的重复 getToolRegistry 已由 main 自身的 fix(core): remove duplicate getToolRegistry in telemetry-swap test mock #10252 修复,分支的临时删除与之收敛,有效 diff 重新归零核心文件。

结论:无阻塞项,无可请求项。 约定全部满足:ESM、严格类型、kebab-case 文件名、同目录 vitest 测试。

测试证据

无人值守运行 —— 按门禁规则仅静态审查,绝不构建或执行 PR 代码。以上证据是 PR 自身 CI 在被审提交上的结果,经 API 获取:凡已运行的检查在被审头部全绿,Qwen Code CISecurity Checks 两个 workflow 运行亦成功完成。macOS/Windows 测试腿与集成测试的跳过是本仓库 CI 的正常形态,不是本 PR 的信号。

静态审查无法单独定论的一点,如实说明:该精确头部上的构建后 CLI 一环(真实 plan → 生成脚本 → 核心加载并编译 → 真实分派)。该环已由维护者相隔一个提交的 macOS 实机复验背书 —— 字节一致性、14 agent 实弹分派、全部围栏、16/16 变异 —— 而其后的增量只有测试文件与合并。沙箱验证可收拢剩余部分:针对该头部的 /verify 已在运行(随本次重新 triage 触发),报告会作为单独评论发布。真实场景(tmux)测试:不适用 —— 在 skill 路由到该命令之前没有用户可见变化。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — the fix round answered the maintainer's own live-stack findings, he re-verified each fix on real hardware and found nothing regressed, and the delta since is test hermeticity plus a keep-both-sides merge; the fifth point stays withheld for the two out-of-diff reasons named below.

Stepping back: this is what a fix round should look like. Four maintainer findings, each resolved at its root — the loop-prescribing message now names the dispatch, the inaccurate truncation claim now states the measured caps (which I checked against the runtime source myself), refusals leave no empty trees, and the last untested guard has a witness that stands the builder in. The hermetic-fixture commits chase a real incident class with the repo's established isolation tools, not new machinery. The merge resolution is the boring, correct union, pinned by the registry test. Nothing over-engineered, no scope creep — still no routing, no cleanup, exactly as staged in the first pass.

Two things I'm naming rather than counting against the PR:

  • §A stays open by design. Fail-closed discards delivered agents on a partial failure; the maintainer framed it as the design question to settle before routing lands, and he is explicitly fine with it in a PR that routes nothing. The rewritten territory refusal now states the tradeoff honestly. The decision belongs to the routing change, and that change should carry it.
  • Every standing CHANGES_REQUESTED review predates all of the fixes. The maintainer's own (on the first commit) and the review loop's two — each finding they carried is either fixed and re-verified, or was disproved at the time. A fresh look from each requester is the remaining step on that side; my approval does not dismiss them. Also cosmetic: the PR body still says 43 tests (it's 54) and marks macOS ⚠️, which the maintainer's full macOS run has since superseded.

Verdict: approve. All PR CI runs on the reviewed head completed green — Qwen Code CI and Security Checks included — so the approval is pinned to that commit now rather than deferred.

中文说明

置信度:4/5 —— 修复轮回应的是维护者本人的实机发现,他已在真实环境逐条复验且无回归,其后的增量只有测试密封性与一次"两边保留"的合并;第五点因下述两个 diff 外原因保留。

退一步看:这是修复轮应有的样子。4 条维护者发现均从根上解决 —— 开出循环药方的消息现在点名 dispatch 本身,不实的截断说法改为陈述实测上限(我已对照 runtime 源码亲自核实),拒绝不再留下空目录,最后一个无测试的守卫有了顶替 builder 的见证。密封夹具提交针对的是真实事故类别,用的是仓库既有的隔离工具而非新机制。合并解决是乏味但正确的并集,由注册表测试钉死。无过度设计、无范围蔓延 —— 仍然不路由、不清理,与首轮的阶段规划完全一致。

两点如实点名、但不计入对本 PR 的扣分:

  • §A 按设计保持开放。 部分失败时失败关闭会丢弃已投递的 agent;维护者把它界定为路由落地前要定的设计问题,并明确接受在一个不路由的 PR 中保持现状。重写后的 territory 拒绝已如实陈述这一取舍。决策属于路由改动,应由那个改动携带。
  • 所有挂起的 CHANGES_REQUESTED 审查均早于全部修复。 维护者本人的(在首个提交上)与 review 循环的两条 —— 其携带的每条发现要么已修复并经复验,要么当时即被证伪。剩余一步是各请求方重新看一眼;我的批准不会撤销它们。另有表面问题:PR 正文仍写 43 个测试(实为 54)、macOS 仍标 ⚠️ —— 维护者已在 macOS 完整跑过,可以更新。

结论:批准。被审头部上的所有 PR CI 运行均已完成且为绿(含 Qwen Code CISecurity Checks),因此批准现在即钉在该提交上,不再推迟。

Qwen Code · qwen3.8-max

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

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

Reviewed.

Not reviewed: the diff sections covering packages/cli/src/commands/review/emit-workflow.test.ts, packages/cli/src/commands/review/workflow-script.ts, the whole-diff test-coverage check, the removed-behavior audit — its prompt was built, but no agent on record was launched with it.

Not reviewed: verification and reverse audit — neither the verifier nor the reverse auditor was launched with a prompt this skill builds — the posted findings were ruled on, and the misses the rest of the review left were hunted, if at all, without the briefs this skill certifies against.

[Critical] C1 (Critical) — Wrong import: isTerritoryFanOut is not exported from ./lib/budget.js

emit-workflow.ts (line 690) imports isTerritoryFanOut from ./lib/budget.js, but the function is defined and exported from ./lib/roster.js (line 107 on main). On main, budget.ts has no imports and no mention of isTerritoryFanOut. The PR's only change to budget.ts is exporting usableLineCount. This is a TypeScript compile-time error: Module '"./lib/budget.js"' has no exported member 'isTerritoryFanOut'.

Fix: Change the import to pull isTerritoryFanOut from ./lib/roster.js instead:

import { isTerritoryFanOut } from './lib/roster.js';
import { usableLineCount } from './lib/budget.js';

Or merge into the existing roster.js import on line 695.

File: packages/cli/src/commands/review/emit-workflow.ts (line 690)
Confidence: High

中文说明

已审查。

未审查:涉及 packages/cli/src/commands/review/emit-workflow.test.ts、packages/cli/src/commands/review/workflow-script.ts 的 diff 片段、全 diff 测试覆盖检查、删除行为审计——它的 prompt 已构建,但没有任何 agent 有记录用它启动过。

未审查:验证与反向审计——验证 agent 与反向审计 agent 都没有用本 skill 构建的 prompt 启动——发布的发现即便被裁定过、评审其余部分遗漏的问题即便被搜寻过,也都缺失了本 skill 用以认证的 brief。

[Critical] C1 (Critical) — Wrong import: isTerritoryFanOut is not exported from ./lib/budget.js

emit-workflow.ts (line 690) imports isTerritoryFanOut from ./lib/budget.js, but the function is defined and exported from ./lib/roster.js (line 107 on main). On main, budget.ts has no imports and no mention of isTerritoryFanOut. The PR's only change to budget.ts is exporting usableLineCount. This is a TypeScript compile-time error: Module '"./lib/budget.js"' has no exported member 'isTerritoryFanOut'.

Fix: Change the import to pull isTerritoryFanOut from ./lib/roster.js instead:

import { isTerritoryFanOut } from './lib/roster.js';
import { usableLineCount } from './lib/budget.js';

Or merge into the existing roster.js import on line 695.

File: packages/cli/src/commands/review/emit-workflow.ts (line 690)
Confidence: High

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

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

Reviewed — no blockers. Suggestions are inline.

中文说明

已审查——无阻断问题。 建议见行内评论。

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

expect(readFileSync(scriptPath, 'utf8')).toContain('export const meta');
});

it('leaves no temp file behind, on success or on a failed write', () => {

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.

[Suggestion] The test title promises no temp file is left behind "on success or on a failed write", but the body exercises only the success path. On success, renameSync moves the temp file to scriptPath, so the directory holds zero .tmp entries whether or not the cleanup runs — the finally { rmSync(temporaryPath, { force: true }) } in runEmitWorkflow exists precisely for the mid-write-failure case its own comment describes (ENOSPC/EIO after the temp file exists), and no test reaches that state. Probe-confirmed both ways: a mutant that removes the finally leaves all 23 tests green (the mutation survives), while a failed-write case passes on unmodified code and fails on the mutant with a stranded temp file.

Witness:

mutant (finally removed): "leaves no temp file…" → 1 passed; full file → 23 passed (23)
probe (failed-write case) on unmodified code → 1 passed
probe on mutant → FAILED: "expected [ Array(1) ] to deeply equal []" — leftover "qwen-review-b68c1503bc.js.<uuid>.tmp"

Add the failed-write case the title already promises, mock-free: pre-create scriptPath as a non-empty directory so renameSync throws ENOTEMPTY after the temp file exists, then assert the handler throws and readdirSync(scriptDir).filter(n => n.endsWith('.tmp')) is empty. That path runs the finally for real; today nothing does.

中文说明

测试标题承诺“无论成功还是写入失败都不会留下临时文件”,但测试体只执行了成功路径。成功时 renameSync 会把临时文件移动到 scriptPath,因此无论清理逻辑是否运行,目录里都不会有 .tmp 条目——而 runEmitWorkflow 中的 finally { rmSync(temporaryPath, { force: true }) } 正是为其注释所描述的中途写入失败场景(临时文件已存在时发生 ENOSPC/EIO)而存在的,却没有任何测试到达该状态。探针双向验证:删除该 finally 的突变体下全部 23 个测试仍然通过(突变存活);而补上失败写入用例后,未修改的代码通过、突变体失败并暴露出残留的临时文件。

建议:补上标题早已承诺的失败写入用例,无需 mock——预先把 scriptPath 创建为非空目录,使 renameSync 在临时文件已存在时抛出 ENOTEMPTY,然后断言 handler 抛错且 readdirSync(scriptDir).filter(n => n.endsWith('.tmp')) 为空。这条路径会真正执行 finally;目前没有任何测试这样做。

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

Comment on lines +49 to +51
const body = script.replace('export const meta =', 'const meta =');
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
const fn = new AsyncFunction('agent', 'parallel', 'phase', 'log', body);

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.

[Suggestion] The runScript harness is documented as "an analogue of the sandbox", but it diverges from the real workflow sandbox in four false-pass channels, each verified against workflow-sandbox.ts and reproduced by probe:

  1. It rewrites export const meta = to const meta = and executes the meta block — leaving a live meta binding in body scope — while the runtime strips it and parses it as a pure literal (extractAndStripMeta/parseWorkflowMetaLiteral, "it is parsed, never executed"). A FAN_OUT_BODY edit that reads meta passes all 15 tests here — as a source mutant the whole file still passes 15/15 — then throws ReferenceError: meta is not defined at real dispatch; a meta block that is valid executable JS but not a pure literal (a call or concatenation) passes here while compileWorkflowScript throws invalid meta object literal at launch.
  2. Bare new AsyncFunction(...) compiles sloppy; wrapWorkflowBody wraps the body in (async () => {'use strict'; …})() — an undeclared-assignment typo silently creates a global here and throws there.
  3. The body runs in the host realm where every Node global resolves; the vm context binds only the sandbox globals (agent/parallel/pipeline/phase/log/console/args/budget/workflow). Probe: typeof setTimeout is "function" here, "undefined" there; calling it reaches in the harness and throws ReferenceError in the sandbox. A future performance.now()/setTimeout reference in FAN_OUT_BODY passes the suite and dies at dispatch.
  4. The stub agent (lines 40–43) applies none of the runtime's option gates — the KNOWN_AGENT_OPTS allowlist, the empty-workingDir refusal, the workingDir+isolation exclusion. Probe end-to-end: adding timeout: 60000 to both dispatch branches keeps this suite green, while the real sandbox rejects every call (agent({timeout}): unknown option) and the fan-out dies with "review fan-out: all 3 agents failed to deliver".

Witness:

metaRef mutant   — harness: PASS | runtime: RUN THROW ReferenceError: meta is not defined
callMeta         — harness: PASS | runtime: COMPILE THROW invalid meta object literal
timeout-opts mutant — harness suite green | sandbox: agent({timeout}): unknown option → "all 3 agents failed to deliver"
control (unmutated script) runs clean in the real sandbox in every arm

All four channels are latent at HEAD — the current body is strict-clean, never reads meta, uses no host-only globals, and its opts satisfy every gate. The cost is a fan-out script the suite certifies as executable but the runtime rejects. Suggested direction — mirror the runtime: strip the meta block instead of converting it (the bounds are already computed) and check it against the real literal-parser contract; compile the remainder under 'use strict'; run the body in a node:vm context binding only the four harness globals (this closes channel 3 too); and assert every dispatched opts key against the runtime's allowlist, or replicate the workingDir/isolation gates in the stub.

中文说明

runScript 测试辅助被描述为“沙箱的等价模拟”,但它与真实 workflow 沙箱存在四个可致假通过的偏差,均已对照 workflow-sandbox.ts 验证并用探针复现:

  1. 它把 export const meta = 改写为 const meta =执行 meta 块——使 meta 绑定在 body 作用域中存活——而运行时会将其剥离并按纯字面量解析(extractAndStripMeta/parseWorkflowMetaLiteral,“只解析、从不执行”)。读取 metaFAN_OUT_BODY 修改在这里能通过全部 15 个测试(作为源码突变体整个文件仍是 15/15),在真实分派时却抛 ReferenceError: meta is not defined;一个可执行但非纯字面量的 meta 块(调用或拼接)在这里通过,而 compileWorkflowScript 在启动时抛 invalid meta object literal
  2. new AsyncFunction(...) 以 sloppy 模式编译;wrapWorkflowBody(async () => {'use strict'; …})() 包裹 body——未声明标识符的赋值在这里静默创建全局变量,在那里抛错。
  3. body 在宿主域中执行,所有 Node 全局都可用;vm 上下文只绑定沙箱自身的全局(agent/parallel/pipeline/phase/log/console/args/budget/workflow)。探针:typeof setTimeout 在这里是 "function",在那里是 "undefined";调用它在这里可达,在沙箱中抛 ReferenceError。未来 FAN_OUT_BODY 引用 performance.now()/setTimeout 会通过本套件、在分派时死亡。
  4. agent(40–43 行)未实现运行时的任何选项门禁——KNOWN_AGENT_OPTS 白名单、空 workingDir 拒绝、workingDir+isolation 互斥。端到端探针:给两个分派分支加上 timeout: 60000 后本套件仍全绿,而真实沙箱拒绝每次调用(agent({timeout}): unknown option),扇出以 "review fan-out: all 3 agents failed to deliver" 终止。

四个通道在 HEAD 上均为潜在问题——当前 body 严格模式安全、从不读取 meta、不使用宿主专有全局、其 opts 满足全部门禁。代价是套件认证为可执行、运行时却拒绝的扇出脚本。建议方向——镜像运行时:剥离(而非转换)meta 块(边界已经算出)并按真实字面量解析器契约校验;以 'use strict' 编译其余部分;在只绑定四个辅助全局的 node:vm 上下文中运行 body(同时关闭通道 3);并断言每次分派的 opts 键都在运行时白名单内,或在桩中复刻 workingDir/isolation 门禁。

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

}
});

it('refuses an unreadable rules path before writing anything', () => {

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.

[Suggestion] No test drives the handler's --rules happy path. The only handler-level rules test covers the unreadable-path throw (which aborts while reading the rules file, before the roster is built), and the rules-threading test (~line 141) calls buildFanOutRoster directly, bypassing runEmitWorkflow's file-read-and-forward. The current code forwards correctly (buildFanOutRoster(report, args.plan, rules)) — the claim is the seam. The pre-existing writeBrief rules-downgrade guard does not cover this path: it only fires when rebuilding over a brief that already carries ## Project rules, while emit-workflow builds fresh briefs.

Failure scenario: a refactor dropping the third argument from the roster call in runEmitWorkflow leaves every test green — the direct-call test never goes through the handler — so a review launched with --rules silently ships rules-free briefs to every agent: precisely the failure the command's own rules refusal names ("would silently review without the project rules the run was told to enforce").

Add a handler-level happy-path test: write a rules file containing a marker, call the handler with { plan, rules: rulesPath }, then assert the script was written and every reviewing brief (all keys except '7') contains the marker.

中文说明

没有任何测试经过 handler 驱动 --rules 的正常路径。唯一的 handler 级 rules 测试覆盖的是不可读路径的抛错(在读取 rules 文件时即中止,早于 roster 构建),而 rules 透传测试(约 141 行)直接调用 buildFanOutRoster,绕过了 runEmitWorkflow 的读文件并转发。当前代码转发正确(buildFanOutRoster(report, args.plan, rules))——问题在于覆盖缝隙。既有的 writeBrief rules 降级守卫也不覆盖这条路径:它只在重建已含 ## Project rules 的 brief 时触发,而 emit-workflow 构建的都是全新 brief。

失败场景:重构时丢掉 runEmitWorkflow 中 roster 调用的第三个参数,全部测试仍为绿色——直接调用测试从未经过 handler——于是带 --rules 启动的评审会静默地向每个 agent 投递不含规则的 brief:这正是命令自身的 rules 拒绝信息所点名的失败("会静默地在不执行本次运行被告知要执行的项目规则的情况下评审")。

建议补一个 handler 级正常路径测试:写入一个含标记的 rules 文件,以 { plan, rules: rulesPath } 调用 handler,然后断言脚本已写出、且每个参与评审的 brief(除 '7' 外的所有键)都包含该标记。

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

Comment on lines +79 to +81
expect(script).not.toContain('Date.now');
expect(script).not.toContain('Math.random');
expect(script).not.toContain('new Date');

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.

[Suggestion] The determinism guard asserts only Date.now / Math.random / new Date, but the sandbox's safeDate also throws on Date.parse(...), Date.UTC(...) and bare Date(...) calls (workflow-sandbox.tssafeDate, .now, .UTC, .parse all throw the same error). The executable layer cannot compensate: runScript runs in the host realm, where all three missed forms work fine. Probe: a FAN_OUT_BODY mutant using Date.parse('2026-01-01') passes all 15 tests as shipped, while the real sandbox throws "Date.now() / new Date() are unavailable in workflow scripts" — the control arm (unmutated script) resolves cleanly, 3/3 delivered, so nothing is broken at HEAD; the gap is the guard against future FAN_OUT_BODY edits.

Witness:

tests as shipped, Date.parse mutant → Tests 15 passed (15)
extended assertions, same mutant   → × expected '…' not to contain 'Date.parse'
real sandbox CONTROL (unmutated)   → RESOLVED, rosterSize=3, delivered=3
real sandbox MUTANT                → THREW: "Date.now() / new Date() are unavailable in workflow scripts"
Suggested change
expect(script).not.toContain('Date.now');
expect(script).not.toContain('Math.random');
expect(script).not.toContain('new Date');
expect(script).not.toContain('Date.now');
expect(script).not.toContain('Math.random');
expect(script).not.toContain('new Date');
expect(script).not.toContain('Date.parse');
expect(script).not.toContain('Date.UTC');
expect(script).not.toMatch(/\bDate\s*\(/);
中文说明

确定性守卫只断言了 Date.now / Math.random / new Date,但沙箱的 safeDateDate.parse(...)Date.UTC(...) 和裸 Date(...) 调用同样抛错(workflow-sandbox.tssafeDate 本体、.now.UTC.parse 抛同一错误)。可执行层也无法弥补:runScript 在宿主域运行,上述三种被漏掉的形式在那里都可用。探针:使用 Date.parse('2026-01-01')FAN_OUT_BODY 突变体在当前测试下全部 15 个通过,而真实沙箱抛 "Date.now() / new Date() are unavailable in workflow scripts"——对照臂(未突变脚本)干净解析、3/3 投递,因此 HEAD 上并无实际损坏;缺口在于针对未来 FAN_OUT_BODY 修改的守卫。

建议按上方 suggestion 补齐沙箱的完整阻止面(如裸构造器调用的正则与夹具内容冲突,可按夹具情况微调)。

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

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 11 finishedview run. See this round's report below.

中文说明

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

…nLM#10119)

Address review feedback on the emit-workflow PR:

- Run the generated fan-out script in a vm context that mirrors the
  workflow sandbox's execution shape: the meta block is stripped instead
  of executed, the body is wrapped in the runtime's strict-mode async
  IIFE, only the sandbox globals are bound, and the agent stub applies
  the runtime's option gates.
- Extend the determinism guard to the sandbox's full Date surface
  (Date.parse, Date.UTC, bare Date calls).
- Exercise the failed-write half of the temp-and-rename cleanup.
- Cover the handler-level --rules happy path end to end.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Review feedback round — PR #10119

Outcome: changed — one commit (a667b678f3, test-only, +116/−22 lines in the two test files the feedback annotates). One Critical finding from the review body was declined with evidence (it is disproved by the code on main); all four inline suggestions were implemented and mutation-probed. No conflict handling (--conflict false, no merge).

Dispositions

[rv:5029099433] C1 (Critical) — "Wrong import: isTerritoryFanOut is not exported from ./lib/budget.js" → Declined (disproved)

The claim is factually wrong against this repository's main:

  • git show origin/main:packages/cli/src/commands/review/lib/budget.ts shows line 245: export function isTerritoryFanOut(plan: DiffSize): boolean { — the function IS defined and exported from budget.ts on main.
  • roster.ts on main (lines 33–34) only re-exports it: export { isTerritoryFanOut } from './budget.js'; — the finding appears to have read that re-export line as the definition and attributed it to the wrong module ("roster.js line 107" defines nothing).
  • The PR's import lives at emit-workflow.ts line 39 (not line 690, which is past the end of the file) and is valid. npm run build and npm run typecheck both pass this round, and a genuinely missing export would fail both.

No code change was made for this finding.

[rc:3862296232] Failed-write half of the temp-file test → Implemented

The test title promised cleanup "on success or on a failed write" but only exercised success. Added the failed-write case exactly as suggested: pre-create scriptPath as a non-empty directory so renameSync throws AFTER the temp file exists, then assert the handler throws and readdirSync(scriptDir) holds zero .tmp entries. This runs the finally { rmSync(...) } for real.

[rc:3862296240] runScript harness divergence from the sandbox → Implemented

Rewrote the harness to mirror the runtime's execution shape (workflow-sandbox.ts):

  • the meta block is stripped, never executed — the body runs with no live meta binding, like dispatch;
  • the body is wrapped in the runtime's (async () => {'use strict'; … })() IIFE — undeclared assignments throw;
  • the body runs in a node:vm context binding only the globals the harness stands in for (agent/parallel/phase/log) — host-only globals like setTimeout throw here like they do in the sandbox;
  • the agent stub applies the runtime's gates: the KNOWN_AGENT_OPTS allowlist, the empty-workingDir refusal, and the workingDir+isolation exclusion.

One scoping note: checking the meta block against the REAL literal parser (parseWorkflowMetaLiteral) would require exporting it from the core package, which is outside this PR's footprint (cli-only). The block's purity keeps being asserted on the source, and stripping it (instead of executing it) closes the channel that mattered — a body reading meta now fails here with the same ReferenceError it dies with at dispatch.

[rc:3862296250] Handler-level --rules happy path → Implemented

Added the missing test: write a rules file carrying a marker, invoke the handler with { plan, rules }, assert the script was written and every reviewing brief (all recorded keys except '7', count asserted non-trivial) contains the marker. The seam — runEmitWorkflow's read-and-forward into buildFanOutRoster — is now covered through the handler.

[rc:3862296262] Determinism guard misses Date forms → Implemented

Extended the assertions with the sandbox's full safeDate refusal surface: Date.parse, Date.UTC, and bare Date( calls (regex), per the suggested code block.

Mutation probes (each restored before committing; working tree verified clean of them)

Probe Mutation Result
A removed the finally cleanup in runEmitWorkflow "leaves no temp file behind…" FAILED (witness for the new failed-write half)
B dropped the rules argument from the handler's roster call "--rules through the handler…" FAILED
C void meta; prepended to FAN_OUT_BODY harness tests threw ReferenceError: meta is not defined
D timeout: 60000 added to both dispatch branches 9/15 tests FAILED — the mirrored gate refuses every dispatch
E undeclared assignment in FAN_OUT_BODY ReferenceError (strict-mode wrapper)
F setTimeout(() => {}, 0) in FAN_OUT_BODY ReferenceError: setTimeout is not defined (vm context)
G void Date.parse('2026-01-01') in FAN_OUT_BODY new Date.parse assertion FAILED

Verification

  • npm run build — passed (exit 0; first attempt failed on TS4111 index-signature access in the new harness code, fixed with bracket notation and re-run to green)
  • npm run typecheck — passed (exit 0)
  • npm run lint — passed (exit 0)
  • npx prettier --check on the two changed files — passed
  • cd packages/cli && npx vitest run src/commands/review/emit-workflow.test.ts src/commands/review/workflow-script.test.ts — 2 files passed, 39 tests passed (38 pre-existing incl. the rewritten harness cases + 2 new, one of which extends an existing test in place)
  • Integration tests after npm run bundle — not applicable: this round is test-only, no behavior changed, and nothing here is exercised solely through the bundled CLI or integration harness
  • npm run generate:settings-schema — not applicable: no settings source changed
中文说明

评审反馈轮次 — PR #10119

结果:有改动 — 一个提交(a667b678f3,纯测试改动,在反馈所标注的两个测试文件中共 +116/−22 行)。评审正文中的一个 Critical 发现被以证据驳回(该说法已被 main 上的代码证伪);四条行内建议全部实现并经过突变探针验证。无冲突处理(--conflict false,未做合并)。

各项处置

[rv:5029099433] C1(Critical)— "错误导入:isTerritoryFanOut 未从 ./lib/budget.js 导出" → 驳回(已证伪)

该说法与本仓库 main 的实际代码不符:

  • git show origin/main:packages/cli/src/commands/review/lib/budget.ts 显示第 245 行:export function isTerritoryFanOut(plan: DiffSize): boolean { — 该函数在 main 上就定义并导出自 budget.ts
  • main 上的 roster.ts(33–34 行)只是再导出:export { isTerritoryFanOut } from './budget.js'; — 该发现似乎是把这行再导出误读成了定义,并归错了模块("roster.js 107 行"并没有定义任何东西)。
  • 本 PR 的导入位于 emit-workflow.ts 第 39 行(而非第 690 行,该行已超出文件末尾),且是有效的。本轮 npm run buildnpm run typecheck 均通过,若导出真的缺失,两者都会失败。

未针对该发现做任何代码改动。

[rc:3862296232] 临时文件测试的"写入失败"半边 → 已实现

测试标题承诺"无论成功还是写入失败都不留临时文件",但测试体只执行了成功路径。按建议补上了失败写入用例:预先把 scriptPath 创建为非空目录,使 renameSync 在临时文件已存在之后抛错,然后断言 handler 抛错且 readdirSync(scriptDir) 中没有任何 .tmp 条目。这条路径会真正执行 finally { rmSync(...) }

[rc:3862296240] runScript 辅助与沙箱的偏差 → 已实现

重写测试辅助以镜像运行时的执行形态(workflow-sandbox.ts):

  • meta 块被剥离、从不执行 — body 运行时不存在存活的 meta 绑定,与真实分派一致;
  • body 被套上运行时的 (async () => {'use strict'; … })() IIFE 包裹 — 未声明标识符的赋值会抛错;
  • body 在 node:vm 上下文中执行,只绑定本辅助所替代的全局(agent/parallel/phase/log)— setTimeout 这类宿主专有全局在这里像沙箱中一样抛错;
  • agent 桩实现运行时的门禁:KNOWN_AGENT_OPTS 白名单、空 workingDir 拒绝、workingDir+isolation 互斥。

一点范围说明:按真实字面量解析器(parseWorkflowMetaLiteral)的契约校验 meta 块,需要先从 core 包导出该函数,这超出了本 PR 的足迹(仅 cli)。meta 块的纯度继续在源码层面断言;而将其剥离(而非执行)已经关闭了真正要紧的通道 — body 读取 meta 时,现在会在这里抛出与真实分派相同的 ReferenceError

[rc:3862296250] handler 级 --rules 正常路径 → 已实现

补上了缺失的测试:写入一个含标记的 rules 文件,以 { plan, rules } 调用 handler,断言脚本已写出、且每个参与评审的 brief(所有已记录的键中除 '7' 外的全部,并断言数量非平凡)都包含该标记。runEmitWorkflow 读文件并转发给 buildFanOutRoster 这条缝隙,现在经由 handler 被覆盖。

[rc:3862296262] 确定性守卫漏掉的 Date 形式 → 已实现

按建议的代码块,把断言扩展到沙箱 safeDate 的完整阻止面:Date.parseDate.UTC 以及裸 Date( 调用(正则)。

突变探针(提交前均已还原;已确认工作区无残留)

探针 突变 结果
A 删除 runEmitWorkflow 中的 finally 清理 "leaves no temp file behind…" 失败(新增失败写入半边的见证)
B 删除 handler 中 roster 调用的 rules 实参 "--rules through the handler…" 失败
C FAN_OUT_BODY 开头加 void meta; 辅助测试抛 ReferenceError: meta is not defined
D 给两个分派分支加 timeout: 60000 15 个测试中 9 个失败 — 镜像门禁拒绝每次分派
E FAN_OUT_BODY 中未声明标识符赋值 ReferenceError(严格模式包裹)
F FAN_OUT_BODYsetTimeout(() => {}, 0) ReferenceError: setTimeout is not defined(vm 上下文)
G FAN_OUT_BODYvoid Date.parse('2026-01-01') 新增的 Date.parse 断言失败

验证

  • npm run build — 通过(exit 0;首次运行因新辅助代码中的 TS4111 索引签名访问而失败,改用方括号访问后重跑通过)
  • npm run typecheck — 通过(exit 0)
  • npm run lint — 通过(exit 0)
  • 对两个改动文件执行 npx prettier --check — 通过
  • cd packages/cli && npx vitest run src/commands/review/emit-workflow.test.ts src/commands/review/workflow-script.test.ts — 2 个文件通过,39 个测试通过(38 个既有测试,含重写后辅助下的用例,外加 2 个新测试,其一为对既有测试的原位扩展)
  • npm run bundle 后的集成测试 — 不适用:本轮为纯测试改动,无行为变化,且此处没有任何仅由打包 CLI 或集成测试框架覆盖的行为
  • npm run generate:settings-schema — 不适用:未改动任何 settings 源

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

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

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

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

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

中文说明

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

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

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

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

const agents = buildFanOutRoster(report, args.plan, rules);

mkdirSync(dirname(scriptPath), { recursive: true });
const temporaryPath = `${scriptPath}.${randomUUID()}.tmp`;

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] R2-1: Reject symlinked generated-workflow directories before writing the script

If the generated workflow root or session directory is a symlink to an external directory, mkdirSync, writeFileSync, and renameSync follow it. The command writes the generated script—including embedded review prompts—outside the trusted root and prints a scriptPath that the secure loader later rejects after canonicalization.

Witness: an unmodified-PR probe made workflows/generated/review/session a symlink to an external directory; emit-workflow succeeded and the external target contained a script with const AGENTS = [. The loader then rejected that canonical path.

Please use a writer that shares the loader's canonical-containment policy: reject symlinked path components and verify the canonical parent remains under the canonical generated-workflow root before building briefs, records, or script contents.

The regression test should make a symlinked generated root/session fail before any external script or prompt record is written; removing the guard must make that test red.

中文说明

如果生成 workflow 的根目录或 session 目录是指向外部目录的符号链接,mkdirSyncwriteFileSyncrenameSync 会沿链接写入。命令会把包含完整 review prompt 的脚本写到可信根之外,并输出一个随后会被安全 loader 在 canonicalize 后拒绝的 scriptPath

实测:在未修改的 PR 代码上,将 workflows/generated/review/session 链接到外部目录后,emit-workflow 成功执行,外部目标中出现了包含 const AGENTS = [ 的脚本;loader 随后拒绝该 canonical path。

请让写入端与 loader 共用 canonical containment 策略:在构建 brief、prompt record 或脚本内容之前拒绝符号链接路径组件,并确认目标父目录的真实路径仍位于生成 workflow 的真实根目录下。回归测试应验证链接根/session 会在任何外部写入前失败,删除防护后测试必须变红。

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

projectDir,
GENERATED_WORKFLOWS_SUBDIR,
REVIEW_WORKFLOWS_SUBDIR,
session ? sanitizeFilenameComponent(session) : 'no-session',

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] R2-3: Make generated workflow directories collision-resistant across raw session IDs

sanitizeFilenameComponent is lossy, so distinct concurrent sessions such as sess.1 and sess_1 both map to sess_1. In the same project, emitting the same plan path then selects the same script target; the later atomic rename replaces the earlier session's script, and the earlier session can dispatch the other session's roster, rules, and worktree pin.

Witness: the unmodified code resolved both IDs to /tmp/project/workflows/generated/review/sess_1. Appending a digest of the raw ID produced distinct suffixes and flipped the collision assertion.

Keep the readable sanitized prefix, but append a digest of the raw session ID. Add a test proving colliding sanitized IDs produce different directories and cannot overwrite each other's script for the same plan; removing the digest must make it red.

中文说明

sanitizeFilenameComponent 是有损映射,因此 sess.1sess_1 这类不同的并发 session 都会变成 sess_1。同一项目中若两者对同一 plan 路径生成脚本,就会选择同一目标文件;后一次原子 rename 会覆盖前一个 session 的脚本,导致前一个 session 可能执行另一个 session 的 roster、规则和 worktree pin。

实测:未修改代码会把两个 ID 都解析到 /tmp/project/workflows/generated/review/sess_1;在清洗后的可读前缀后追加原始 session ID 的摘要后,两条路径分离并翻转了碰撞断言。

请保留可读前缀并追加原始 ID 的 digest。测试应证明清洗后碰撞的两个 ID 会得到不同目录,且同一 plan 的脚本不会互相覆盖;删除 digest 后测试必须变红。

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

env: NodeJS.ProcessEnv = process.env,
): string {
const digest = createHash('sha256')
.update(resolve(planPath))

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] R2-4: Canonicalize the plan path before deriving the generated script identity

On macOS, the same existing plan can be spelled as /var/... and /private/var/.... Hashing resolve(planPath) directly therefore gives different generated script names for the same file and makes the new relative-versus-absolute identity test fail on Darwin.

Witness from the unmodified PR: the targeted test expected qwen-review-63dc4469bc.js but received qwen-review-aefd102e7b.js (1 failed, 22 passed). With realpath canonicalization, both forms produced qwen-review-48eff06521.js.

Canonicalize the existing resolved plan path before hashing it. The existing names a script per plan test should pass on Darwin and fail again if canonicalization is removed.

中文说明

在 macOS 上,同一个已存在的 plan 可能分别表示为 /var/.../private/var/...。直接哈希 resolve(planPath) 会为同一文件生成不同脚本名,并使新增的相对/绝对路径身份测试在 Darwin 上失败。

未修改 PR 的实测结果:目标测试期望 qwen-review-63dc4469bc.js,实际得到 qwen-review-aefd102e7b.js1 failed, 22 passed)。使用 realpath canonicalize 后,两种写法都生成 qwen-review-48eff06521.js

请在哈希前 canonicalize 已存在的 plan 真实路径。现有 names a script per plan 测试应在 Darwin 上通过,并在移除 canonicalization 后再次变红。

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

}
}

if (missingRoles.length > 0) {

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] R2-2: Fail the workflow when any required review agent returns nothing

When one required agent times out, fails dispatch, or returns an empty final response while another succeeds, this branch only logs the missing role. The workflow returns success with a shortened delivered list, allowing the caller to aggregate a review that silently lacks a required dimension—even though the nearby contract says a non-empty missingRoles is a failed step.

Witness: the unmodified script returned { rosterSize: 2, delivered: [{ key: 'a', text: 'ok:A' }], missingRoles: ['b'] }. Making this branch throw caused the same probe to fail with review fan-out: required agents failed to deliver (b).

Fail closed whenever missingRoles is non-empty, or add an equally deterministic mandatory recovery boundary before this result can be consumed. Add a test where one of several agents fails and assert the script rejects and names the missing role; removing the guard must make it red.

中文说明

当一个必需 agent 超时、调度失败或返回空 final response,而其他 agent 成功时,这里只记录缺失角色,workflow 仍以缩短后的 delivered 列表成功返回。调用方因此可能聚合一个静默缺少必需审查维度的结果,尽管相邻契约明确写着非空 missingRoles 应当使该步骤失败。

实测:未修改脚本返回 { rosterSize: 2, delivered: [{ key: 'a', text: 'ok:A' }], missingRoles: ['b'] }。把该分支改为抛错后,同一探针以 review fan-out: required agents failed to deliver (b) 失败。

请在 missingRoles 非空时 fail closed,或在结果被消费前增加同等确定性的强制恢复边界。测试应构造多个 agent 中一个失败的情况,断言脚本拒绝并点名缺失角色;移除防护后测试必须变红。

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

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

Not explored to full depth (tool budget reached): "agent 3b": execute the two new test files (the shared worktree has no node_modules or built dist; a full install + build exceeded the remaining tool budget and would leave….

Test Plan (not a blocker): 43 passed — this review observed 24797 passed; 119 passed — this review observed 24797 passed.

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

  • packages/cli/src/commands/review/workflow-script.test.ts:122 — [probe] meta-purity oracle is a regex weaker than the runtime parser it stands in for
  • packages/cli/src/commands/review/emit-workflow.test.ts:428 — [review] no test pins the handler's cannot-read-the-plan guard every sibling command pins

[Critical] R2-1: Re-checked existing blocker (comment 3863610970, @wenshao) — still stands at HEAD. Symlinked generated-workflow directories are written through: runEmitWorkflow runs mkdirSync(dirname(scriptPath), {recursive: true}) and a temp+rename write under $QWEN_CODE_PROJECT_DIR/workflows/generated/review/<session>, and no step rejects symlinked path components or verifies the canonical parent stays under the canonical generated-workflow root, while the loader (readWorkflowFileSecurely) realpaths and refuses. If the root or session directory is a symlink to an external directory, the script embedding every review prompt is written outside the trusted root and the printed scriptPath is unloadable. Original probe: with a symlinked session directory, emit-workflow succeeded and the external target contained the script. The write path is unchanged since the comment (the only later commit is test-only), so the mechanism is present at the reviewed commit.

[Critical] R2-2: Re-checked existing blocker (comment 3863611003, @wenshao) — still stands at HEAD. FAN_OUT_BODY collects missing roles and only logs them, throwing solely when delivered.length === 0 (workflow-script.ts, the missingRoles block near line 125), while its own contract comment says a non-empty missingRoles is a failed step, not a shorter finding set. When one required agent times out, fails dispatch, or returns empty while others succeed, the workflow completes successfully and the caller can aggregate a review silently lacking a required dimension. Original probe: the unmodified script returned { rosterSize: 2, delivered: [one agent], missingRoles: ['b'] } with exit success. Unchanged since the comment; the mechanism is present at the reviewed commit.

[Critical] R2-3: Re-checked existing blocker (comment 3863610979, @wenshao) — still stands at HEAD. reviewWorkflowsDir maps the session id through the lossy sanitizeFilenameComponent and the script name digests only the resolved plan path (paths.ts, reviewWorkflowsDir/reviewWorkflowScriptPath), so distinct concurrent sessions such as sess.1 and sess_1 resolve to the same directory and, for the same plan path, select the same script target; the later atomic rename replaces the earlier session's script, so the earlier session can dispatch the other session's roster, rules, and worktree pin. Original probe: both ids resolved to the same directory. Unchanged since the comment; the mechanism is present at the reviewed commit.

[Critical] R2-4: Re-checked existing blocker (comment 3863610988, @wenshao) — still stands at HEAD. reviewWorkflowScriptPath hashes resolve(planPath) without realpath canonicalization (paths.ts), and on macOS the same existing plan can be spelled /var/... and /private/var/..., giving different script names for the same file and failing the relative-versus-absolute identity test on Darwin. Original probe on the unmodified PR: the test expected qwen-review-63dc4469bc.js but received qwen-review-aefd102e7b.js (1 failed, 22 passed). The PR's macOS CI legs were skipped and its description marks macOS untested. Unchanged since the comment; the mechanism is present at the reviewed commit.

中文说明

未探索到全部深度(达到工具调用预算):"agent 3b"execute the two new test files (the shared worktree has no node_modules or built dist; a full install + build exceeded the remaining tool budget and would leave…

Test Plan(非阻断):43 passed — this review observed 24797 passed; 119 passed — this review observed 24797 passed

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

[Critical] R2-1: Re-checked existing blocker (comment 3863610970, @wenshao) — still stands at HEAD. Symlinked generated-workflow directories are written through: runEmitWorkflow runs mkdirSync(dirname(scriptPath), {recursive: true}) and a temp+rename write under $QWEN_CODE_PROJECT_DIR/workflows/generated/review/<session>, and no step rejects symlinked path components or verifies the canonical parent stays under the canonical generated-workflow root, while the loader (readWorkflowFileSecurely) realpaths and refuses. If the root or session directory is a symlink to an external directory, the script embedding every review prompt is written outside the trusted root and the printed scriptPath is unloadable. Original probe: with a symlinked session directory, emit-workflow succeeded and the external target contained the script. The write path is unchanged since the comment (the only later commit is test-only), so the mechanism is present at the reviewed commit.

[Critical] R2-2: Re-checked existing blocker (comment 3863611003, @wenshao) — still stands at HEAD. FAN_OUT_BODY collects missing roles and only logs them, throwing solely when delivered.length === 0 (workflow-script.ts, the missingRoles block near line 125), while its own contract comment says a non-empty missingRoles is a failed step, not a shorter finding set. When one required agent times out, fails dispatch, or returns empty while others succeed, the workflow completes successfully and the caller can aggregate a review silently lacking a required dimension. Original probe: the unmodified script returned { rosterSize: 2, delivered: [one agent], missingRoles: ['b'] } with exit success. Unchanged since the comment; the mechanism is present at the reviewed commit.

[Critical] R2-3: Re-checked existing blocker (comment 3863610979, @wenshao) — still stands at HEAD. reviewWorkflowsDir maps the session id through the lossy sanitizeFilenameComponent and the script name digests only the resolved plan path (paths.ts, reviewWorkflowsDir/reviewWorkflowScriptPath), so distinct concurrent sessions such as sess.1 and sess_1 resolve to the same directory and, for the same plan path, select the same script target; the later atomic rename replaces the earlier session's script, so the earlier session can dispatch the other session's roster, rules, and worktree pin. Original probe: both ids resolved to the same directory. Unchanged since the comment; the mechanism is present at the reviewed commit.

[Critical] R2-4: Re-checked existing blocker (comment 3863610988, @wenshao) — still stands at HEAD. reviewWorkflowScriptPath hashes resolve(planPath) without realpath canonicalization (paths.ts), and on macOS the same existing plan can be spelled /var/... and /private/var/..., giving different script names for the same file and failing the relative-versus-absolute identity test on Darwin. Original probe on the unmodified PR: the test expected qwen-review-63dc4469bc.js but received qwen-review-aefd102e7b.js (1 failed, 22 passed). The PR's macOS CI legs were skipped and its description marks macOS untested. Unchanged since the comment; the mechanism is present at the reviewed commit.

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

…M#10119)

Address the four review blockers on the emit-workflow PR:

- Share the loader's canonical-containment policy on the write side:
  refuse a symlinked directory from the generated root down to the
  session dir, and prove the canonical session dir stays under the
  canonical root, before any brief, prompt record, or script is written.
- Keep colliding sanitized session ids apart by appending a digest of
  the RAW session id to the readable prefix, so two concurrent sessions
  can never select the same script target for the same plan.
- Canonicalize an existing plan path with realpath before hashing it,
  so one plan keeps one script name under divergent spellings of the
  same file (macOS /var vs /private/var, or a link).
- Fail the fan-out whenever any required agent delivered nothing,
  instead of returning a shortened delivered list; a missing role is a
  failed step, not a shorter finding set.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Address-review round summary — PR #10119

Round scope: 4 Critical findings, each a re-checked existing blocker confirmed
still present at HEAD by both reviewers. All four are resolved in code in
commit 12f5d47a99, each with a witness test that was mutation-probed.

Feedback points and dispositions

[rc:3863610970] R2-1 — symlinked generated-workflow directories written through → Fixed

Reproduced by code reading: runEmitWorkflow ran mkdirSync(dirname(scriptPath), {recursive: true})
and a temp+rename write with no symlink rejection and no canonical-containment
check, while the loader (readWorkflowFileSecurely, core workflow-saved.ts)
realpaths the file and refuses a symlinked root — so a linked root or session
directory carried the script (embedding every review prompt) outside the
trusted root, and the printed scriptPath was unloadable.

Fix: ensureWritableReviewWorkflowsDir() in review/lib/paths.ts — the
writer half of the loader's canonical-containment policy. It refuses a
symlinked directory from the generated root down to the session dir (lstat
walk), creates the session dir, then proves via realpathSync that the
canonical session dir stays under the canonical root. Called in
runEmitWorkflow BEFORE buildFanOutRoster, so no brief, prompt record, or
script content exists when the refusal lands; the old standalone mkdirSync
was removed (the guard creates the directory).

Witness: refuses a symlinked session directory before writing anything and
refuses a symlinked generated root before writing anything — both assert the
throw, an empty external dir, and zero prompt records.

[rc:3863610979] R2-3 — lossy session-id sanitization collides directories → Fixed

Reproduced by code reading: sanitizeFilenameComponent('sess.1') and
sanitizeFilenameComponent('sess_1') both yield sess_1, so two concurrent
sessions selected the same script target for the same plan and the later
atomic rename replaced the earlier session's script.

Fix: reviewSessionDirName() keeps the readable sanitized prefix and appends
an 8-hex-char sha256 digest of the RAW session id
(<sanitized>-<digest>), keeping the prefix sweep-compatible while making
distinct raw ids collision-resistant.

Witness: keeps sessions that sanitize identically in separate directories
asserts distinct dirs, surviving sess_1- prefix on both, and distinct script
paths for the same plan; the existing writes the script under the generated-scripts root, per session now pins the digest of the raw
'sess.1' id.

[rc:3863610988] R2-4 — plan path hashed without canonicalization → Fixed

Mechanism confirmed by code reading: reviewWorkflowScriptPath hashed
resolve(planPath) directly, so two spellings of the same existing file
(macOS /var/... vs /private/var/...; the reported Darwin test failure)
produced different script names for one plan.

Fix: canonicalize the resolved plan with realpathSync before hashing, with a
lexical fallback when the plan is not on disk (the caller's read then fails on
its own terms). Mirrors the loader, which canonicalizes with realpath too.

Witness: names an existing plan one script however the path is spelled — a
symlinked alias of an existing plan resolves to the same script path; this is
the platform-neutral shape of the Darwin divergence and fails on Linux when
canonicalization is removed.

[rc:3863611003] R2-2 — workflow succeeds when a required agent returns nothing → Fixed

Reproduced by executing the real generated script in the vm harness (pre-fix
behavior: one dead agent returned { rosterSize: 3, delivered: [...2], missingRoles: ['2'] }
with exit success), matching the reported probe.

Fix: FAN_OUT_BODY now throws review fan-out: required agents failed to deliver (<keys>) whenever missingRoles is non-empty, per the contract
comment beside the collection loop. The now-unreachable delivered.length === 0
branch was removed.

Witness: four tests flipped from asserting a shortened successful result to
asserting rejection naming the missing role:
rejects, naming the agent, when a dispatched agent returned nothing,
rejects on an undefined return, not a shorter finding set,
rejects when a result strips to empty, not as delivered (both '' and
whitespace), and the all-failed case now expects
required agents failed to deliver (1a, 2, 7).

Not in this round's actionable set (recorded, no action)

  • The rv bodies' R2-1..R2-4 entries are the same four blockers as the inline
    comments above (one-to-one), not additional work.
  • The four Suggestion-level findings (R1-1..R1-4, comments 3862296232/40/50/62)
    were listed only as "already reported and not repeated"; they are not part
    of this round's actionable sections.
  • The two items deferred under the convergence posture (meta-purity oracle
    probe; handler cannot-read-the-plan guard test) were explicitly "recorded,
    not requested in this round".
  • No Growth audit required section; no failed checks; no conflicts
    (--conflict false, no merge performed).

Mutation probes (per guard, before committing)

Guard Mutation Expected tests red Result
R2-1 ensureWritableReviewWorkflowsDir() call call removed both symlink-refusal tests RED (7 failed incl. both), restored → green
R2-2 missingRoles throw condition negated (&& false) all 4 fail-closed tests RED (4 failed), restored → green
R2-3 raw-id digest return sanitized prefix only collision test + session-dir shape test RED (2 failed), restored → green
R2-4 realpath canonicalization canonical = resolved spelling-identity test RED (test red), restored → green

Files changed

  • packages/cli/src/commands/review/lib/paths.ts — session-dir digest,
    realpath-canonicalized plan digest, ensureWritableReviewWorkflowsDir().
  • packages/cli/src/commands/review/emit-workflow.ts — guard call before
    roster build; standalone mkdirSync removed.
  • packages/cli/src/commands/review/workflow-script.ts — fail-closed throw on
    any missing role; unreachable all-failed branch removed.
  • packages/cli/src/commands/review/emit-workflow.test.ts — updated
    session-dir expectation + 4 new witness tests.
  • packages/cli/src/commands/review/workflow-script.test.ts — 4 tests flipped
    to assert fail-closed rejection.

Verification

Commands actually run and their results:

  • npm run build — passed (exit 0)
  • npm run typecheck — passed (exit 0)
  • npm run lint — passed (exit 0)
  • npx prettier --check on the 5 changed files — passed (after --write on 3 files; re-checked clean on the final tree)
  • cd packages/cli && npx vitest run src/commands/review/emit-workflow.test.ts src/commands/review/workflow-script.test.ts — 2 files, 43 passed
  • cd packages/cli && npx vitest run src/commands/review — 105 files, 5139 passed, 5 skipped (pre-existing skips)
  • Mutation probes ×4 as tabled above — each guard removal turned its witness red; restore returned all 43 to green
  • Integration tests after npm run bundle — not run: the touched behavior (command handler, path helpers, generated-script execution) is exercised directly by the unit tests above, not only through the bundled CLI or integration harness
  • npm run generate:settings-schema — not applicable: no settings source changed

Self-audit note: the first audit pass caught four accidental comment
rewraps/rewords introduced while rewriting files; the exact HEAD comment text
was restored before committing (verified in the final diff).

中文说明

处理评审意见轮次总结 — PR #10119

本轮范围:4 条 Critical 发现,均为两位审查者确认在 HEAD 上仍然存在既有阻断项的复核。四条全部在提交 12f5d47a99于代码层面解决,每条都有经过变异探针验证的见证测试。

反馈点与处置

[rc:3863610970] R2-1 — 符号链接的生成 workflow 目录被穿透写入 → 已修复

通过代码阅读复现:runEmitWorkflow 执行 mkdirSync(dirname(scriptPath), {recursive: true}) 与临时文件+rename 写入,既不拒绝符号链接也不做 canonical-containment 检查;而加载端(core workflow-saved.tsreadWorkflowFileSecurely)会对文件做 realpath 并拒绝符号链接根目录——因此被链接的根目录或 session 目录会把脚本(内嵌全部 review prompt)写到可信根之外,输出的 scriptPath 也无法被加载。

修复:review/lib/paths.ts 中的 ensureWritableReviewWorkflowsDir()——加载端 canonical-containment 策略的写入端对应实现。它拒绝从生成根目录到 session 目录之间任何符号链接目录(lstat 逐层检查),创建 session 目录,再用 realpathSync 证明 canonical session 目录仍在 canonical 根目录之下。在 runEmitWorkflow 中于 buildFanOutRoster 之前调用,确保拒绝落地时不存在任何 brief、prompt record 或脚本内容;原先独立的 mkdirSync 已删除(由该守卫负责创建目录)。

见证:refuses a symlinked session directory before writing anythingrefuses a symlinked generated root before writing anything——两者均断言抛出、外部目录为空且 prompt record 数为零。

[rc:3863610979] R2-3 — 有损的 session id 清洗导致目录碰撞 → 已修复

通过代码阅读复现:sanitizeFilenameComponent('sess.1')sanitizeFilenameComponent('sess_1') 都得到 sess_1,因此两个并发 session 对同一 plan 会选中同一脚本目标,后一次原子 rename 会覆盖前一个 session 的脚本。

修复:reviewSessionDirName() 保留可读的清洗前缀,并追加原始(RAW)session id 的 sha256 摘要(8 位十六进制,形如 <sanitized>-<digest>),在保持前缀可被同一套清扫规则匹配的同时,使不同原始 id 抗碰撞。

见证:keeps sessions that sanitize identically in separate directories 断言目录不同、两者的 sess_1- 前缀均保留、同一 plan 的脚本路径不同;既有的 writes the script under the generated-scripts root, per session 现在固定为原始 'sess.1' id 的摘要。

[rc:3863610988] R2-4 — plan 路径未经 canonicalize 即哈希 → 已修复

通过代码阅读确认机制:reviewWorkflowScriptPath 直接哈希 resolve(planPath),因此同一已存在文件的两种写法(macOS 的 /var/.../private/var/...,即报告中的 Darwin 测试失败)会为同一 plan 生成不同脚本名。

修复:哈希前先用 realpathSync canonicalize 已解析的 plan;plan 不在磁盘上时回退为词法路径(调用方的读取会按自身语义失败)。与加载端一致——后者同样用 realpath canonicalize。

见证:names an existing plan one script however the path is spelled——已存在 plan 的符号链接别名解析出相同脚本路径;这是 Darwin 分歧的平台无关形态,移除 canonicalization 后在 Linux 上同样变红。

[rc:3863611003] R2-2 — 必需 agent 无返回时 workflow 仍成功 → 已修复

通过在 vm 测试装置中执行真实生成的脚本复现(修复前行为:一个 agent 失败时返回 { rosterSize: 3, delivered: [...2], missingRoles: ['2'] } 且退出成功),与报告中的探针一致。

修复:FAN_OUT_BODY 现在只要 missingRoles 非空就抛出 review fan-out: required agents failed to deliver (<keys>),与收集循环旁的契约注释一致。此后不可达的 delivered.length === 0 分支已删除。

见证:四个测试从断言"带缺失角色的成功返回"翻转为断言"拒绝并点名缺失角色":rejects, naming the agent, when a dispatched agent returned nothingrejects on an undefined return, not a shorter finding setrejects when a result strips to empty, not as delivered'' 与纯空白两种),以及全失败场景现在期望 required agents failed to deliver (1a, 2, 7)

不属于本轮可执行集合(仅记录,不处理)

  • 两个 rv body 中的 R2-1..R2-4 条目与上述内联评论是同一批阻断项(一一对应),不是额外工作。
  • 四条 Suggestion 级发现(R1-1..R1-4,评论 3862296232/40/50/62)仅被列为"已报告过,不再重复",不属于本轮可执行部分。
  • 收敛姿态下延后的两条(meta-purity oracle 探针;handler cannot-read-the-plan 守卫测试)明确为"已记录,本轮不要求修改"。
  • Growth audit required 部分;无失败检查项;无冲突(--conflict false,未做任何合并)。

变异探针(提交前逐个守卫执行)

守卫 变异方式 应变红的测试 结果
R2-1 ensureWritableReviewWorkflowsDir() 调用 删除调用 两个符号链接拒绝测试 变红(7 失败,含两者),还原 → 绿
R2-2 missingRoles 抛错 条件取反(&& false 全部 4 个 fail-closed 测试 变红(4 失败),还原 → 绿
R2-3 原始 id 摘要 仅返回清洗前缀 碰撞测试 + session 目录形状测试 变红(2 失败),还原 → 绿
R2-4 realpath canonicalize canonical = resolved 路径写法同一性测试 变红(该测试失败),还原 → 绿

变更文件

  • packages/cli/src/commands/review/lib/paths.ts — session 目录摘要、plan 摘要改用 realpath canonicalize、ensureWritableReviewWorkflowsDir()
  • packages/cli/src/commands/review/emit-workflow.ts — 在构建 roster 前调用守卫;删除独立的 mkdirSync
  • packages/cli/src/commands/review/workflow-script.ts — 任一必需角色缺失即抛错;删除不可达的全失败分支。
  • packages/cli/src/commands/review/emit-workflow.test.ts — 更新 session 目录期望值 + 4 个新见证测试。
  • packages/cli/src/commands/review/workflow-script.test.ts — 4 个测试翻转为断言 fail-closed 拒绝。

验证

实际执行的命令及结果:

  • npm run build — 通过(exit 0)
  • npm run typecheck — 通过(exit 0)
  • npm run lint — 通过(exit 0)
  • npx prettier --check(5 个变更文件)— 通过(先对 3 个文件执行 --write;最终代码树复检干净)
  • cd packages/cli && npx vitest run src/commands/review/emit-workflow.test.ts src/commands/review/workflow-script.test.ts — 2 个文件,43 通过
  • cd packages/cli && npx vitest run src/commands/review — 105 个文件,5139 通过,5 跳过(既有跳过项)
  • 变异探针 ×4(见上表)— 每个守卫被移除时其见证测试变红;还原后 43 个测试全部回绿
  • npm run bundle 后的集成测试 — 未执行:本次触及的行为(命令 handler、路径辅助函数、生成脚本的执行)已由上述单元测试直接覆盖,并非只能经由打包 CLI 或集成测试框架验证
  • npm run generate:settings-schema — 不适用:未改动任何 settings 源

自审说明:第一轮自审发现重写文件时意外带入的四处注释重排/措辞改动;提交前已恢复为 HEAD 的原始注释文本(已在最终 diff 中确认)。

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

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

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

Round 3 — Re-check of 4 Round 2 blockers

All four Round 2 Criticals are fixed at HEAD 12f5d47. 3 of 4 Suggestions are also addressed.

R2-1: ❌→✅ Fixed — Symlinked generated-workflow directories rejected before writing

ensureWritableReviewWorkflowsDir() in paths.ts checks for symlinks at the generated root, review subdirectory, and session directory levels before creating the session directory, then verifies canonical containment. Tests cover both symlinked root and session directory, and verify no briefs or records are written before the refusal.

R2-2: ❌→✅ Fixed — Fan-out fails closed on any missing agent

FAN_OUT_BODY in workflow-script.ts now throws on ANY missing required agent (not just when all are missing). Tests verify the throw for null, undefined, and empty-string returns.

R2-3: ❌→✅ Fixed — Collision-resistant session directories

reviewSessionDirName() appends a SHA-256 digest of the raw session id to the sanitized component. sess.1 and sess_1 produce different directories. Dedicated test verifies they cannot overwrite each other's script.

R2-4: ❌→✅ Fixed — Plan path canonicalized before hashing

reviewWorkflowScriptPath() uses realpathSync on existing plans before hashing, so /var/... and /private/var/... produce the same script name. Symlink alias test verifies identity.

Suggestions

Suggestion Status
Temp file test: both success and failure paths ✅ Fixed
--rules happy path test ✅ Fixed
runScript harness divergence ↔️ Standing (acknowledged design trade-off)
Determinism guard: Date.parse/etc. ✅ Fixed

No new issues found.

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

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

  • meta-purity oracle at workflow-script.test.ts:119-126 (a regex weaker than the runtime parser it stands in for) — already reported in round 2's deferred list (review 5031737007)

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

Test Plan (not a blocker): 43 passed — this review observed 24801 passed; 119 passed — this review observed 24801 passed.

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

  • packages/cli/src/commands/review/emit-workflow.ts:172 — [probe] Key-mismatch guard in buildFanOutRoster untested
中文说明

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

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

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

Test Plan(非阻断):43 passed — this review observed 24801 passed; 119 passed — this review observed 24801 passed

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

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

Comment on lines +417 to +421
const scriptPath = reviewWorkflowScriptPath(plan);
mkdirSync(dirname(scriptPath), { recursive: true });
writeFileSync(victim, 'keep me', 'utf8');
symlinkSync(victim, scriptPath);
writeFileSync(plan, JSON.stringify(localPlan()), 'utf8');

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] R3-1: The replaces a symlinked script entry without writing through it test computes scriptPath while plan.json does not exist yet (it is written five lines down), while runEmitWorkflow recomputes the script path after the plan exists. reviewWorkflowScriptPath canonicalizes with realpathSync only when the plan exists, so on any host where os.tmpdir() resolves through a symlink — macOS's default TMPDIR=/var/folders/… with /var → /private/var — the test-side digest is taken over the aliased spelling and the handler-side digest over the canonical one. The handler then writes the script under a DIFFERENT filename than the one the symlink was planted at: expect(lstatSync(scriptPath).isSymbolicLink()).toBe(false) fails and the readFileSync(victim) "keep me" assertion passes vacuously, so the replace-don't-follow property this test exists to pin is never exercised on that platform. The ubuntu lane has a real-path tmpdir, so CI stays green; the red shows on local macOS runs, and possibly the macOS merge-queue lane depending on whether RUNNER_TEMP is alias-free.

Witness (aliased-TMPDIR reproduction of the macOS shape on Linux):

BASE (real TMPDIR): Tests 1 passed | 27 skipped (28)
PR (aliased TMPDIR): FAIL emit-workflow.test.ts:425 AssertionError: expected true to be false
  at expect(lstatSync(scriptPath).isSymbolicLink()).toBe(false)
FIXED (aliased TMPDIR, fixture canonicalized): Tests 1 passed | 27 skipped (28)

Canonicalize the fixture root in this describe's beforeEach — the house pattern from local-diff.integration.test.tsdir = realpathSync(mkdtempSync(join(tmpdir(), 'emit-wf-'))); with realpathSync imported from node:fs; both sides then spell every path identically on every platform. With the fixture canonicalized, replacing the implementation's temp-file + renameSync with a plain writeFileSync(scriptPath, …) writes through the symlink, overwrites the victim, and expect(readFileSync(victim, 'utf8')).toBe('keep me') goes red — please confirm that mutation turns the test red.

中文说明

replaces a symlinked script entry without writing through it 测试在 plan.json 尚未写入时(计划文件在五行之后才写入)就计算 scriptPath,而 runEmitWorkflow 会在计划文件存在后重新计算脚本路径。reviewWorkflowScriptPath 只在计划文件存在时才用 realpathSync 做 canonicalize,因此在 os.tmpdir() 经由符号链接解析的主机上(macOS 默认 TMPDIR=/var/folders/…,且 /var → /private/var),测试侧摘要基于带别名的拼写,而处理器侧摘要基于规范化路径。处理器会把脚本写到一个与符号链接植入位置不同的文件名下:expect(lstatSync(scriptPath).isSymbolicLink()).toBe(false) 失败,而 readFileSync(victim) 的 "keep me" 断言空转通过——该测试本要钉住的「替换而非穿透写入」属性在该平台上从未被真正验证。ubuntu lane 的 tmpdir 是真实路径,CI 保持绿色;红色只出现在本地 macOS 运行(以及可能的 macOS 合并队列 lane,取决于 RUNNER_TEMP 是否含别名)。

实测(在 Linux 上用带别名的 TMPDIR 复现 macOS 形态):真实 TMPDIR 下通过;别名 TMPDIR 下在 emit-workflow.test.ts:425 以 AssertionError: expected true to be false 失败;fixture canonicalize 后恢复通过。

修复:在本 describe 的 beforeEach 中规范化 fixture 根目录(local-diff.integration.test.ts 的既有模式):dir = realpathSync(mkdtempSync(join(tmpdir(), 'emit-wf-')));(从 node:fs 导入 realpathSync),两侧路径拼写在所有平台上即保持一致。修复后请验证变异:把实现中的临时文件 + renameSync 换成直接 writeFileSync(scriptPath, …),应穿透符号链接覆盖 victim,使 readFileSync(victim) 断言变红。

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

Comment on lines +449 to +451
it('refuses a symlinked generated root before writing anything', () => {
const plan = join(dir, 'plan.json');
writeFileSync(plan, JSON.stringify(localPlan()), 'utf8');

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.

[Suggestion] R3-2: The symlink-containment suite pins refusal for the generated root and for the session directory, but nothing exercises the middle loop component of ensureWritableReviewWorkflowsDirjoin(root, REVIEW_WORKFLOWS_SUBDIR), the review directory — so that branch ships unwitnessed. If a refactor drops the middle component (leaving the loop [root, dir]) and $QWEN_CODE_PROJECT_DIR/workflows/generated/review is a symlink to an external directory, lstatSync(dir) ENOENTs through the link, the loop breaks, mkdirSync(dir, {recursive: true}) follows the link and creates the session directory at the external target, and only then the post-mkdir canonical-containment check throws — with the generic "escapes the canonical root" message instead of /symlinked/. A stray directory is created outside the trusted root on a refused emit, the specific diagnostic is lost, and every test in this describe block stays green (containment still refuses, so there is no escape — hence a Suggestion).

Witness (probe): with the loop reduced to [root, dir], a third test mirroring the other two is the only failure — Tests 1 failed | 28 passed (29) — and driving ensureWritableReviewWorkflowsDir directly shows the generic escape message plus external contents = ["sess_1-58d657d2"] (the stray directory).

Add a third test mirroring the other two: symlink join(projectDir, 'workflows', 'generated', 'review') to an external directory, then expect run(plan) to throw /symlinked/, readdirSync(external) to be [], and readRecordedPrompts(plan).size to be 0. The new test is its own pin: removing join(root, REVIEW_WORKFLOWS_SUBDIR) from the loop must turn it red — please confirm the mutation does.

中文说明

符号链接收容测试套件钉住了生成根目录与 session 目录两层的拒绝,但 ensureWritableReviewWorkflowsDir 循环的中间组件——join(root, REVIEW_WORKFLOWS_SUBDIR),即 review 目录——没有任何测试覆盖,该分支在无见证的情况下上线。若重构删掉中间组件(循环变为 [root, dir]),且 $QWEN_CODE_PROJECT_DIR/workflows/generated/review 是指向外部目录的符号链接:lstatSync(dir) 会透过链接得到 ENOENT,循环 break,mkdirSync(dir, {recursive: true}) 沿链接在外部目标创建 session 目录,之后 post-mkdir 的 canonical 容纳检查才抛错——且抛出的是通用的 "escapes the canonical root" 而非 /symlinked/。结果是拒绝写入前已在可信根之外留下游离目录、丢失特定诊断信息,而 describe 块内所有测试保持绿色(容纳检查仍会拒绝,不存在逃逸——因此是建议级)。

实测(探针):把循环缩减为 [root, dir] 后,镜像另两条的第三条测试是唯一失败项(Tests 1 failed | 28 passed (29));直接驱动 ensureWritableReviewWorkflowsDir 可见通用逃逸信息与 external contents = ["sess_1-58d657d2"](游离目录)。

修复:新增镜像测试——把 join(projectDir, 'workflows', 'generated', 'review') 链接到外部目录,断言 run(plan) 抛出 /symlinked/readdirSync(external)[]readRecordedPrompts(plan).size 为 0。新测试自身即见证:从循环中移除 join(root, REVIEW_WORKFLOWS_SUBDIR) 后它必须变红,请确认该变异生效。

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round summary — PR #10119

Both inline findings of round 3 are resolved in code (one commit, cf4696c734, test-only). No conflict handling was needed (--conflict false), the branch feat/review-emit-workflow-v2 was kept, and the commit is additive.

Inline findings

[rc:3865455294] R3-1 (Critical) — symlinked-script test diverges under a symlinked tmpdir → Fixed

The claim was reproduced before any change. The test computes reviewWorkflowScriptPath(plan) before plan.json exists, while the handler recomputes it after; reviewWorkflowScriptPath canonicalizes with realpathSync only when the plan exists. On this Linux host /tmp is a real path, so a witness matching the macOS shape was built: a real directory plus a symlink alias, run as TMPDIR=<alias>.

  • Baseline (real TMPDIR): Tests 1 passed | 27 skipped (28)
  • Reproduction (aliased TMPDIR): FAIL emit-workflow.test.ts > replaces a symlinked script entry without writing through it → AssertionError: expected true to be false, i.e. the handler wrote under a different filename than the planted symlink, and the property the test pins was never exercised.

Fix (as suggested, the house pattern used 26× across review tests): canonicalize the fixture root in the where it writes describe's beforeEachdir = realpathSync(mkdtempSync(join(tmpdir(), 'emit-wf-'))). Only that describe changed: it is the only one with a pre-plan/post-plan path-identity comparison (checked test by test).

Witness probe after the fix: replacing the implementation's temp-file + renameSync with a plain writeFileSync(scriptPath, …) turns the test red — AssertionError: expected 'export const meta…' to be 'keep me' (the write follows the symlink and overwrites the victim). Implementation restored byte-exact afterwards (git diff empty); the test green again on the restored code.

[rc:3865455314] R3-2 (Suggestion) — middle loop component of ensureWritableReviewWorkflowsDir unwitnessed → Fixed

The gap was probed first: with the loop reduced to [root, dir] (middle component join(root, REVIEW_WORKFLOWS_SUBDIR) removed), the full file stayed green — Tests 28 passed (28) — confirming the branch ships unwitnessed, exactly as described.

Added the third test mirroring the other two: symlink join(projectDir, 'workflows', 'generated', 'review') to an external directory, expect run(plan) to throw /symlinked/, readdirSync(external) to be [], and readRecordedPrompts(plan).size to be 0.

Witness probe: removing the middle component again makes the new test the only failure — Tests 1 failed | 28 passed (29) — failing on expected [Function] to throw error matching /symlinked/ but got 'refusing to write a generated review …' (the generic escape message, after a stray session directory was created outside the trusted root). Restored, green again.

Reviews (no action required)

  • [rv:5032669862] @doudouOUC round-3 re-check — confirms all four Round 2 blockers fixed at 12f5d47, no new issues. The remaining table entry (runScript harness divergence) is marked Standing / acknowledged design trade-off. Nothing to do.
  • [rv:5033638522] automated reviewer — partially reviewed, gaps disclosed — its two posted findings are the inline comments above, both resolved. Its body discloses, without requesting action this round: the reverse audit did not converge within its cap; a Test Plan count observation (non-blocking — the PR text cites focused runs, the review observed the wider suite); and one item deferred under the convergence posture, "recorded, not requested in this round": emit-workflow.ts:172 — [probe] Key-mismatch guard in buildFanOutRoster untested. Left as recorded for a later round. The meta-purity oracle item (workflow-script.test.ts:119-126) was already on round 2's deferred list and is not re-requested; it stays tracked there.

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx prettier --check packages/cli/src/commands/review/emit-workflow.test.ts — passed
  • npx vitest run src/commands/review/emit-workflow.test.ts (focused, real TMPDIR) — 29/29 passed (pre-round baseline: 28/28)
  • npx vitest run src/commands/review/emit-workflow.test.ts (focused, aliased TMPDIR via TMPDIR=<symlink alias>) — 29/29 passed (pre-round: 1 failed — the R3-1 reproduction)
  • Mutation probe R3-1 (plain writeFileSync at scriptPath, rename removed): symlink test red (expected 'export const meta…' to be 'keep me'); restored → green
  • Mutation probe R3-2 gap (loop [root, dir], pre-fix suite): 28/28 green — gap confirmed
  • Mutation probe R3-2 witness (loop [root, dir], post-fix suite): new test the only failure, 1 failed | 28 passed; restored → 29/29 green
  • No integration run: the change is test-only unit coverage, not behavior exercised solely through the bundled CLI. No settings source touched, so no schema regeneration.
中文说明

Autofix 轮次总结 — PR #10119

第 3 轮的两条行内发现均已在代码中解决(单个提交 cf4696c734,纯测试改动)。无需处理冲突(--conflict false),分支 feat/review-emit-workflow-v2 保持不变,提交为增量提交。

行内发现

[rc:3865455294] R3-1(Critical)——符号链接脚本测试在带符号链接的 tmpdir 下发生分歧 → 已修复

修改前先复现了该论断。测试在 plan.json 尚不存在时计算 reviewWorkflowScriptPath(plan),而处理器会在其后重新计算;reviewWorkflowScriptPath 只在计划文件存在时才用 realpathSync 做规范化。本机 Linux 的 /tmp 是真实路径,因此构造了与 macOS 形态一致的见证环境:一个真实目录加一个符号链接别名,以 TMPDIR=<别名> 运行。

  • 基线(真实 TMPDIR):Tests 1 passed | 27 skipped (28)
  • 复现(别名 TMPDIR):FAIL emit-workflow.test.ts > replaces a symlinked script entry without writing through it → AssertionError: expected true to be false,即处理器把脚本写到了与植入符号链接不同的文件名下,该测试本要钉住的属性从未被真正验证。

修复(按建议采用 review 测试中已使用 26 次的既有模式):在 where it writes describe 的 beforeEach 中规范化 fixture 根目录 —— dir = realpathSync(mkdtempSync(join(tmpdir(), 'emit-wf-')))。只改了这一个 describe:逐条测试核对后确认它是唯一存在「计划写入前/后」路径同一性比较的 describe。

修复后的见证探针:把实现中的临时文件 + renameSync 替换为直接 writeFileSync(scriptPath, …),测试变红 —— AssertionError: expected 'export const meta…' to be 'keep me'(写入穿透符号链接覆盖了 victim)。随后逐字节恢复实现(git diff 为空),测试恢复绿色。

[rc:3865455314] R3-2(Suggestion)—— ensureWritableReviewWorkflowsDir 循环的中间组件无见证 → 已修复

先做缺口探针:把循环缩减为 [root, dir](移除中间组件 join(root, REVIEW_WORKFLOWS_SUBDIR))后,整个测试文件仍全绿 —— Tests 28 passed (28) —— 证实该分支确实在无见证的情况下上线,与描述完全一致。

新增镜像另两条的第三条测试:把 join(projectDir, 'workflows', 'generated', 'review') 符号链接到外部目录,断言 run(plan) 抛出 /symlinked/readdirSync(external)[]readRecordedPrompts(plan).size 为 0。

见证探针:再次移除中间组件后,新测试是唯一失败项 —— Tests 1 failed | 28 passed (29) —— 失败于 expected [Function] to throw error matching /symlinked/ but got 'refusing to write a generated review …'(通用的逃逸信息,且此时可信根之外已被创建了一个游离 session 目录)。恢复后重新变绿。

评审(无需处理)

  • [rv:5032669862] @doudouOUC 第 3 轮复查 —— 确认第 2 轮全部四个阻断项已在 12f5d47 修复,无新问题。表格中剩余一项(runScript harness 分歧)标记为 Standing/已确认的设计权衡。无需处理。
  • [rv:5033638522] 自动审查器 —— 部分审查,缺口已披露 —— 其发布的两条发现即上述两条行内评论,均已解决。正文披露且本轮不要求处理的内容:反向审计在轮数上限内未收敛;Test Plan 计数观察(非阻断 —— PR 文本引用的是聚焦运行,审查观察到的是更大范围的套件);以及收敛姿态下延后的一项,「已记录,本轮不要求修改」:emit-workflow.ts:172 — [probe] Key-mismatch guard in buildFanOutRoster untested,留作后续轮次处理。meta-purity oracle 一项(workflow-script.test.ts:119-126)已在第 2 轮延后列表中,本轮未重新提出要求,继续在那里跟踪。

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • npx prettier --check packages/cli/src/commands/review/emit-workflow.test.ts — 通过
  • npx vitest run src/commands/review/emit-workflow.test.ts(聚焦,真实 TMPDIR)— 29/29 通过(轮前基线:28/28)
  • npx vitest run src/commands/review/emit-workflow.test.ts(聚焦,TMPDIR=<符号链接别名> 的别名 TMPDIR)— 29/29 通过(轮前:1 失败 —— 即 R3-1 复现)
  • R3-1 变异探针(在 scriptPath 处直接 writeFileSync、移除 rename):符号链接测试变红(expected 'export const meta…' to be 'keep me');恢复后变绿
  • R3-2 缺口探针(循环 [root, dir],修复前套件):28/28 全绿 —— 缺口确认
  • R3-2 见证探针(循环 [root, dir],修复后套件):新测试为唯一失败项,1 failed | 28 passed;恢复后 29/29 全绿
  • 未运行集成测试:本次改动为纯单元测试覆盖,不涉及仅经打包 CLI 验证的行为。未触碰任何设置源,因此无需重新生成 schema。

🦷 Gate advisory — this round resolves a Critical/Request-changes finding with test-only changes (machine-measured): the bite check cannot verify a test-side fix, so the resolution rests on the round summary alone. · 本轮以纯测试改动解决 Critical/Request-changes 反馈(门自动测量):bite 检查无法验证测试侧修复,该解决仅以轮次摘要为凭。

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

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

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

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

  • cannot-read-the-plan handler-test gap (emit-workflow.test.ts) — already reported in round 2's deferred list (review 5031737007)

Not reviewed: build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Test Plan (not a blocker): 43 passed — this review observed 24807 passed; 119 passed — this review observed 24807 passed.

中文说明

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

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

未审查:build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未审查:build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

Test Plan(非阻断):43 passed — this review observed 24807 passed; 119 passed — this review observed 24807 passed

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix review-address round — PR #10119

Feedback triaged this round: 1 item (round-4 review rv:5034531001). No inline comments, no failed checks, no still-red checks, no base conflict (--conflict false).

Finding 1 — cannot-read-the-plan handler-test gap (emit-workflow.test.ts) — Implemented

Source: round-4 review confirmed this Suggestion-level finding was already reported in round 2's deferred list (review 5031737007): "no test pins the handler's cannot-read-the-plan guard every sibling command pins".

Verified first: runEmitWorkflow wraps the plan read in a try/catch that throws emit-workflow: cannot read the plan <path>: … (packages/cli/src/commands/review/emit-workflow.ts:189-195). Every sibling review subcommand pins its equivalent guard at handler level — agent-prompt.test.ts:409 ("names the plan it could not read, instead of a raw stack"), build-test.test.ts:2377, base-tree.test.ts:293 — but emit-workflow.test.ts pinned only the unreadable-rules guard. The gap is real.

Change (test-only, +9 lines): added one handler-level test, refuses an unreadable plan path before writing anything, in the emit-workflow — where it writes suite beside the unreadable-rules test it mirrors. It drives the real handler entry point (emitWorkflowCommand.handler via the suite's run() helper) with a missing plan path and asserts:

  • the handler throws /cannot read the plan/ (the named-path refusal, not a raw stack),
  • nothing was written: zero recorded prompts and no script file — the guard lands before rules reading, script-path resolution, and directory creation.

Mutation probe (witness check): temporarily removed the try/catch guard from runEmitWorkflow → the focused suite failed with exactly 1 failed | 29 passed, the failing test being only the new one; restored the guard from HEAD → 30 passed (30). The test genuinely witnesses the guard.

Not in scope this round

  • The round-2/round-3 deferred items (meta-purity oracle regex at workflow-script.test.ts:119-126; key-mismatch guard in buildFanOutRoster) were recorded as non-blocking in their own rounds and are not re-raised in this round's feedback — untouched.
  • The macOS/Windows test legs skipped in CI are disclosed review gaps (the round-4 review's "Not reviewed" notes), not action requests; the ubuntu leg is green.
  • No inline comments arrived this round, so there are no resolved-comments.txt entries and no open-thread replies.

Verification

  • npx vitest run src/commands/review/emit-workflow.test.ts (in packages/cli) — 30 passed (30)
  • Mutation probe: guard removed → 1 failed | 29 passed (only the new test); guard restored → 30 passed (30)
  • npm run buildpassed (exit 0)
  • npm run typecheckpassed (exit 0, all workspaces + integration)
  • npm run lintpassed (exit 0)
  • npx prettier --check packages/cli/src/commands/review/emit-workflow.test.tspassed
  • Integration tests: not needed — the pinned behavior is fully exercised by the unit suite, not only through the bundled CLI or integration harness.
  • Settings schema: not touched, no regeneration needed.

Commit: 74abe76903 test(review): pin the emit-workflow cannot-read-the-plan guard (#10119) (1 file changed, +9 lines).

中文说明

Autofix 审查处理轮次 — PR #10119

本轮分类的反馈: 1 条(第 4 轮审查 rv:5034531001)。无行内评论、无失败检查、无持续失败的检查、无基线冲突(--conflict false)。

发现 1 — cannot-read-the-plan 处理器测试缺口(emit-workflow.test.ts)— 已实现

来源:第 4 轮审查确认该建议级发现已在第 2 轮的延后列表中报告过(审查 5031737007):“没有测试钉住该处理器的 cannot-read-the-plan 守卫,而所有兄弟命令都钉住了”

先行核实: runEmitWorkflow 将计划读取包在 try/catch 中,抛出 emit-workflow: cannot read the plan <path>: …packages/cli/src/commands/review/emit-workflow.ts:189-195)。每个兄弟 review 子命令都在处理器层级钉住了各自的等价守卫 —— agent-prompt.test.ts:409(“names the plan it could not read, instead of a raw stack”)、build-test.test.ts:2377base-tree.test.ts:293 —— 但 emit-workflow.test.ts 只钉住了不可读 rules 的守卫。缺口属实。

变更(仅测试,+9 行):emit-workflow — where it writes 套件中、紧邻其所镜像的不可读-rules 测试处,新增一个处理器层级测试 refuses an unreadable plan path before writing anything。它通过真实的处理器入口(经由该套件的 run() 辅助函数调用 emitWorkflowCommand.handler),传入一个不存在的计划路径,并断言:

  • 处理器抛出 /cannot read the plan/(指名路径的拒绝信息,而非原始堆栈),
  • 没有写入任何内容:零条已记录的 prompt,也没有脚本文件 —— 该守卫先于 rules 读取、脚本路径解析和目录创建而生效。

变异探测(见证检查): 临时移除 runEmitWorkflow 中的 try/catch 守卫 → 定向测试套件恰好以 1 failed | 29 passed 失败,失败的正是新增测试;从 HEAD 恢复守卫 → 30 passed (30)。该测试确实见证了守卫。

本轮不在范围内

  • 第 2/第 3 轮延后项(workflow-script.test.ts:119-126 处的 meta 纯度 oracle 正则;buildFanOutRoster 中的键不匹配守卫)在其所属轮次中被记录为非阻断项,且本轮反馈未重新提出 —— 未改动。
  • CI 中跳过的 macOS/Windows 测试分支是已披露的审查缺口(第 4 轮审查的 “Not reviewed” 说明),不是行动请求;ubuntu 分支为绿色。
  • 本轮没有收到行内评论,因此没有 resolved-comments.txt 条目,也没有待回复的开放线程。

验证

  • npx vitest run src/commands/review/emit-workflow.test.ts(在 packages/cli 中)— 30 passed (30)
  • 变异探测:移除守卫 → 1 failed | 29 passed(仅新增测试失败);恢复守卫 → 30 passed (30)
  • npm run build通过(exit 0)
  • npm run typecheck通过(exit 0,全部 workspace + integration)
  • npm run lint通过(exit 0)
  • npx prettier --check packages/cli/src/commands/review/emit-workflow.test.ts通过
  • 集成测试:不需要 —— 被钉住的行为完全由单元测试套件执行,并非仅通过打包后的 CLI 或集成测试框架执行。
  • 设置 schema:未改动,无需重新生成。

提交:74abe76903 test(review): pin the emit-workflow cannot-read-the-plan guard (#10119)(1 个文件变更,+9 行)。

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

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@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 — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): "agent 6a": executing emit-workflow.test.ts under vitest — the worktree and parent checkout have no node_modules and no built packages/core/dist , so a run requires a ….

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

  • packages/cli/src/commands/review/emit-workflow.test.ts:688 — [probe] no test pins the clean-worktree no-residue-warning branch
  • packages/cli/src/commands/review/emit-workflow.test.ts:323 — [probe] the dispatch-guidance stdout line is unpinned
中文说明

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

未审查:build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未审查:build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):"agent 6a"executing emit-workflow.test.ts under vitest — the worktree and parent checkout have no node_modules and no built packages/core/dist , so a run requires a …

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

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

…nce (QwenLM#10119)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
One conflict, in review/lib/paths.ts: both sides grew the import
block — main added `Storage` for QwenLM#9883's lastReviewEffortPath, this
branch added the fs/path/crypto imports for the generated-workflow
dir helpers. Resolution is the union: one combined core import, both
sides' functions kept untouched.

client.telemetrySwap.test.ts resolved itself: this branch's
48a01fa and main's 6dfb5a1 (QwenLM#10252) are byte-identical fixes
for the same TS1117 duplicate getToolRegistry.

Verified: npm run build, npm run typecheck, npm run lint all clean;
packages/cli src/commands/review 105 files / 5249 passed; packages/core
client.telemetrySwap.test.ts 10 passed.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round summary — PR #10119 (address-review)

Scope of this round

This round was invoked with --conflict true: its work was merging origin/main
into the PR branch and resolving the conflicts. Critical-only mode is active,
and the actionable sections carried no Critical findings: no reviews, no
inline comments, no failed or still-red checks. The two issue-level comments are
informational (dispositions below), and the deferred non-Critical automated
review is an audit record only — no code was changed for it, no thread was
touched.

Conflict resolution

Merged origin/main (a3ec41a281, 27 commits since the branch's last main
merge at 8beaa8d9e3). Exactly one conflict; everything else auto-merged.

packages/cli/src/commands/review/lib/paths.ts — resolved as the union of
both sides.
Both sides grew the same import block for unrelated reasons:
main added import { Storage } for #9883's lastReviewEffortPath, this
branch added the node:crypto/node:fs/node:path imports plus
sanitizeFilenameComponent for the generated-workflow directory helpers.
The resolution combines both into one @qwen-code/qwen-code-core import and
keeps every function from both sides untouched. Audited with a two-way diff:
vs our pre-merge HEAD the file gains exactly main's additions (header comment,
Storage, lastReviewEffortPath, the probeWorktreePath doc simplification);
vs origin/main it gains exactly this branch's generated-workflow section.
Nothing dropped, nothing invented.

packages/core/src/core/client.telemetrySwap.test.ts — auto-resolved.
This branch's 48a01faf2b and main's 6dfb5a1fea (#10252) are byte-identical
fixes (same source and target blobs) for the same TS1117 duplicate
getToolRegistry, so git applied the single change once. The file now carries
exactly one getToolRegistry property.

Feedback dispositions

  • [ic:5435323137] (@qqqys) — no action requested, confirmed by the merge.
    The comment documents where the TS1117 breakage came from (two independent
    main commits) and what 48a01faf2b did about it. Main has since landed the
    same removal as fix(core): remove duplicate getToolRegistry in telemetry-swap test mock #10252; this merge proves the two fixes byte-identical, so
    the branch and main now agree and the "main needs the same removal" flag is
    moot.
  • [ic:5438379701] (@wenshao) — no action requested. Positive
    re-verification at 6962311: §B, §C and both nits confirmed fixed, mutation
    matrix 16/16 caught, nothing regressed, "nothing here blocks merge". §A is
    explicitly accepted as a design question about the runtime's caps to settle
    before routing lands, not a defect of this PR. The environmental note about
    a vite-plugin-dts cold-run flake in packages/webui is noted as
    watch-only — this round's own npm run build ran clean, and the comment
    itself says it is not something to chase here.
  • Deferred non-Critical feedback (automated reviewer review) — excluded by
    Critical-only mode; treated as an audit record per the workflow rules. No
    code changes, no thread resolutions, no replies.

Mutation probes

Not applicable: a merge commit authored no new guard or branch. The only
hand-written content in the resolution is the combined import statement, which
has no behavior of its own; its witnesses are the compile steps (a missing or
mis-named import fails build/typecheck) and the full review suite that
imports paths.ts (5249 tests below).

Verification

  • npm run build — passed (exit 0)
  • npm run typecheck — passed (exit 0)
  • npm run lint — passed (exit 0)
  • vitest run src/commands/review (packages/cli, contains the resolved
    paths.ts and both sides' tests for it) — 105 files passed, 5249 passed /
    5 skipped
  • vitest run src/core/client.telemetrySwap.test.ts (packages/core) — 10
    passed
  • npm run generate:settings-schema — ran clean; regenerated artifact
    identical to the committed one (no drift introduced by the merge)
  • Merge committed as 4b3c95bccd (parents 69623115f9 + a3ec41a281);
    pre-commit hook passed; working tree clean.
中文说明

Autofix 轮次总结 — PR #10119(address-review)

本轮范围

本轮以 --conflict true 调用:工作内容是将 origin/main 合入 PR 分支并解决冲突。当前处于仅处理 Critical 的模式,可执行区域中没有任何 Critical 发现:没有 review、没有行内评论、没有失败或持续失败的检查。两条 issue 级评论均为信息性内容(处置见下),被延后的非 Critical 自动审查仅作为审计记录 —— 没有为它改动任何代码,也没有触碰任何讨论串。

冲突解决

已合并 origin/maina3ec41a281,自分支上次在 8beaa8d9e3 合并 main 以来的 27 个提交)。恰好只有一个冲突,其余全部自动合并。

packages/cli/src/commands/review/lib/paths.ts —— 按双方的并集解决。 双方出于互不相关的原因扩充了同一段 import 块:main 为 #9883lastReviewEffortPath 添加了 import { Storage },本分支为生成式 workflow 目录辅助函数添加了 node:crypto/node:fs/node:path 的导入以及 sanitizeFilenameComponent。解决方案把两者合并为一条 @qwen-code/qwen-code-core 导入,并原样保留双方的全部函数。已用双向 diff 审计:相对合并前的本分支 HEAD,该文件恰好只多出 main 的新增内容(头部注释、StoragelastReviewEffortPathprobeWorktreePath 的文档简化);相对 origin/main,恰好只多出本分支的生成式 workflow 部分。没有丢弃任何内容,也没有凭空加入任何内容。

packages/core/src/core/client.telemetrySwap.test.ts —— 自动合并。 本分支的 48a01faf2b 与 main 的 6dfb5a1fea#10252)是针对同一个 TS1117 重复 getToolRegistry 的逐字节相同修复(源、目标 blob 均相同),因此 git 将该改动只应用了一次。文件现在恰好只保留一个 getToolRegistry 属性。

反馈处置

  • [ic:5435323137](@qqqys)—— 未要求任何改动,且已被本次合并证实。 该评论说明了 TS1117 破坏的来源(main 上两个互不知情的提交)以及 48a01faf2b 的处理方式。main 随后已以 fix(core): remove duplicate getToolRegistry in telemetry-swap test mock #10252 落地了同样的删除;本次合并证明两个修复逐字节相同,分支与 main 现已一致,"main 也需要同样的删除"这一提醒已不再成立。
  • [ic:5438379701](@wenshao)—— 未要求任何改动。6962311 上的正面重新验证:§B、§C 与两个小问题均确认修复,变异矩阵 16/16 全部被抓,无回归,"这里没有任何阻塞合并的内容"。§A 被明确接受为关于 runtime 上限的设计问题,留待路由落地前定夺,而非本 PR 的缺陷。关于 packages/webuivite-plugin-dts 冷启动抖动的环境备注已记录为仅观察 —— 本轮自己的 npm run build 运行干净,且该评论本身也说明不必在此追查。
  • 延后的非 Critical 反馈(自动审查者的 review) —— 被仅处理 Critical 的模式排除;按工作流规则仅作为审计记录对待。没有改动代码,没有解决讨论串,没有回复。

变异探针

不适用:合并提交没有引入任何新的守卫或分支。解决方案中唯一手写的内容是合并后的 import 语句,它本身没有任何行为;其见证是编译步骤(缺失或写错的导入会让 build/typecheck 失败)以及导入 paths.ts 的完整 review 测试套件(下方 5249 个测试)。

验证

  • npm run build —— 通过(exit 0)
  • npm run typecheck —— 通过(exit 0)
  • npm run lint —— 通过(exit 0)
  • vitest run src/commands/review(packages/cli,包含已解决的 paths.ts 及双方针对它的测试)—— 105 个文件全部通过,5249 通过 / 5 跳过
  • vitest run src/core/client.telemetrySwap.test.ts(packages/core)—— 10 通过
  • npm run generate:settings-schema —— 运行干净;重新生成的产物与已提交版本一致(合并没有引入漂移)
  • 合并提交为 4b3c95bccd(父提交 69623115f9 + a3ec41a281);pre-commit 钩子通过;工作区干净。

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

Base-conflict check · 基分支冲突检查: conflicted with main — resolved in this push. · 与 main 有冲突——已在本次推送中解决。

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

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

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

  • R8-1 post-mkdirSync containment-guard missing witness — already recorded in round 7's deferred list (review 5039876946)
  • R8-2 null plan raw TypeError — already recorded in round 7's deferred list (review 5039876946)
  • R8-3 over-cap residue-warning branch unwitnessed — already recorded in round 7's deferred list (review 5039876946)
  • R8-4 meta-purity regex oracle — already recorded in round 2's deferred list (review 5031737007) and re-recorded in round 3 (review 5033638522)

Not reviewed: build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): chunk 2: executed packages/cli/src/commands/review/emit-workflow.test.ts to confirm it passes — the review worktree has no node_modules and a full monorepo install +….

Test Plan (not a blocker): 43 passed — this review observed 25392 passed; 119 passed — this review observed 25392 passed.

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

  • packages/cli/src/commands/review/emit-workflow.test.ts:149 — [probe] byte-parity pin never drives the hand-launched path
  • packages/cli/src/commands/review/emit-workflow.test.ts:564 — [probe] mid-write failure branch of the cleanup has no witness
  • packages/cli/src/commands/review/lib/paths.ts:164 — [probe] guard-to-write TOCTOU redirects the script write
  • packages/cli/src/commands/review/workflow-script.test.ts:334 — [probe] dispatch-logic guard is a two-substring proxy
  • packages/cli/src/commands/review/emit-workflow.test.ts:474 — [probe] worktree-pin serialization boundary unwitnessed
中文说明

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

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

未审查:build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未审查:build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):chunk 2:executed packages/cli/src/commands/review/emit-workflow.test.ts to confirm it passes — the review worktree has no node_modules and a full monorepo install +…

Test Plan(非阻断):43 passed — this review observed 25392 passed; 119 passed — this review observed 25392 passed

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

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix address-review round — PR #10119 (same-run verification repair)

Feedback triage

No actionable feedback this round: the Reviews, Inline comments, and
Issue-level comments sections of feedback.md are empty, and the
Failed/Still-red checks sections are empty too. Critical-only mode is active;
everything listed sits in the Deferred non-Critical feedback audit section,
which is an audit record — not addressed, not modified, threads left as-is.

This round is a same-run verification repair: the previous commit (the
origin/main merge 9fe8574b2d) was rejected by deterministic verification
with tests failed in packages/cli. Per the repair rule the rejected commit
is preserved and this round adds one verified follow-up commit on top.

What was rejected

npm run test --workspace packages/cli -- --changed origin/main failed one
test:

FAIL src/commands/review/test-efficacy.test.ts > restoreProbeTreeTracked, through runOneMutant > refuses to run when the index hides a tracked file from the restore
Error: vitest not found searching up from /tmp/qwen-skipwt-5BS9NT
 ❯ findVitestBin src/commands/review/test-efficacy.ts:1361:13
 ❯ runProbeSuite src/commands/review/test-efficacy.ts:1771:5
 ❯ attempt src/commands/review/test-efficacy.ts:2360:27
 ❯ runOneMutant src/commands/review/test-efficacy.ts:2390:18

Root cause (reproduced, with probe evidence)

The stack means the skip-worktree refusal in restoreProbeTreeTracked did
NOT fire: the test set a skip-worktree bit, the guard read no bit, certified
the restore, and the mutant run then died in findVitestBin (the fixture
tmpdir has no vitest up-tree, by design).

Reproduction: launching the test file under a HOME whose .gitconfig
carries core.sparseCheckout = true reproduces the gate failure
byte-identically (same error, same stack, same line numbers). Mechanism,
measured with isolated fixture probes on this runner's git 2.39.5:

  1. sanitizedGitEnv() strips env redirects (GIT_DIR, GIT_CONFIG_*, …)
    but leaves the FILE config scopes reachable — the fixture's git calls fall
    back to $HOME/.gitconfig and /etc/gitconfig. A persistent pool's
    ambient core.sparseCheckout = true therefore reaches every fixture git
    call. (The pool is documented as accumulating exactly this class of state;
    the job-start sanitize sweeps only the exec-knob families, and
    core.sparseCheckout is not an exec knob.)
  2. Under an ACTIVE core.sparseCheckout flag, git 2.39.5 changes the very
    semantics the test pins — probes on fresh fixtures:
    • the bit IS written (git ls-files --debugflags: 4000), but
      git ls-files -v renders H a.ts instead of S a.ts; reading the same
      index with the flag off answers S a.ts;
    • git checkout --force HEAD -- . clears the bit (flags: 40000)
      and restores the tampered content, so the restore reads clean.
  3. Consequence in the gate: the guard's ls-files -v -z scan matched no
    /^[a-zS]/ record, restoreProbeTreeTracked returned null, runOneMutant
    proceeded, and findVitestBin threw — the exact rejection above.
  4. Plain re-runs pass because the ambient state is point-in-time runner
    state: $HOME/.gitconfig, $HOME/.config/git/config, and
    /etc/gitconfig are all absent NOW. The failure needs the ambient flag
    present at test time, which is why the deterministic fix is hermetic
    isolation, not chasing the state.

Changes made (follow-up commit 42b79e236f; rejected merge preserved)

packages/cli/src/commands/review/test-efficacy.test.ts only (+18/-1):

  • File-wide hermetic git-config isolation via the repository's existing
    isolateHostGitConfig() (beforeEach/afterEach) — the same discipline the
    sibling integration suites (local-anchor.integration.test.ts) already
    use. It redirects HOME to a throwaway home with an empty .gitconfig
    and sets GIT_CONFIG_NOSYSTEM=1; both survive sanitizedGitEnv() (which
    strips GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM but not HOME/
    GIT_CONFIG_NOSYSTEM), so every fixture git call in this file now reads
    no host config through either the env layer or the file layer.

No production code changed. With isolation active the skip-worktree test
reaches the refusal deterministically on any host — including one carrying
the ambient core.sparseCheckout that broke the gate run.

Witness / mutation probe

  • Pre-fix repro: polluted HOME (only core.sparseCheckout = true) →
    the exact gate failure (FAIL).
  • Post-fix, same polluted HOME → 132/132 tests in the file pass.
  • Mutation probe: negating the isolation hook (setup disposes immediately) →
    polluted HOME → the gate failure returns (1 failed); restoring the hook →
    green. The isolation is the load-bearing change.

Residual environment-specific note (not merge-induced, not addressed here)

A whole-suite run on THIS runner still shows the 3
local-anchor.integration.test.ts sparse-checkout failures: this runner's
git 2.39.5 has no git sparse-checkout check-rules, and the production code
deliberately fails closed without it. That test file (and the code under
test) is byte-identical to origin/main — pre-existing on base in this
environment, documented in the previous round, and green on GitHub-hosted CI
(newer git). Note it only entered this PR's --changed origin/main set
through the merge (it landed in main after every earlier gate ran). Skipping
or guarding main's own tests on older gits is a maintainer call, not repair
work.

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed (also npx eslint on the changed file and
    npx prettier --check on it — both clean)
  • npx vitest run src/commands/review/test-efficacy.test.ts (in
    packages/cli) — 132/132 passed (clean environment)
  • same command under a polluted HOME carrying core.sparseCheckout = true
    — 132/132 passed (hermeticism witness; reproduces the gate failure pre-fix)
  • mutation probe: isolation negated → the gate failure returns under the
    polluted HOME; restored → green
  • npx vitest run src/commands/review (in packages/cli) — 113/114 files
    passed, 5398 tests passed, 5 skipped, 3 failed — the 3 are the
    environment-specific local-anchor.integration.test.ts failures documented
    above (identical result to the previous round, pre-existing on base)
  • No settings source changed, so npm run generate:settings-schema was not
    required. No integration tests run: the change is a unit-suite hermeticism
    hook fully covered by the focused runs above.
中文说明

Autofix address-review 轮次 — PR #10119(same-run 验证修复)

反馈分类

本轮没有可处理的反馈:feedback.md 中的 Reviews(评审)、Inline comments(行内评论)、Issue-level comments(议题级评论)部分均为空,Failed/Still-red checks(失败/持续失败的检查)部分也为空。当前处于 Critical-only(仅处理 Critical)模式;所有列出的内容都位于 Deferred non-Critical feedback(已延后的非 Critical 反馈)审计区,该区是审计记录——未处理、未修改,相关讨论串保持原样。

本轮是一次 same-run verification repair(同轮验证修复):上一个提交(origin/main 合并 9fe8574b2d)被确定性验证以 tests failed in packages/cli(packages/cli 测试失败)为由拒绝。按照修复规则,被拒绝的提交予以保留,本轮在其之上追加一个经过验证的后续提交。

被拒绝的内容

npm run test --workspace packages/cli -- --changed origin/main 有 1 个测试失败:

FAIL src/commands/review/test-efficacy.test.ts > restoreProbeTreeTracked, through runOneMutant > refuses to run when the index hides a tracked file from the restore
Error: vitest not found searching up from /tmp/qwen-skipwt-5BS9NT
 ❯ findVitestBin src/commands/review/test-efficacy.ts:1361:13
 ❯ runProbeSuite src/commands/review/test-efficacy.ts:1771:5
 ❯ attempt src/commands/review/test-efficacy.ts:2360:27
 ❯ runOneMutant src/commands/review/test-efficacy.ts:2390:18

根因(已复现,附探测证据)

该堆栈意味着 restoreProbeTreeTracked 中的 skip-worktree 拒绝没有触发:测试设置了 skip-worktree 位,守卫却没有读到任何位,于是为 restore 出具了"干净"证明,随后 mutant 运行死于 findVitestBin(按设计,夹具临时目录向上找不到 vitest)。

复现:在 .gitconfig 带有 core.sparseCheckout = trueHOME 下启动该测试文件,可以逐字节复现门处的失败(相同错误、相同堆栈、相同行号)。机制已在本 runner 的 git 2.39.5 上用隔离夹具探测实测:

  1. sanitizedGitEnv() 会剥掉环境变量重定向(GIT_DIRGIT_CONFIG_* 等),但文件配置作用域仍然可达——夹具的 git 调用会回退到 $HOME/.gitconfig/etc/gitconfig。因此持久 runner 池中环境残留的 core.sparseCheckout = true 能到达每一个夹具 git 调用。(该池被明确记载会累积这类状态;作业起始的清理只扫除 exec 旋钮族,而 core.sparseCheckout 不是 exec 旋钮。)
  2. 生效的 core.sparseCheckout 标志下,git 2.39.5 改变了测试所钉住的语义本身——在新建夹具上的探测:
    • 确实被写入git ls-files --debugflags: 4000),但 git ls-files -v 渲染为 H a.ts 而非 S a.ts;关闭标志后再读同一个索引,答案为 S a.ts
    • git checkout --force HEAD -- . 会清掉该位(flags: 40000)并恢复被篡改的内容,于是 restore 读起来是干净的。
  3. 门处运行时的后果:守卫的 ls-files -v -z 扫描没有任何记录匹配 /^[a-zS]/restoreProbeTreeTracked 返回 null,runOneMutant 继续执行,findVitestBin 抛错——正是上面被拒绝的形态。
  4. 直接重跑之所以通过,是因为该环境状态是时点性的 runner 状态:$HOME/.gitconfig$HOME/.config/git/config/etc/gitconfig 现在不存在。失败需要测试时刻恰好存在该环境标志,这正是确定性修复选择密封隔离、而不是去追那份状态的原因。

所做更改(后续提交 42b79e236f;被拒绝的合并已保留)

仅改动 packages/cli/src/commands/review/test-efficacy.test.ts(+18/-1):

  • 全文件的密封 git 配置隔离,使用仓库现成的 isolateHostGitConfig()(beforeEach/afterEach)——与兄弟集成套件(local-anchor.integration.test.ts)已在使用的纪律相同。它把 HOME 重定向到一个带空 .gitconfig 的一次性 home,并设置 GIT_CONFIG_NOSYSTEM=1;两者都能穿过 sanitizedGitEnv() 存活(后者剥掉 GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM,但不剥 HOME/GIT_CONFIG_NOSYSTEM),因此本文件中每一个夹具 git 调用,无论经由环境变量层还是文件层,都不再读取任何宿主配置。

未改动生产代码。隔离生效后,skip-worktree 测试在任何主机上——包括带着曾破坏门处运行的环境 core.sparseCheckout 的主机——都能确定性地到达拒绝分支。

见证 / 突变探针

  • 修复前复现:污染 HOME(仅含 core.sparseCheckout = true)→ 与门处完全一致的失败(FAIL)。
  • 修复后,同一污染 HOME → 该文件 132/132 个测试全部通过。
  • 突变探针:把隔离钩子取反(setup 时立即 dispose)→ 污染 HOME → 门处失败重现(1 个失败);恢复钩子 → 转绿。隔离是承重的改动。

环境相关的遗留说明(非合并引入,此处不处理)

在本 runner 上跑全套测试,仍会出现 local-anchor.integration.test.ts 的 3 个 sparse-checkout 失败:本 runner 的 git 2.39.5 没有 git sparse-checkout check-rules,而生产代码在没有它时故意采用 fail closed(封闭失败)。该测试文件(及其被测代码)与 origin/main 逐字节相同——在此环境的 base 分支上即已存在,上一轮已有记录,且在 GitHub 托管 CI(更新的 git)上为绿色。注意:它只是在合并之后才进入本 PR 的 --changed origin/main 集合(它是在此前所有门运行之后才进入 main 的)。在旧版 git 上跳过或为主分支自带测试添加守卫,属于维护者的决策,不属于修复工作。

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过(另对改动文件执行了 npx eslintnpx prettier --check——均干净)
  • npx vitest run src/commands/review/test-efficacy.test.ts(在 packages/cli 中执行)— 132/132 通过(干净环境)
  • 在携带 core.sparseCheckout = true 的污染 HOME 下执行同一命令 — 132/132 通过(密封性见证;修复前该条件复现门处失败)
  • 突变探针:取反隔离 → 污染 HOME 下门处失败重现;恢复 → 转绿
  • npx vitest run src/commands/review(在 packages/cli 中执行)— 114 个文件中 113 个通过,5398 个测试通过,5 个跳过,3 个失败——这 3 个即上文记录的环境相关 local-anchor.integration.test.ts 失败(与上一轮结果完全一致,base 上已存在)
  • settings 源未改动,因此无需执行 npm run generate:settings-schema。未运行集成测试:该改动是一个单元套件密封钩子,上述聚焦运行已完整覆盖。

🧭 Gate advisory — this round modified areas outside the PR footprint (machine-measured, not agent-authored):

  • packages/core
    Review the expansion deliberately; the footprint gate is in advisory mode. · 本轮改动了 PR 足迹之外的区域(门自动测量,非 agent 文本),当前足迹门为 advisory 模式,请有意识地审阅该扩张。

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

Base-conflict check · 基分支冲突检查: conflicted with main — resolved in this push. · 与 main 有冲突——已在本次推送中解决。

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review did not complete successfully. The review pipeline failed before a review could be posted. A transient error is retried automatically; if you are seeing this, retry with @qwen-code /review. See workflow logs.

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix review round: no action needed (PR #10119)

This round had no actionable feedback:

  • Critical-only mode is active — 5 change-producing rounds are complete in this counting window, so only Critical findings, Request-changes reviews, failed checks, and base-conflict resolution are in scope.
  • All actionable sections were empty: no reviews, no inline comments, no issue-level comments, no failed checks, and no still-red checks.
  • The only pending feedback is non-Critical (a bot PR comment listed in the deferred section) and remains open for human follow-up per the Critical-only rules.
  • --conflict false: no base-conflict resolution was requested, so no merge of origin/main was performed.
  • Diff growth this window (source 117 / test 340 lines) is within budget (400/400), and no growth audit was requested.

No code changes, no commits, and no thread resolutions were made this round.

中文说明

Autofix 审查轮次:无需处理(PR #10119

本轮没有可处理的反馈:

  • 已进入仅处理 Critical 的模式 —— 本计数窗口已完成 5 个产生改动的轮次,因此本轮仅处理 Critical 发现、Request changes 审查、失败的检查以及 base 分支冲突解决。
  • 所有可处理区块均为:没有审查意见、没有行内评论、没有 issue 级评论、没有失败的检查,也没有持续失败的检查。
  • 唯一未处理的反馈为非 Critical 内容(一条列在延后区块中的机器人 PR 评论),按 Critical-only 规则保持开放,留待人工跟进。
  • --conflict false:未要求解决 base 分支冲突,因此未执行 origin/main 的合并。
  • 本窗口的 diff 增长(源码 117 行 / 测试 340 行)在预算(400/400)之内,且未要求进行增长审计。

本轮未做任何代码改动、未提交任何 commit,也未解决任何评审线程。

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qqqys

qqqys commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /review

@github-actions

Copy link
Copy Markdown
Contributor

Qwen Code review request accepted. Review is queued in workflow run.

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

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

  • over-cap residue-warning branch unwitnessed (emit-workflow.ts) — already recorded in round 7's deferred list (review 5039876946), re-confirmed in round 8 (R8-3)
  • null plan escapes the refusal contract with a bare TypeError — already recorded in round 7's deferred list (review 5039876946), re-confirmed in round 8 (R8-2)
  • unmeasured-residue brief seam unpinned (emit-workflow.test.ts) — already recorded in round 7's deferred list (review 5039876946)
  • meta-purity oracle not pinning the runtime meta contract fields (workflow-script.test.ts) — already recorded in round 2's deferred list (review 5031737007), re-recorded in round 3 (review 5033638522), re-confirmed in round 8 (R8-4)
  • mid-write failure branch of the cleanup has no witness (emit-workflow.test.ts) — already recorded in round 8's deferred list (review 5043515382)
  • stdout not pinned as a closed two-line set (emit-workflow.test.ts) — already recorded in round 6's deferred list (review 5036577458)
  • silence test discards the roster build return (emit-workflow.test.ts) — already recorded in round 6's deferred list (review 5036577458)
  • restore → re-run residue transition unpinned (emit-workflow.test.ts) — already recorded in round 7's deferred list (review 5039876946)
  • meta-purity regex weaker than the runtime parser (workflow-script.test.ts) — already recorded in round 2's deferred list (review 5031737007)
  • containment guard has no red-going test (lib/paths.ts) — already recorded in round 7's deferred list (review 5039876946), re-confirmed in round 8 (R8-1)

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

Not reviewed: build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): chunk 2: executing npx vitest run src/commands/review/emit-workflow.test.ts in packages/cli — the shared review worktree has no node_modules and no built packages….

Test Plan (not a blocker): 43 passed — this review observed 25551, 22008, 1794, 1667, 605, 4333, 639 passed; 119 passed — this review observed 25551, 22008, 1794, 1667, 605, 4333, 639 passed.

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

  • packages/cli/src/commands/review/emit-workflow.ts:255 — [probe] no-worktree → WORKING_DIR=null handler wiring unpinned
  • packages/cli/src/commands/review/emit-workflow.test.ts:765 — [probe] dirty-tree warning never asserts the untracked probe path
  • packages/cli/src/commands/review/emit-workflow.test.ts:716 — [probe] fetchedSha identity handover never parity-checked
  • packages/cli/src/commands/review/workflow-script.test.ts:228 — [probe] no-worktree shape test never pins isolation absence
  • packages/cli/src/commands/review/emit-workflow.test.ts:240 — [probe] refusal tests witness 'no record' but never 'no brief'
  • packages/cli/src/commands/review/workflow-script.test.ts:64 — [probe] harness stub omits the runtime's stallMs value gate
中文说明

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

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

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

未审查:build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未审查:build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):chunk 2:executing npx vitest run src/commands/review/emit-workflow.test.ts in packages/cli — the shared review worktree has no node_modules and no built packages…

Test Plan(非阻断):43 passed — this review observed 25551, 22008, 1794, 1667, 605, 4333, 639 passed; 119 passed — this review observed 25551, 22008, 1794, 1667, 605, 4333, 639 passed

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

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

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

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

  • R9-10 meta-purity oracle weaker than the runtime acceptance contract — already reported in round 2's deferred list (review 5031737007) and re-recorded in round 3 (review 5033638522)
  • R9-17 sandbox-analogue option-gate drift (value gates) — already reported (comment 3862296240) and ruled 'Standing (acknowledged design trade-off)' in round 3 (review 5032669862)

Not reviewed: build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not explored to full depth (tool budget reached): chunk 3: executed packages/cli/src/commands/review/emit-workflow.test.ts to confirm the residue-parity describe block passes — the review worktree has no root node_mo….

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

Test Plan (not a blocker): 43 passed — this review observed 25560, 22087, 1794, 1667, 605, 4333, 639 passed; 119 passed — this review observed 25560, 22087, 1794, 1667, 605, 4333, 639 passed.

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

  • packages/cli/src/commands/review/emit-workflow.ts:148 — [probe] over-cap residue-warning branch unwitnessed
  • packages/cli/src/commands/review/emit-workflow.test.ts:778 — [probe] unmeasured-residue brief seam unpinned
  • packages/cli/src/commands/review/emit-workflow.test.ts:623 — [probe] rules-refusal test misses the no-directory pin
  • packages/cli/src/commands/review/agent-prompt.ts:1355 — [review] residue warning duplicated instead of shared
  • packages/cli/src/commands/review/emit-workflow.test.ts:396 — [review] seam assertion weaker than the loader's containment rule
  • packages/cli/src/commands/review/emit-workflow.test.ts:608 — [review] plan-refusal test misses the no-directory pin
  • packages/cli/src/commands/review/emit-workflow.test.ts:765 — [review] dirty-warning enumeration pins only the tracked path
  • packages/cli/src/commands/review/emit-workflow.test.ts:803 — [probe] clean-residue brief shape unpinned
  • packages/cli/src/commands/review/emit-workflow.test.ts:786 — [probe] unmeasured-warning reason interpolation unpinned
  • packages/cli/src/commands/review/emit-workflow.test.ts:792 — [probe] warning conditions lack mutual-absence pins
  • packages/cli/src/commands/review/workflow-script.test.ts:230 — [probe] no-worktree branch missing isolation-absence pin
  • packages/cli/src/commands/review/emit-workflow.ts:158 — [probe] warning's restore-then-rebuild tail unpinned
  • packages/cli/src/commands/review/workflow-script.test.ts:282 — [probe] delivered-text verbatimness unpinned
中文说明

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

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

未审查:build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未审查:build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未探索到全部深度(达到工具调用预算):chunk 3:executed packages/cli/src/commands/review/emit-workflow.test.ts to confirm the residue-parity describe block passes — the review worktree has no root node_mo…

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

Test Plan(非阻断):43 passed — this review observed 25560, 22087, 1794, 1667, 605, 4333, 639 passed; 119 passed — this review observed 25560, 22087, 1794, 1667, 605, 4333, 639 passed

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

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round result: no action (PR #10119)

Critical-only mode is active for this window (5 change-producing rounds are complete), and every actionable feedback section for this round is empty:

  • Reviews: no new reviews since the last evaluation (2026-08-27T21:33:19Z).
  • Inline comments: none.
  • Issue-level comments: none.
  • Failed checks: none.
  • Still-red checks: none.

The two automated-reviewer reviews listed under Deferred non-Critical feedback were excluded by the workflow's deterministic brake; they are an audit record, not work for this round, and remain open for human follow-up. No code was modified and no thread was resolved for them.

No conflict with the base branch was reported for this round, so no merge was performed.

Result: no code changes, no commit, and no verification commands were needed since nothing changed. New [Critical] feedback, a Request changes review, or an @qwen-code /retry comment (which starts a fresh counting window) will re-enter the loop.

中文说明

Autofix 轮次结果:无操作(PR #10119

本窗口已进入 Critical-only(仅处理 Critical)模式(已完成 5 个产生改动的轮次),本轮所有可执行的反馈区域均为空:

  • Reviews(评审): 自上次评估(2026-08-27T21:33:19Z)以来没有新的评审。
  • Inline comments(行内评论): 无。
  • Issue-level comments(议题级评论): 无。
  • Failed checks(失败的检查): 无。
  • Still-red checks(持续失败的检查): 无。

列在 Deferred non-Critical feedback(已延后的非 Critical 反馈)中的两条自动化评审器评审已被工作流的确定性刹车机制排除在本轮之外;它们是审计记录,不是本轮的工作项,保持开放状态留待人工跟进。本轮未针对它们修改任何代码,也未解决任何相关线程。

本轮未报告与 base 分支存在冲突,因此未执行任何合并。

结果: 无代码改动、无提交;由于没有任何变更,也无需运行验证命令。新的 [Critical] 反馈、Request changes 评审、或评论 @qwen-code /retry(将开启新的计数窗口)即可重新进入自动修复循环。

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Round summary — PR #10119 (base-conflict resolution round)

Feedback triage

Critical-only mode is active (5 change-producing rounds complete). All actionable sections this round were empty: no reviews, no inline comments, no issue-level comments, no failed checks, no still-red checks. The deferred non-Critical section (maintainer feedback-budget notice for @wenshao) is an audit record and was not acted on, per the workflow rules.

The only work requested this round was --conflict true: merge origin/main and resolve the conflict with the PR branch.

Changes

Merged origin/main (22 commits ahead, including the Gemini→Llm rename refactor #10124, the Step-4 execution-grade review verification #9740, and new review subcommands ab-drive / revert-hunk).

One conflict, in packages/cli/src/commands/review.ts — both sides added review subcommands:

  • Ours: emitWorkflowCommand (this PR), registered after agent-prompt.
  • Theirs: abDriveCommand after drive, and revertHunkCommand after script-lint.

Resolution: keep both sides. Imports and .command() registrations auto-merged cleanly; the only conflicting hunk was the demandCommand help string, resolved as the union of both enumerations in registration order (... agent-prompt, emit-workflow, build-test, ..., drive, ab-drive, ..., script-lint, revert-hunk, ...). review.test.ts auto-merged with main's ab-drive / revert-hunk entries added next to our emit-workflow entry, matching the merged registration order exactly.

Committed as merge c88b60bd89 (parents: 42b79e236f, 464b77782f).

Post-merge sanity checks performed:

  • The merged help-string order matches the builder registration order, which review.test.ts pins with an exact-array assertion plus a "demandCommand message names every registered subcommand" check — so the resolution is witnessed by existing tests (no new guard was added this round; no mutation probe applies).
  • Grepped this branch's new files (emit-workflow.ts, workflow-script.ts, lib/budget.ts, lib/paths.ts, agent-prompt.ts, teamHelpers.test.ts) for stale identifiers from main's Gemini→Llm rename: none found.
  • Net diff of the merge result vs the pre-merge head is limited to main's incoming changes; nothing from this branch was lost.

Resolved / open threads

No inline comment findings existed this round, so resolved-comments.txt and comment-replies.json are omitted.

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • vitest packages/cli (touched: src/commands/review.test.ts + src/commands/review/) — 116 test files, 5524 passed, 17 skipped
  • vitest packages/core (touched: src/agents/team/teamHelpers.test.ts) — 49 passed
  • npm run generate:settings-schema — not needed (no settings source changed this round)
  • Integration tests — not needed (the conflict touched only review subcommand registration, covered by the unit tests above; running the CLI directly is not permitted under GitHub Actions rules)
中文说明

轮次总结 — PR #10119(基分支冲突解决轮)

反馈分类

当前处于仅处理 Critical 的模式(已完成 5 个产生改动的轮次)。本轮所有可执行区域均为空:无 review、无行内评论、无 issue 级评论、无失败检查、无持续失败的检查。延后的非 Critical 区域(@wenshao 的维护者反馈预算提示)属于审计记录,按工作流规则不作处理。

本轮唯一的工作是 --conflict true:合并 origin/main 并解决与 PR 分支的冲突。

变更

合并了 origin/main(领先 22 个提交,包括 Gemini→Llm 重命名重构 #10124、Step-4 执行级 review 校验 #9740,以及新的 review 子命令 ab-drive / revert-hunk)。

packages/cli/src/commands/review.ts 有一处冲突 —— 双方都添加了 review 子命令:

  • 我方:emitWorkflowCommand(本 PR),注册在 agent-prompt 之后。
  • 对方:abDriveCommanddrive 之后,revertHunkCommandscript-lint 之后。

解决方式:保留双方。import 与 .command() 注册均自动合并无误;唯一冲突块是 demandCommand 帮助字符串,按注册顺序取两侧枚举的并集解决(... agent-prompt, emit-workflow, build-test, ..., drive, ab-drive, ..., script-lint, revert-hunk, ...)。review.test.ts 自动合并后在 emit-workflow 条目旁加入了 main 的 ab-drive / revert-hunk 条目,与合并后的注册顺序完全一致。

以合并提交 c88b60bd89 提交(父提交:42b79e236f464b77782f)。

合并后的完整性检查:

  • 合并后帮助字符串的顺序与 builder 注册顺序一致,review.test.ts 通过精确数组断言以及「demandCommand 消息须列出每个已注册子命令」检查锁定了这一点 —— 因此该解决方案由现有测试见证(本轮未新增任何防护逻辑,不适用变异探针)。
  • 在本分支新增文件(emit-workflow.tsworkflow-script.tslib/budget.tslib/paths.tsagent-prompt.tsteamHelpers.test.ts)中检索 main 的 Gemini→Llm 重命名遗留标识符:未发现。
  • 合并结果相对合并前 HEAD 的净差异仅限 main 引入的变更;本分支内容无丢失。

已解决 / 未决讨论串

本轮不存在行内评论发现,因此省略 resolved-comments.txtcomment-replies.json

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • vitest packages/cli(本轮涉及:src/commands/review.test.ts + src/commands/review/)— 116 个测试文件、5524 个用例通过,17 个跳过
  • vitest packages/core(本轮涉及:src/agents/team/teamHelpers.test.ts)— 49 个用例通过
  • npm run generate:settings-schema — 不需要(本轮未改动 settings 源)
  • 集成测试 — 不需要(合并冲突仅涉及 review 子命令注册,已由上述单元测试覆盖;按 GitHub Actions 规则不允许直接运行 CLI)

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

  • @wenshao is at this window's regular-feedback budget — to continue: tag [Critical], submit a Request changes review, or comment @qwen-code /retry for a fresh window. / @wenshao 本窗口常规反馈预算已用完——继续请标 [Critical]、提交 Request changes、或评论 @qwen-code /retry 开新窗口。
中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

Base-conflict check · 基分支冲突检查: conflicted with main — resolved in this push. · 与 main 有冲突——已在本次推送中解决。

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

📊 Takeover milestone — round 10/100, in the current window. Census: 9 pushed fix(es), 4 no-change review(s), 0 timeout(s), 0 rejected attempt(s), 0 deliberate stop(s) under instruction (deferred to a human), 1 other round(s) (crash / model error / gate error / infra), 0 base update(s).

This many rounds deserves a human look. Options: keep going (fine — nothing changes), split or reduce the PR if rounds keep accumulating, or release takeover (remove the autofix/takeover label or comment @qwen-code /takeover stop). Management continues unchanged unless you act.

中文说明

📊 接管里程碑 —— 第 10/100 轮(当前窗口)。统计:推送修复 9 次、审阅无需改动 4 次、超时 0 次、验证拒绝 0 次、按指示有意停止(移交人工)0 次、其他轮次(崩溃/模型错误/门错误/infra)1 次、base 更新 0 次。

轮次到这个量值得人工看一眼。可选:继续(无需操作);若轮次持续累积,考虑拆分或缩减 PR;或释放接管(移除 autofix/takeover 标签或评论 @qwen-code /takeover stop)。不操作则托管照常继续。

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

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

  • R10-3 null-plan TypeError escape in fanOutBlocker (emit-workflow.ts:65) — already recorded in round 7's deferred list (review 5039876946) and as R8-2 in round 8 (review 5043515382)
  • R10-4 over-cap residue-warning branch untested (emit-workflow.ts:153) — already recorded in round 7's deferred list (review 5039876946) and as R8-3 in round 8 (review 5043515382)
  • R10-5 meta-purity oracle weaker than the runtime acceptance contract (workflow-script.test.ts:122) — already recorded in round 2's deferred list (review 5031737007), re-recorded in round 3 (review 5033638522), re-confirmed as R8-4 in round …
  • R10-8 dirty-warning enumeration pins only the tracked path (emit-workflow.test.ts:755) — already recorded in round 9's deferred list (review 5048149089)

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

Not reviewed: build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally.

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

  • packages/cli/src/commands/review/emit-workflow.test.ts:672 — [probe] residue fixture git calls inherit the ambient env the probe sanitizes away
  • packages/cli/src/commands/review/lib/paths.ts:148 — [probe] symlink at the workflows parent level bypasses the containment loop
  • packages/cli/src/commands/review/workflow-script.test.ts:146 — [probe] opts.phase is read by the runtime but pinned by no test
中文说明

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

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

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

未审查:build-and-test — "Test (macos-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

未审查:build-and-test — "Test (windows-latest, Node 22.x)" was skipped in CI and its suite did not run locally。

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

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Address-review round — PR #10119

No action was taken this round; no commit was made and the branch is unchanged.

Why

  • Critical-only mode is active (5 change-producing rounds complete in this window). The workflow's deterministic filter excluded all remaining non-Critical feedback into the Deferred non-Critical feedback section, where it stays open for human follow-up.
  • Every actionable section for this round is empty: no new reviews, no inline comments, no issue-level comments, no failed checks, and no still-red checks newer than the last evaluation (2026-08-28T05:24:12Z).
  • The deferred items — the automated reviewer's review (pullrequestreview-5052393027) and @wenshao's regular-feedback budget notice — are an audit record, not work for this round: per the workflow rules, no code was modified, no threads were resolved, and no comment replies were written for them.
  • No Growth audit required section is present: diff growth this window is within budget (source 117/400, test 342/400; 0 prior rounds over budget).
  • No merge was performed (--conflict false, no base conflict reported).
中文说明

评审反馈处理轮次 — PR #10119

本轮未采取任何操作;未创建提交,分支保持不变。

原因

  • 已进入仅处理 Critical 的模式(本窗口已完成 5 个产生改动的轮次)。工作流的确定性过滤器已将所有剩余的非 Critical 反馈移入 Deferred non-Critical feedback(延后的非 Critical 反馈)区域,这些条目保持开放,留待人工跟进。
  • 本轮所有可执行区域均为空:没有新的评审、没有行内评论、没有 issue 级评论、没有失败的检查,也没有比上次评估(2026-08-28T05:24:12Z)更新的持续红色检查。
  • 延后条目——自动评审器的评审(pullrequestreview-5052393027)与 @wenshao 的常规反馈预算提示——属于审计记录,不是本轮的工作:按照工作流规则,未修改任何代码、未解决任何讨论串、也未针对它们撰写任何评论回复。
  • 未出现 Growth audit required(需要增长审计)区域:本窗口的 diff 增长在预算之内(源码 117/400 行,测试 342/400 行;此前 0 轮超出预算)。
  • 未执行合并(--conflict false,未报告与基分支的冲突)。

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 28, 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: 132 passed · 0 failed · 132 total

Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence

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

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

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

抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence

Verification report

<!-- qwen-triage:verify -->
<!-- qwen-triage:verify-substantive -->

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - follow-up round at head c88b60bd

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

Scripted assertions: 132 passed · 0 failed · 132 total

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

第二轮验证(新 head c88b60bd,新 base d6533785)。132 条脚本化断言全部通过,0 失败。

  • 上轮遗留:M6 幸存者(key 不匹配守卫)已修复——新测试钉住该守卫,变异后死于预期断言;两条描述更正(node --check、测试计数)仍然成立,已在本文重测。
  • A/B(见下表)emit-workflow 在新 base 上不存在(按预期失败);head 的 --roster 与 base 逐字节一致;head 的 emit-workflow 所写记录/简报树与 base --roster 逐字节一致(15-agent roster)。
  • 本轮增量探针:全灭失败消息改为点名 dispatch 而非重跑 emit-workflow(不再循环);territory 拒绝措辞改为真实的 wall-clock/fail-closed 边界(不再声称"静默截断");被拒 plan 不再留下空会话目录(且有变异钉住);test-efficacy 夹具在敌对 git 环境下仍 132/132 全绿(密封性已验证,危害对照证明环境重定向真实存在)。
  • 变异矩阵:7/7 全部死于预期测试(含两个阳性对照);门禁:src/commands/review/ 全套 115 文件 5,561 通过 / 17 跳过,packages/cli typecheck 干净(活性已证明)。
  • 未覆盖:见 Not covered(路由/真实模型分派不存在、仅 Linux、逐提交验证受 depth-2 限制、TOCTOU 未驱动等)。
Verification report

PR #10119 deep verification — follow-up round — feat(review): emit the Step 3A fan-out as a generated workflow script

Verdict: merge-ready — 132 scripted assertions executed, 132 pass / 0 fail. Verified head: c88b60bd89d2007c27e290823e275025ec476a41 (git rev-parse HEAD^2); control base: d6533785bdb6d5057470632f2e74866ae71215ef (HEAD^1, the merge parent; the snapshot's baseRefOid 464b7778… drifted from it, as in round 1 — the control used the base the PR would actually land on). Previous round (head 891b6ac3) was merge-ready 120/120; this round re-measures every carried-forward measurement at the new head and probes the delta since that round.

Previous-finding status (round 1 → new head)

# Round-1 item Severity Status at c88b60bd
M6 Mutation survivor: key-mismatch guard in buildFanOutRoster — redundant defence, statically unreachable completeness fixedd9bd123c added 'refuses a roster key the builder did not build under' (delegation mock stands buildLaunch in with a wrong key). My M4 mutant (guard deleted) now fails on the intended assertion (expected [Function] to throw an error, quoted in logs/04-M4-no-key-mismatch-guard.txt). The guard remains statically unreachable in production (both sides derive the key from the same (role, file) pair) — it is now a pinned canary, which is the right treatment. I agree with the fix.
C1 Description claims the emitted script "parses (node --check)" correction stands — re-measured at the new head: cjs goal fails SyntaxError: Unexpected token 'export', module goal fails SyntaxError: Illegal return statement (S11 cells; the round-1 probe saw the module error on both goals because it parsed the file under the repo-root type: module — the parse goal is set by the nearest package.json, which is exactly why the test-plan step is wrong). The real gate, compileWorkflowScript, compiles the file (S3).
C2 Description test counts are a pre-final-commit snapshot (43 = 23+15+5; 104 files / 5,119 passed / 15 skipped) correction stands — re-measured: the three named files pass 54 (34 emitter + 15 script + 5 registry) and the full src/commands/review/ directory is 115 files / 5,561 passed / 17 skipped at the new head (the width grew with main's merges; the 54 vs 43 delta is this PR's own commits). Everything green; only the text is stale.
TOCTOU between ensureWritableReviewWorkflowsDir and the rename (not covered, round 1) not covered stands as not covered (loader's realpath containment is the enforcement point; the race was not driven this round either).

Scope selection

  • Central claim (unchanged): qwen review emit-workflow writes a runnable workflow script whose dispatch data — roster keys, prompts, worktree pin, subagent type — is byte-identical to what agent-prompt --roster builds through the shared buildLaunch, and the script loads/compiles/executes in the real Workflow loader and sandbox.
  • Secondary claim 1: every refusal lands before any brief, record, or script is written — and since d9bd123c, before the session directory exists.
  • Secondary claim 2: the additive refactor changes nothing for existing callers.
  • Delta probes (new since 891b6ac3): D1 all-dead failure names the dispatch instead of prescribing a re-emit loop; D2 territory refusal states the real bound (wall-clock caps + fail-closed guard) instead of "silently truncated"; D3 a refused plan leaves no empty session directory; D4 the key-mismatch guard gains its pinning test; D5 test-efficacy fixtures are hermetic against ambient git config; D6 the TS1117 duplicate getToolRegistry is absorbed by the merge (absent from the effective diff).

The effective diff remains purely additive inside packages/cli (10 files, 1,866+/13−; git diff HEAD^1..HEAD -- packages/core empty; lockfiles untouched), and nothing routes through the command yet (repo-wide grep: only the registry entry in review.ts and its test reference it).

Central claim & A/B

Fixture: the same small local-review plan the PR's own tests use (high effort, 1 chunk, no worktree → 15-agent roster), driven through the built CLI binaries (packages/cli/dist/index.js at head; base rebuilt from HEAD^1 = d6533785 in tmp/base-tree) as real subprocesses with scrubbed env. Witness: 01-ab-base-vs-head-roster-parity.png.

Cell Build Invocation Oracle Result
B1 base d6533785 review emit-workflow --plan P non-zero exit, no scriptPath, zero writes failed as predicted → assertion passed (3/3)
B2 base review agent-prompt --roster --plan P control capture: 13,316 B stdout, 31 record+brief entries exit 0 (3/3)
H2 head c88b60bd review agent-prompt --roster --plan P stdout, stderr, and record+brief tree byte-identical to B2 identical (4/4) — no behavior change for existing callers
H1 head review emit-workflow --plan P exit 0; script at <proj>/workflows/generated/review/sess_ab-<8hex>/qwen-review-<10hex>.js; pure-literal meta; WORKING_DIR=null; AGENT_TYPE="review-agent"; guidance line exact; record+brief tree byte-identical to B2; every baked prompt byte-identical to its record; no .tmp leftovers 16/16

Delta probes (since round 1)

Probe Cells Result
D1 all-dead names the dispatch S7 (5 assertions) + S7b measurement fixed: all-dead message contains failure is the dispatch itself, writes the identical script, Fix what the dispatch reads … dispatch this same script again, and does not contain and dispatch again; names all 15 keys. Partial-failure message measured separately (see Findings).
D2 honest territory wording G1 (8 assertions) fixed: stderr names territory fan-out (Step 3B), wall-clock capped, fails closed; no truncat* claim remains; still points at agent-prompt --roster.
D3 no empty session dir G1/G2a–c/G10/G11 "no workflows dir" cells (6) + M3 mutant fixed and pinned: every refusal leaves <projectDir>/workflows absent; M3 (handler-level pre-refusal deleted) is killed by exactly the new test creates no directory for a plan it refuses — the layered pair (M2 builder check kills 2 tests, M3 handler check kills 1) shows the ordering property is what the new test pins.
D4 key-mismatch pin M4 mutant fixed: killed by refuses a roster key the builder did not build under on the intended assertion (previous round's survivor). Witness: 04-mutation-matrix-round2.png (7/7 mutants and controls killed by their intended tests).
D5 hermetic fixtures hazard control (2) + clean run (1) + hostile run (1) verified: an unsanitized git add under ambient GIT_INDEX_FILE really stages into the ambient index (repo index untouched — the persistent-runner hazard is real), and test-efficacy.test.ts passes 132/132 under both a clean env and the hostile ambient (GIT_INDEX_FILE + GIT_CONFIG_GLOBAL with core.sparseCheckout=true).
D6 TS1117 duplicate read-only absorbed: client.telemetrySwap.test.ts at HEAD carries exactly one getToolRegistry property (the branch fix and main's #10252 were byte-identical; the merge kept one), so the fix commit correctly does not appear in the effective diff.

Guard matrix re-run at the new head (witness 02-guard-matrix-refusals-before-write.png): territory, three unusable-size shapes, missing QWEN_CODE_PROJECT_DIR, unreadable plan/rules, three symlinked-directory plants (session dir / review subdir / generated root — link targets stayed empty), 500/3200 boundary accepted and 501/3201 refused, sess.1 vs sess_1 land in different session dirs, a symlinked plan spelling produces the same script name. 52/52.

Real loader/sandbox re-run (witness 03-real-loader-sandbox-fail-closed.png): the emitted file resolves through core's real resolveSavedWorkflowScript({scriptPath}) (content byte-identical, name = file stem), is not listed by listSavedWorkflows, compiles through the real compileWorkflowScript (meta.name = review-step-3a), and the same bytes copied outside the trusted roots are refused. Executing the compiled vm.Script with sandbox-shaped stubs (runtime option gates included): 15/15 dispatches once each with agentType: 'review-agent' and roster keys as labels; one dead agent / undefined / whitespace-only each fail the step naming the exact key; all-dead fails naming every key and the dispatch; empty roster throws; a worktree plan pins every dispatch via workingDir without isolation, and an unmeasurable tree warns on stderr (an unmeasured tree is not a clean one) instead of staying silent. 32/32.

Corrections (to the description — no code change requested)

Both round-1 corrections still stand at the new head; re-measured values are in the status table (C1, C2). The test-plan step "the script parses (node --check)" should read "compiles under compileWorkflowScript".

Findings

F1 (low, completeness — no action required): the partial-failure and empty-roster messages still prescribe re-running emit-workflow, whose re-emit regenerates a byte-identical script when the worktree state is unchanged. Measured: S7b shows the partial message ends Re-run 'qwen review emit-workflow' and dispatch again, and H1/digest naming prove a re-emit for an unchanged plan+tree writes the identical file — the same redundancy the commit removed for the all-dead case, one level down. It is not a loop (the instruction terminates in a dispatch, which may succeed on a transient per-agent cap — the author's stated rationale for scoping the fix to all-dead), so this is an observation, not a defect. If symmetry is wanted, the partial message can say "dispatch this same script again" too; note the suite is green with and without such a wording change (the message tail is unpinned — a fixture asserting the partial message's remedy would be the pin).

Not covered

  • Live dispatch through a real Workflow tool call / model. Nothing routes to the command yet (author-declared out of scope; grep-confirmed). The exec cells run the real compiled script with stub agent/parallel matching the sandbox's contracts.
  • Per-commit verification. Depth-2 checkout: git rev-list HEAD^1..HEAD^2 returns 1 while the metadata lists 14 commits — the shallow boundary, not the truth. The aggregate HEAD^1..HEAD diff is what was verified; per-commit attribution was out of reach. The commit messages are consistent with the final state (each delta claim was probed as behavior, not as history).
  • Windows / macOS. Linux container only; the author marks both ⚠️.
  • TOCTOU between ensureWritableReviewWorkflowsDir and the rename (carried from round 1).
  • A full base-side npm ci. Out of budget; the base control reuses the root node_modules. Clean because the PR leaves package.json/lockfiles untouched (verified) and packages/core byte-identical (verified); the internal-link realpath was asserted (node_modules/@qwen-code/qwen-code-core → repo packages/core, built from identical sources). The base tsc reported 283 environmental type errors, all in test files (missing nested @testing-library/react), none under src/commands/review/; dist/ was still emitted and the base CLI ran (asset-copy step run manually after the tsc exit).
  • The first matrix run of this round had a harness ANSI-parsing bug (vitest's × markers are ANSI-wrapped, so failedTests came back empty and every mutant mis-scored as "survived"). The parser was fixed and the matrix re-run in full; only the fixed run is counted. The buggy run is not evidence about the PR.

Methodology

Environment: node:22-bookworm CI container, Node v22.23.2, merge-ref checkout (HEAD = merge 12bd18e8, HEAD^1 = base tip d6533785, HEAD^2 = PR head c88b60bd), npm ci + npm run build pre-run at head; the base arm rebuilt only packages/cli (build_package.js + tsc --build, PATH-amended) in tmp/base-tree after copying the git-ignored generated git-commit.ts. Harnesses (01-ab.mjs, 02-guards.mjs, 03-sandbox.mjs, 04-mutations.mjs, 05-hermetic.mjs, 06-gates.mjs, shared lib.mjs) live in this artifact dir and drive the built CLI as a subprocess with scrubbed env per scratch world; loader/sandbox cells import the built core modules directly and execute the real compiled vm.Script under the runtime's wrapper and option gates. Mutants were single-occurrence string replacements applied to the source, run through npx vitest run on the three new/changed test files, and reverted — final git status --porcelain over the review dir clean, asserted. Gates: full src/commands/review/ vitest (115 files / 5,561 passed / 17 skipped, exit 0) and tsc --noEmit on packages/cli (clean; liveness proven by a planted type error reported at exit 2, then clean again). Evidence images produced with scripts/verify-capture.mjs; raw per-cell stdout/stderr and every vitest run live under logs/. PR text scanned for steering instructions: none found (the trailing Claude session link is metadata).

Flakiness gate log

rounds=5 files=4 skipped=0
file packages/cli/src/commands/review.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review.test.ts
file packages/cli/src/commands/review/emit-workflow.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/emit-workflow.test.ts
file packages/cli/src/commands/review/test-efficacy.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/test-efficacy.test.ts
file packages/cli/src/commands/review/workflow-script.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/workflow-script.test.ts


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  packages/cli/src/commands/review.test.ts: PPPPP
  packages/cli/src/commands/review/emit-workflow.test.ts: PPPPP
  packages/cli/src/commands/review/test-efficacy.test.ts: PPPPP
  packages/cli/src/commands/review/workflow-script.test.ts: PPPPP

verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence

--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/cli/src/commands/review.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/emit-workflow.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/workflow-script.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/emit-workflow.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/workflow-script.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/emit-workflow.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/workflow-script.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/emit-workflow.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/workflow-script.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/emit-workflow.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/workflow-script.test.ts: P (exit 0)

Evidence images

01-ab-base-vs-head-roster-parity

02-guard-matrix-refusals-before-write

03-real-loader-sandbox-fail-closed

04-mutation-matrix-round2

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

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, looks ready to ship. ✅

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round summary — no action taken (PR #10119)

Critical-only mode is active for this PR: the window's deterministic brake engaged after 5 change-producing rounds. This round's actionable sections are all empty:

  • Reviews: none
  • Inline comments: none
  • Issue-level comments: none
  • Failed checks: none
  • Still-red checks: none

The only feedback newer than the last evaluation was excluded by the deterministic brake as non-Critical and remains open for human follow-up:

  • A PR comment from the automated reviewer (@qwen-code-ci-bot) — deferred as non-Critical.
  • Maintainer @wenshao is at this window's regular-feedback budget, so untagged feedback from that author is deferred. To continue: tag [Critical], submit a Request-changes review, or comment @qwen-code /retry for a fresh window.

Per the Critical-only rules, deferred items are an audit record, not work: no code was modified, no threads were resolved, and no commit was made. The PR head remains at its previously verified state.

Diff growth this window stays within budget (source 117 / test 342 lines vs 400/400).

中文说明

Autofix 轮次总结 — 未采取任何行动(PR #10119

本 PR 当前处于仅处理 Critical 的模式:本窗口在完成 5 个产生改动的轮次后,确定性刹车机制已启动。本轮所有可执行区域均为空:

  • Reviews(评审):无
  • Inline comments(行内评论):无
  • Issue-level comments(议题级评论):无
  • Failed checks(失败的检查):无
  • Still-red checks(持续失败的检查):无

自上次评估以来的唯一反馈均被确定性刹车机制判定为非 Critical 而排除,保持开放留待人工跟进:

  • 自动评审机器人(@qwen-code-ci-bot)的一条 PR 评论 —— 因非 Critical 被延后。
  • 维护者 @wenshao 已达到本窗口的常规反馈预算,其未标记的反馈被延后。如需继续:请标记 [Critical]、提交 Request-changes 评审,或评论 @qwen-code /retry 开启新的计数窗口。

按照仅处理 Critical 的规则,被延后的条目属于审计记录而非工作项:未修改任何代码,未关闭任何评审线程,也未产生任何提交。PR 的 head 保持在此前已通过验证的状态。

本窗口的 diff 增长仍在预算之内(源码 117 / 测试 342 行,预算 400/400)。

Deferred non-Critical feedback

Critical-only mode is active: 5 change-producing rounds are complete. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

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


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Local verification on macOS — built from source, run end to end

I checked out this PR's head, built the whole workspace and the bundled CLI on macOS, drove qwen review emit-workflow against a real Step 1 plan, and then loaded and executed the script it generated through core's own workflow loader and vm sandbox — only the LLM leaf was stubbed.

Verdict: every claim in the PR description holds on macOS, and all ten open review threads are addressed at HEAD, each with a source mutant that goes red. I found nothing that should block the merge. The 🍏 macOS row in Tested on can move from ⚠️ to ✅.

Environment

OS macOS 25.6.0 (arm64)
Node / npm 24.18.1 / 11.16.0
PR head under test c88b60bd89 (unchanged for the whole run)
Build npm run build ✅ · npm run bundle
Static npm run typecheck ✅ · eslint (5 changed/added files) ✅ · prettier --check

1 · Test runs on macOS

Scope Result
emit-workflow.test.ts + workflow-script.test.ts + review.test.ts 54 passed (3 files)
the whole packages/cli/src/commands/review/ directory 5538 passed, 3 skipped (116 files), exit 0

The PR body's "43 passed" is stale — the later commits grew it to 54. The directory run also emitted two [vitest-worker]: Timeout calling "onTaskUpdate" unhandled errors; that is the known reporter/worker RPC flake, unrelated to this PR, and vitest still exited 0 with zero failing tests.

2 · The emitted script — real CLI, real plan

capture-local on a small local change → emit-workflow --plan … --rules …. Two stdout lines, the second an absolute scriptPath under <projectDir>/workflows/generated/review/<session>/qwen-review-<digest>.js; node --check parses it; meta is the first statement; the three baked literals and the parallel() dispatch are where the design says they are.

emit-workflow happy path

3 · Byte parity with the hand-launched roster

Running agent-prompt --roster over the same plan rewrites the same 20 artifacts. diff -r between the two trees is empty, and each of the 10 AGENTS[].prompt values baked into the script is byte-identical to the roster's own recorded prompt. --rules reaches all 9 reviewing briefs; agent 7 (build + test, not a review) correctly carries none.

byte parity

4 · The script under the production workflow runtime

This is the part the PR description stops short of, and it is what convinced me. I fed the generated file to core's real resolveSavedWorkflowScript({scriptPath}), listSavedWorkflows, and then to new WorkflowOrchestrator(dispatch).run({script}) — the production orchestrator, so the vm sandbox, the meta parser, parallel()'s errors-as-data contract and the JSON revival are all the real ones; only dispatch (the subagent leaf) is a stub that records what it was asked for.

  • loads by path; not listed as a saved workflow; workflow('<stem>') by name is refused
  • 10 dispatches, one per roster key, all agentType: "review-agent", all phase: "Review", meta.name = review-step-3a
  • R2-2 fail-closed holds for every failure shape: one agent null, one agent whitespace-only, one agent whose dispatch throws, and all agents dead — each rejects the step instead of handing the caller a shortened delivered list

production runtime

I also probed the shipped script against the sandbox constraints directly: as shipped it resolves cleanly, while mutants that read meta, call Date.parse()/Date.UTC(), or make meta a non-literal are each rejected by the real runtime — confirming the body is clean and the constraints are live.

5 · Every refusal writes nothing

Territory-sized plan, missing/null size fields, no QWEN_CODE_PROJECT_DIR, and a symlink planted at each of the three components of the trusted root. In every case the command refuses with a message that names the reason, the generated tree is byte-identical before and after, no brief or prompt record exists, and the external symlink target stays empty.

refusals

6 · The four Critical fixes, on a real macOS filesystem

  • R2-4/tmp/…/plan.json and /private/tmp/…/plan.json (the same file, two spellings macOS hands you) now select one script name. This is the one that could only be caught here.
  • R2-3 — raw session ids sess.1 and sess_1 land in sess_1-58d657d2/ and sess_1-8ac3c66d/; the readable prefix survives, the raw-id digest keeps them apart.
  • R2-1 — a symlink planted at the script path is replaced, not written through: the external victim keeps its contents, the target is a regular file afterwards, and no .tmp is stranded on success or on a failed write.
  • Worktree pin + residue: on a real linked review worktree the plan's worktreePath is baked in as the pin, a contaminated tree produces the residue warning, and briefs + records stay byte-identical to --roster.

macOS-specific fixes

7 · Do the tests actually hold the fixes?

I reverted each fix in source, one at a time, and re-ran the suite. 10 / 10 mutants killed — including the two the earlier review rounds flagged as unwitnessed (the review middle path component, and the temp-file cleanup's failed-write half).

mutation matrix

Separately, all four runScript fidelity channels raised earlier are now closed — the harness strips the meta block instead of executing it, runs the body in a node:vm context binding only the sandbox globals under 'use strict', and gates the stub agent's options. Mutants for each (meta read, setTimeout, Date.parse, an unknown timeout: option, a sloppy-mode undeclared assignment) all go red now.

8 · Open review threads — status at this head

Thread Status at c88b60bd89 Evidence
R2-1 symlinked generated dirs fixed all 3 components refuse; 0 files leak; mutant kills
R2-2 partial fan-out failed open fixed real runtime throws on all 4 failure shapes; mutant kills
R2-3 session-dir collision fixed distinct dirs on the real CLI; mutant kills
R2-4 plan path not canonicalized fixed /var vs /private/var → one script on macOS; mutant kills
R3-1 symlink test vacuous on macOS fixed fixture dir is realpathSync'd; write-through mutant kills on macOS
R3-2 middle path component unwitnessed fixed dedicated test; dropping only the middle component kills it
bot: temp file only on the success path fixed failed-write half present; removing the finally kills it
bot: no handler-level --rules happy path fixed handler test; dropping the third arg kills it
bot: runScript diverges in 4 channels fixed all four now go red under mutation
bot: determinism guard too narrow fixed guard covers Date.parse / Date.UTC / bare Date(

9 · Minor, non-blocking

  1. PR body wording. It says stderr "carries the same worktree-residue warnings agent-prompt --roster would print". The two carry the same facts (same paths, same suppression rule) but not the same bytes — emit says "Restore them BEFORE dispatching the workflow … so the script is rebuilt", roster says "BEFORE launching this wave … so the wave is rebuilt". That difference is correct and deliberate; only the sentence in the description overstates it. The briefs and prompt records are byte-identical.
  2. Test count in the body (43) is stale; it is 54 at this head.
  3. Refusals surface as An unexpected critical error occurred: plus a stack trace — identical to the sibling agent-prompt, so this is house style rather than a regression. Noting only for the record.

10 · What this run does not cover

  • No live-model A/B. The runtime run stubs the subagent leaf; measuring review quality against the hand-launched path needs routing, which this PR deliberately excludes.
  • Windows is still unverified — I only have macOS here; CI covers Linux.
  • Nothing here exercises the skill, because nothing routes to this command yet, which is the PR's stated scope.
中文说明

macOS 本地验证 —— 源码构建、端到端实跑

我拉取了本 PR 的 head,在 macOS 上完整构建了 workspace 与打包 CLI,用一个真实的 Step 1 plan 跑了 qwen review emit-workflow,并且把它生成的脚本交给 core 自己的 workflow 加载器和 vm 沙箱真实执行——只有 LLM 叶子节点被打桩。

结论: PR 描述中的每一项断言在 macOS 上都成立;十条未解决的评审 thread 在当前 head 上全部已修,且每一条都有一个"改回去就变红"的源码变异体作证。没有发现应当阻塞合并的问题。Tested on 表里的 🍏 macOS 可以从 ⚠️ 改为 ✅。

环境

系统 macOS 25.6.0 (arm64)
Node / npm 24.18.1 / 11.16.0
被测 head c88b60bd89(全程未变)
构建 npm run build ✅ · npm run bundle
静态检查 npm run typecheck ✅ · eslint(5 个改动/新增文件)✅ · prettier --check

1 · macOS 上的测试

范围 结果
emit-workflow.test.ts + workflow-script.test.ts + review.test.ts 54 通过(3 个文件)
整个 packages/cli/src/commands/review/ 目录 5538 通过,3 跳过(116 个文件),exit 0

PR 正文里的"43 通过"已过时——后续 commit 把它涨到了 54。目录级运行另有两条 [vitest-worker]: Timeout calling "onTaskUpdate" 未捕获错误;那是已知的 reporter/worker RPC 抖动,与本 PR 无关,vitest 仍以 0 退出且零失败用例。

2 · 生成的脚本 —— 真实 CLI、真实 plan

对一个小型本地改动跑 capture-local,再跑 emit-workflow --plan … --rules …。stdout 两行,第二行是位于 <projectDir>/workflows/generated/review/<session>/qwen-review-<digest>.js 的绝对 scriptPathnode --check 通过;meta 是首个语句;三个烘入的字面量与 parallel() 分派都在设计所说的位置。(截图 1)

3 · 与手工 roster 的逐字节一致

对同一个 plan 跑 agent-prompt --roster 会重写同样的 20 个产物。两棵树 diff -r 为空,脚本里 10 条 AGENTS[].prompt 与 roster 自己的 prompt 记录逐字节相同。--rules 抵达全部 9 个评审 brief;agent 7(跑 build+test 而非评审)正确地不带 rules。(截图 2)

4 · 在生产 workflow 运行时里执行

这是 PR 描述没有走到的一步,也是最有说服力的一步。我把生成文件交给 core 真实的 resolveSavedWorkflowScript({scriptPath})listSavedWorkflows,再交给 new WorkflowOrchestrator(dispatch).run({script})——即生产 orchestrator,所以 vm 沙箱、meta 解析器、parallel() 的 errors-as-data 契约、JSON 复活全是真的;只有 dispatch(子 agent 叶子)是一个记录调用参数的桩。

  • 按路径可加载;不会被列为 saved workflow;按名字 workflow('<stem>') 被拒绝
  • 10 次分派,每个 roster key 一次,全部 agentType: "review-agent"phase: "Review"meta.name = review-step-3a
  • R2-2 的 fail-closed 在每种失败形态下都成立:某个 agent 返回 null、某个只返回空白、某个分派抛异常、全部 agent 都死——每一种都让这一步失败,而不是把缩短后的 delivered 交给调用方

我还直接用沙箱约束探测了发布版脚本:原样可正常 resolve,而读取 meta、调用 Date.parse()/Date.UTC()、或把 meta 写成非纯字面量的变异体都被真实运行时拒绝——说明脚本主体是干净的,而这些约束确实在生效。(截图 3)

5 · 每一次拒绝都不写任何东西

territory 规模的 plan、size 字段缺失或为 null、没有 QWEN_CODE_PROJECT_DIR、以及在受信根的三个路径分量上分别种下软链接。每一种情况下命令都以点名原因的信息拒绝,生成目录树前后逐字节一致,没有 brief、没有 prompt 记录,外部软链目标始终为空。(截图 4)

6 · 四条 Critical 修复在真实 macOS 文件系统上

  • R2-4 —— /tmp/…/plan.json/private/tmp/…/plan.json(macOS 给出的同一个文件的两种拼法)现在选中同一个脚本名。这一条只有在这里才能验出来。
  • R2-3 —— 原始 session id sess.1sess_1 分别落到 sess_1-58d657d2/sess_1-8ac3c66d/;可读前缀保留,原始 id 摘要把两者分开。
  • R2-1 —— 种在脚本路径的软链被替换而非被穿透写入:外部受害文件内容不变,事后目标是普通文件,成功路径失败写入路径都没有残留 .tmp
  • worktree pin 与残留:在真实的 linked review worktree 上,plan 的 worktreePath 被烘成每个 agent 的 pin;被污染的树会产生残留告警;brief 与记录仍与 --roster 逐字节一致。(截图 5)

7 · 这些测试真的守得住修复吗?

我把每条修复逐一在源码里改回去再跑测试。10 / 10 变异体被杀——包括此前评审轮次点名"无人见证"的两条(review 这一中间路径分量、以及临时文件清理的失败写入那一半)。(截图 6)

另外,此前提出的 runScript 四条保真度缺口现已全部闭合——辅助函数改为剥离 meta 块而非执行它,在只绑定沙箱全局变量的 node:vm 上下文中以 'use strict' 运行主体,并对桩 agent 的选项做了门禁。对应的五个变异体(读 metasetTimeoutDate.parse、未知的 timeout: 选项、sloppy 模式下的未声明赋值)现在都会变红。

8 · 未解决 thread 在当前 head 的状态

Thread c88b60bd89 的状态 证据
R2-1 软链接的生成目录 已修 三个分量全部拒绝;0 文件泄漏;变异体被杀
R2-2 部分扇出 fail-open 已修 真实运行时在 4 种失败形态下都抛错;变异体被杀
R2-3 session 目录碰撞 已修 真实 CLI 下目录不同;变异体被杀
R2-4 plan 路径未规范化 已修 macOS 上 /var/private/var → 同一脚本;变异体被杀
R3-1 软链测试在 macOS 上空转 已修 fixture 目录已 realpathSync;穿透写入变异体在 macOS 上被杀
R3-2 中间路径分量无见证 已修 有专门用例;只删中间分量即被杀
bot:临时文件只覆盖成功路径 已修 失败写入那一半已补;删掉 finally 即被杀
bot:handler 层缺 --rules 正例 已修 有 handler 级用例;去掉第三个参数即被杀
bot:runScript 四条保真度缺口 已修 四条在变异下全部变红
bot:确定性守卫覆盖太窄 已修 守卫已覆盖 Date.parse / Date.UTC / 裸 Date(

9 · 次要、不阻塞

  1. PR 正文措辞。 正文说 stderr "carries the same worktree-residue warnings agent-prompt --roster would print"。两者携带相同的事实(相同路径、相同的抑制规则),但不是相同的字节——emit 说 "Restore them BEFORE dispatching the workflow … so the script is rebuilt",roster 说 "BEFORE launching this wave … so the wave is rebuilt"。这个差异本身是正确且刻意的,只是描述里那句话说过了头。brief 与 prompt 记录确实逐字节一致。
  2. 测试数量(43)已过时;当前 head 是 54。
  3. 拒绝的呈现形式是 An unexpected critical error occurred: 加一段栈——与同级的 agent-prompt 完全一致,属于既有风格而非回归。仅作记录。

10 · 本次验证没有覆盖的部分

  • 没有真实模型 A/B。 运行时实跑打桩了子 agent 叶子;要把评审质量与手工路径对比,需要先有路由,而本 PR 刻意不含路由。
  • Windows 仍未验证——我这边只有 macOS;Linux 由 CI 覆盖。
  • 没有任何环节触及 review skill,因为目前还没有东西路由到这个命令——这正是本 PR 声明的范围。

@wenshao
wenshao added this pull request to the merge queue Aug 28, 2026
Merged via the queue into QwenLM:main with commit 11afc40 Aug 28, 2026
75 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants