feat(review): emit the Step 3A fan-out as a generated workflow script - #10119
Conversation
`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
|
Gate re-check for the moved head — nine commits since the last pass ( 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 ( 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 Risk: no high-risk path matches in the revert-correlation scan. No elevated risk signals. Moving on to the incremental code review. 🔍 中文说明对移动后的头部重新过门禁 —— 上一轮( 模板 ✓ 未变 —— 各必填部分齐全,中英文对照完整。 问题 / 方向: 与首轮一致 —— 动机是 #8769 记录的已观测 orchestrator 失败类别(扇出约定在真实运行中被打破),方向 issue 明确写明了这个子命令;地基(#9987、#8971、#8972)已合入。此后维护者已在真实会话 + 真实 subagent 分派下验证过 emitter,确认它做到了声称的事。 规模: 上一轮增量中唯一的核心文件( 方案: 增量恰是对维护者验证的回应 —— 其 §B、§C 从根上修复,空目录问题在所有拒绝类别上修复,最后一个无测试的守卫补上测试,另有 probe 测试的密封 git 夹具与 风险: revert 相关性扫描未命中高风险路径。无升级风险信号。 进入增量代码审查。🔍 — Qwen Code · qwen3.8-max Reviewed at |
|
@qwen-code /takeover |
|
🤝 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 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
Code review — the increment since
|
| 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-drive与revert-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 CI 与 Security 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
|
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:
Verdict: approve. All PR CI runs on the reviewed head completed green — 中文说明置信度:4/5 —— 修复轮回应的是维护者本人的实机发现,他已在真实环境逐条复验且无回归,其后的增量只有测试密封性与一次"两边保留"的合并;第五点因下述两个 diff 外原因保留。 退一步看:这是修复轮应有的样子。4 条维护者发现均从根上解决 —— 开出循环药方的消息现在点名 dispatch 本身,不实的截断说法改为陈述实测上限(我已对照 runtime 源码亲自核实),拒绝不再留下空目录,最后一个无测试的守卫有了顶替 builder 的见证。密封夹具提交针对的是真实事故类别,用的是仓库既有的隔离工具而非新机制。合并解决是乏味但正确的并集,由注册表测试钉死。无过度设计、无范围蔓延 —— 仍然不路由、不清理,与首轮的阶段规划完全一致。 两点如实点名、但不计入对本 PR 的扣分:
结论:批准。被审头部上的所有 PR CI 运行均已完成且为绿(含 — Qwen Code · qwen3.8-max Reviewed at |
doudouOUC
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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', () => { |
There was a problem hiding this comment.
[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)
| const body = script.replace('export const meta =', 'const meta ='); | ||
| const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor; | ||
| const fn = new AsyncFunction('agent', 'parallel', 'phase', 'log', body); |
There was a problem hiding this comment.
[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:
- It rewrites
export const meta =toconst meta =and executes the meta block — leaving a livemetabinding in body scope — while the runtime strips it and parses it as a pure literal (extractAndStripMeta/parseWorkflowMetaLiteral, "it is parsed, never executed"). AFAN_OUT_BODYedit that readsmetapasses all 15 tests here — as a source mutant the whole file still passes 15/15 — then throwsReferenceError: meta is not definedat real dispatch; a meta block that is valid executable JS but not a pure literal (a call or concatenation) passes here whilecompileWorkflowScriptthrowsinvalid meta object literalat launch. - Bare
new AsyncFunction(...)compiles sloppy;wrapWorkflowBodywraps the body in(async () => {'use strict'; …})()— an undeclared-assignment typo silently creates a global here and throws there. - 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 setTimeoutis"function"here,"undefined"there; calling it reaches in the harness and throws ReferenceError in the sandbox. A futureperformance.now()/setTimeoutreference inFAN_OUT_BODYpasses the suite and dies at dispatch. - The stub
agent(lines 40–43) applies none of the runtime's option gates — theKNOWN_AGENT_OPTSallowlist, the empty-workingDirrefusal, theworkingDir+isolationexclusion. Probe end-to-end: addingtimeout: 60000to 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 验证并用探针复现:
- 它把
export const meta =改写为const meta =并执行 meta 块——使meta绑定在 body 作用域中存活——而运行时会将其剥离并按纯字面量解析(extractAndStripMeta/parseWorkflowMetaLiteral,“只解析、从不执行”)。读取meta的FAN_OUT_BODY修改在这里能通过全部 15 个测试(作为源码突变体整个文件仍是 15/15),在真实分派时却抛ReferenceError: meta is not defined;一个可执行但非纯字面量的 meta 块(调用或拼接)在这里通过,而compileWorkflowScript在启动时抛invalid meta object literal。 - 裸
new AsyncFunction(...)以 sloppy 模式编译;wrapWorkflowBody用(async () => {'use strict'; …})()包裹 body——未声明标识符的赋值在这里静默创建全局变量,在那里抛错。 - body 在宿主域中执行,所有 Node 全局都可用;vm 上下文只绑定沙箱自身的全局(
agent/parallel/pipeline/phase/log/console/args/budget/workflow)。探针:typeof setTimeout在这里是"function",在那里是"undefined";调用它在这里可达,在沙箱中抛 ReferenceError。未来FAN_OUT_BODY引用performance.now()/setTimeout会通过本套件、在分派时死亡。 - 桩
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', () => { |
There was a problem hiding this comment.
[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)
| expect(script).not.toContain('Date.now'); | ||
| expect(script).not.toContain('Math.random'); | ||
| expect(script).not.toContain('new Date'); |
There was a problem hiding this comment.
[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.ts — safeDate, .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"
| 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,但沙箱的 safeDate 对 Date.parse(...)、Date.UTC(...) 和裸 Date(...) 调用同样抛错(workflow-sandbox.ts 中 safeDate 本体、.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)
…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.
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Review feedback round — PR #10119Outcome: changed — one commit ( Dispositions[rv:5029099433] C1 (Critical) — "Wrong import:
|
| 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 --checkon the two changed files — passedcd 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 build与npm 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.parse、Date.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_BODY 中 setTimeout(() => {}, 0) |
ReferenceError: setTimeout is not defined(vm 上下文) |
| G | FAN_OUT_BODY 中 void 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
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
4 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-1 failed-write temp cleanup test gap — already reported (comment 3862296232)
- R1-2 sandbox-analogue false-pass channels — already reported (comment 3862296240)
- R1-3 handler --rules happy-path test gap — already reported (comment 3862296250)
- R1-4 incomplete Date determinism guard — already reported (comment 3862296262)
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`; |
There was a problem hiding this comment.
[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 目录是指向外部目录的符号链接,mkdirSync、writeFileSync 和 renameSync 会沿链接写入。命令会把包含完整 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', |
There was a problem hiding this comment.
[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.1 与 sess_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)) |
There was a problem hiding this comment.
[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.js(1 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) { |
There was a problem hiding this comment.
[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
left a comment
There was a problem hiding this comment.
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 forpackages/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.
|
🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下: Address-review round summary — PR #10119Round scope: 4 Critical findings, each a re-checked existing blocker confirmed Feedback points and dispositions[rc:3863610970] R2-1 — symlinked generated-workflow directories written through → FixedReproduced by code reading: Fix: Witness: [rc:3863610979] R2-3 — lossy session-id sanitization collides directories → FixedReproduced by code reading: Fix: Witness: [rc:3863610988] R2-4 — plan path hashed without canonicalization → FixedMechanism confirmed by code reading: Fix: canonicalize the resolved plan with Witness: [rc:3863611003] R2-2 — workflow succeeds when a required agent returns nothing → FixedReproduced by executing the real generated script in the vm harness (pre-fix Fix: Witness: four tests flipped from asserting a shortened successful result to Not in this round's actionable set (recorded, no action)
Mutation probes (per guard, before committing)
Files changed
VerificationCommands actually run and their results:
Self-audit note: the first audit pass caught four accidental comment 中文说明处理评审意见轮次总结 — PR #10119本轮范围:4 条 Critical 发现,均为两位审查者确认在 HEAD 上仍然存在既有阻断项的复核。四条全部在提交 反馈点与处置[rc:3863610970] R2-1 — 符号链接的生成 workflow 目录被穿透写入 → 已修复通过代码阅读复现: 修复: 见证: [rc:3863610979] R2-3 — 有损的 session id 清洗导致目录碰撞 → 已修复通过代码阅读复现: 修复: 见证: [rc:3863610988] R2-4 — plan 路径未经 canonicalize 即哈希 → 已修复通过代码阅读确认机制: 修复:哈希前先用 见证: [rc:3863611003] R2-2 — 必需 agent 无返回时 workflow 仍成功 → 已修复通过在 vm 测试装置中执行真实生成的脚本复现(修复前行为:一个 agent 失败时返回 修复: 见证:四个测试从断言"带缺失角色的成功返回"翻转为断言"拒绝并点名缺失角色": 不属于本轮可执行集合(仅记录,不处理)
变异探针(提交前逐个守卫执行)
变更文件
验证实际执行的命令及结果:
自审说明:第一轮自审发现重写文件时意外带入的四处注释重排/措辞改动;提交前已恢复为 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/模型 |
doudouOUC
left a comment
There was a problem hiding this comment.
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 |
|
Determinism guard: Date.parse/etc. |
✅ Fixed |
No new issues found.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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)
| const scriptPath = reviewWorkflowScriptPath(plan); | ||
| mkdirSync(dirname(scriptPath), { recursive: true }); | ||
| writeFileSync(victim, 'keep me', 'utf8'); | ||
| symlinkSync(victim, scriptPath); | ||
| writeFileSync(plan, JSON.stringify(localPlan()), 'utf8'); |
There was a problem hiding this comment.
[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.ts — dir = 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)
| it('refuses a symlinked generated root before writing anything', () => { | ||
| const plan = join(dir, 'plan.json'); | ||
| writeFileSync(plan, JSON.stringify(localPlan()), 'utf8'); |
There was a problem hiding this comment.
[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 ensureWritableReviewWorkflowsDir — join(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)
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Autofix round summary — PR #10119Both inline findings of round 3 are resolved in code (one commit, 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
Fix (as suggested, the house pattern used 26× across review tests): canonicalize the fixture root in the Witness probe after the fix: replacing the implementation's temp-file + [rc:3865455314] R3-2 (Suggestion) — middle loop component of The gap was probed first: with the loop reduced to Added the third test mirroring the other two: symlink Witness probe: removing the middle component again makes the new test the only failure — Reviews (no action required)
Verification
中文说明Autofix 轮次总结 — PR #10119第 3 轮的两条行内发现均已在代码中解决(单个提交 行内发现[rc:3865455294] R3-1(Critical)——符号链接脚本测试在带符号链接的 tmpdir 下发生分歧 → 已修复 修改前先复现了该论断。测试在
修复(按建议采用 review 测试中已使用 26 次的既有模式):在 修复后的见证探针:把实现中的临时文件 + [rc:3865455314] R3-2(Suggestion)—— 先做缺口探针:把循环缩减为 新增镜像另两条的第三条测试:把 见证探针:再次移除中间组件后,新测试是唯一失败项 —— 评审(无需处理)
验证
🦷 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/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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)
|
🤖 Addressed the latest review feedback (round 4/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/100 轮)。改动内容与我反驳保留之处如下: Autofix review-address round — PR #10119Feedback triaged this round: 1 item (round-4 review rv:5034531001). No inline comments, no failed checks, no still-red checks, no base conflict ( Finding 1 — cannot-read-the-plan handler-test gap (emit-workflow.test.ts) — ImplementedSource: 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: Change (test-only, +9 lines): added one handler-level test,
Mutation probe (witness check): temporarily removed the try/catch guard from Not in scope this round
Verification
Commit: 中文说明Autofix 审查处理轮次 — PR #10119本轮分类的反馈: 1 条(第 4 轮审查 rv:5034531001)。无行内评论、无失败检查、无持续失败的检查、无基线冲突( 发现 1 — cannot-read-the-plan 处理器测试缺口(emit-workflow.test.ts)— 已实现来源:第 4 轮审查确认该建议级发现已在第 2 轮的延后列表中报告过(审查 5031737007):“没有测试钉住该处理器的 cannot-read-the-plan 守卫,而所有兄弟命令都钉住了”。 先行核实: 变更(仅测试,+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/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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 branchpackages/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.
|
🤖 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 roundThis round was invoked with Conflict resolutionMerged
Feedback dispositions
Mutation probesNot applicable: a merge commit authored no new guard or branch. The only Verification
中文说明Autofix 轮次总结 — PR #10119(address-review)本轮范围本轮以 冲突解决已合并
反馈处置
变异探针不适用:合并提交没有引入任何新的守卫或分支。解决方案中唯一手写的内容是合并后的 import 语句,它本身没有任何行为;其见证是编译步骤(缺失或写错的导入会让 验证
Deferred non-Critical feedbackCritical-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. (
中文说明已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 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/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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 pathpackages/cli/src/commands/review/emit-workflow.test.ts:564 — [probe] mid-write failure branch of the cleanup has no witnesspackages/cli/src/commands/review/lib/paths.ts:164 — [probe] guard-to-write TOCTOU redirects the script writepackages/cli/src/commands/review/workflow-script.test.ts:334 — [probe] dispatch-logic guard is a two-substring proxypackages/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)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
🤖 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 triageNo actionable feedback this round: the Reviews, Inline comments, and This round is a same-run verification repair: the previous commit (the What was rejected
Root cause (reproduced, with probe evidence)The stack means the skip-worktree refusal in Reproduction: launching the test file under a
Changes made (follow-up commit
|
|
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 |
|
🤖 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:
No code changes, no commits, and no thread resolutions were made this round. 中文说明Autofix 审查轮次:无需处理(PR #10119)本轮没有可处理的反馈:
本轮未做任何代码改动、未提交任何 commit,也未解决任何评审线程。 Deferred non-Critical feedbackCritical-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. (
中文说明已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
@qwen-code /review |
|
Qwen Code review request accepted. Review is queued in workflow run. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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 unpinnedpackages/cli/src/commands/review/emit-workflow.test.ts:765 — [probe] dirty-tree warning never asserts the untracked probe pathpackages/cli/src/commands/review/emit-workflow.test.ts:716 — [probe] fetchedSha identity handover never parity-checkedpackages/cli/src/commands/review/workflow-script.test.ts:228 — [probe] no-worktree shape test never pins isolation absencepackages/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
left a comment
There was a problem hiding this comment.
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 unwitnessedpackages/cli/src/commands/review/emit-workflow.test.ts:778 — [probe] unmeasured-residue brief seam unpinnedpackages/cli/src/commands/review/emit-workflow.test.ts:623 — [probe] rules-refusal test misses the no-directory pinpackages/cli/src/commands/review/agent-prompt.ts:1355 — [review] residue warning duplicated instead of sharedpackages/cli/src/commands/review/emit-workflow.test.ts:396 — [review] seam assertion weaker than the loader's containment rulepackages/cli/src/commands/review/emit-workflow.test.ts:608 — [review] plan-refusal test misses the no-directory pinpackages/cli/src/commands/review/emit-workflow.test.ts:765 — [review] dirty-warning enumeration pins only the tracked pathpackages/cli/src/commands/review/emit-workflow.test.ts:803 — [probe] clean-residue brief shape unpinnedpackages/cli/src/commands/review/emit-workflow.test.ts:786 — [probe] unmeasured-warning reason interpolation unpinnedpackages/cli/src/commands/review/emit-workflow.test.ts:792 — [probe] warning conditions lack mutual-absence pinspackages/cli/src/commands/review/workflow-script.test.ts:230 — [probe] no-worktree branch missing isolation-absence pinpackages/cli/src/commands/review/emit-workflow.ts:158 — [probe] warning's restore-then-rebuild tail unpinnedpackages/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)
|
🤖 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:
The two automated-reviewer reviews listed under 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 中文说明Autofix 轮次结果:无操作(PR #10119)本窗口已进入 Critical-only(仅处理 Critical)模式(已完成 5 个产生改动的轮次),本轮所有可执行的反馈区域均为空:
列在 本轮未报告与 base 分支存在冲突,因此未执行任何合并。 结果: 无代码改动、无提交;由于没有任何变更,也无需运行验证命令。新的 [Critical] 反馈、Request changes 评审、或评论 Deferred non-Critical feedbackCritical-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. (
中文说明已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
🤖 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 triageCritical-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 ChangesMerged One conflict, in
Resolution: keep both sides. Imports and Committed as merge Post-merge sanity checks performed:
Resolved / open threadsNo inline comment findings existed this round, so Verification
中文说明轮次总结 — PR #10119(基分支冲突解决轮)反馈分类当前处于仅处理 Critical 的模式(已完成 5 个产生改动的轮次)。本轮所有可执行区域均为空:无 review、无行内评论、无 issue 级评论、无失败检查、无持续失败的检查。延后的非 Critical 区域(@wenshao 的维护者反馈预算提示)属于审计记录,按工作流规则不作处理。 本轮唯一的工作是 变更合并了
解决方式:保留双方。import 与 以合并提交 合并后的完整性检查:
已解决 / 未决讨论串本轮不存在行内评论发现,因此省略 验证
Deferred non-Critical feedbackCritical-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. (
中文说明已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 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/模型 |
|
📊 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 中文说明📊 接管里程碑 —— 第 10/100 轮(当前窗口)。统计:推送修复 9 次、审阅无需改动 4 次、超时 0 次、验证拒绝 0 次、按指示有意停止(移交人工)0 次、其他轮次(崩溃/模型错误/门错误/infra)1 次、base 更新 0 次。 轮次到这个量值得人工看一眼。可选:继续(无需操作);若轮次持续累积,考虑拆分或缩减 PR;或释放接管(移除 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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 awaypackages/cli/src/commands/review/lib/paths.ts:148 — [probe] symlink at the workflows parent level bypasses the containment looppackages/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)
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Address-review round — PR #10119No action was taken this round; no commit was made and the branch is unchanged. Why
中文说明评审反馈处理轮次 — PR #10119本轮未采取任何操作;未创建提交,分支保持不变。 原因
Deferred non-Critical feedbackCritical-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. (
中文说明已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
@qwen-code /triage |
|
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 --> Sandboxed verification: ✅ passed — merge-ready (agent verdict) - follow-up round at head 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
Verification reportPR #10119 deep verification — follow-up round —
|
| # | Round-1 item | Severity | Status at c88b60bd |
|---|---|---|---|
| M6 | Mutation survivor: key-mismatch guard in buildFanOutRoster — redundant defence, statically unreachable |
completeness | fixed — d9bd123c 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-workflowwrites a runnable workflow script whose dispatch data — roster keys, prompts, worktree pin, subagent type — is byte-identical to whatagent-prompt --rosterbuilds through the sharedbuildLaunch, 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 duplicategetToolRegistryis 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/parallelmatching the sandbox's contracts. - Per-commit verification. Depth-2 checkout:
git rev-list HEAD^1..HEAD^2returns 1 while the metadata lists 14 commits — the shallow boundary, not the truth. The aggregateHEAD^1..HEADdiff 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
ensureWritableReviewWorkflowsDirand the rename (carried from round 1). - A full base-side
npm ci. Out of budget; the base control reuses the rootnode_modules. Clean because the PR leavespackage.json/lockfiles untouched (verified) andpackages/corebyte-identical (verified); the internal-link realpath was asserted (node_modules/@qwen-code/qwen-code-core→ repopackages/core, built from identical sources). The basetscreported 283 environmental type errors, all in test files (missing nested@testing-library/react), none undersrc/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, sofailedTestscame 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
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
|
🤖 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:
The only feedback newer than the last evaluation was excluded by the deterministic brake as non-Critical and remains open for human follow-up:
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 个产生改动的轮次后,确定性刹车机制已启动。本轮所有可执行区域均为空:
自上次评估以来的唯一反馈均被确定性刹车机制判定为非 Critical 而排除,保持开放留待人工跟进:
按照仅处理 Critical 的规则,被延后的条目属于审计记录而非工作项:未修改任何代码,未关闭任何评审线程,也未产生任何提交。PR 的 head 保持在此前已通过验证的状态。 本窗口的 diff 增长仍在预算之内(源码 117 / 测试 342 行,预算 400/400)。 Deferred non-Critical feedbackCritical-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. (
中文说明已进入仅处理 Critical 的模式:已完成 5 个产生改动的轮次。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
Local verification on macOS — built from source, run end to endI checked out this PR's head, built the whole workspace and the bundled CLI on macOS, drove 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 Environment
1 · Test runs on macOS
The PR body's "43 passed" is stale — the later commits grew it to 54. The directory run also emitted two 2 · The emitted script — real CLI, real plan
3 · Byte parity with the hand-launched rosterRunning 4 · The script under the production workflow runtimeThis is the part the PR description stops short of, and it is what convinced me. I fed the generated file to core's real
I also probed the shipped script against the sandbox constraints directly: as shipped it resolves cleanly, while mutants that read 5 · Every refusal writes nothingTerritory-sized plan, missing/ 6 · The four Critical fixes, on a real macOS filesystem
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 Separately, all four 8 · Open review threads — status at this head
9 · Minor, non-blocking
10 · What this run does not cover
中文说明macOS 本地验证 —— 源码构建、端到端实跑我拉取了本 PR 的 head,在 macOS 上完整构建了 workspace 与打包 CLI,用一个真实的 Step 1 plan 跑了 结论: PR 描述中的每一项断言在 macOS 上都成立;十条未解决的评审 thread 在当前 head 上全部已修,且每一条都有一个"改回去就变红"的源码变异体作证。没有发现应当阻塞合并的问题。Tested on 表里的 🍏 macOS 可以从 环境
1 · macOS 上的测试
PR 正文里的"43 通过"已过时——后续 commit 把它涨到了 54。目录级运行另有两条 2 · 生成的脚本 —— 真实 CLI、真实 plan对一个小型本地改动跑 3 · 与手工 roster 的逐字节一致对同一个 plan 跑 4 · 在生产 workflow 运行时里执行这是 PR 描述没有走到的一步,也是最有说服力的一步。我把生成文件交给 core 真实的
我还直接用沙箱约束探测了发布版脚本:原样可正常 resolve,而读取 5 · 每一次拒绝都不写任何东西territory 规模的 plan、size 字段缺失或为 6 · 四条 Critical 修复在真实 macOS 文件系统上
7 · 这些测试真的守得住修复吗?我把每条修复逐一在源码里改回去再跑测试。10 / 10 变异体被杀——包括此前评审轮次点名"无人见证"的两条( 另外,此前提出的 8 · 未解决 thread 在当前 head 的状态
9 · 次要、不阻塞
10 · 本次验证没有覆盖的部分
|










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 wayagent-prompt --rosterdoes — 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 withparallel(), 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 absolutescriptPath.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
QWEN_CODE_PROJECT_DIRandQWEN_CODE_SESSION_ID), take any Step 1 plan for a small source change and runqwen review emit-workflow --plan <plan> [--rules <rules>]. Expected: stderr carries the same worktree-residue warningsagent-prompt --rosterwould print; stdout ends withscriptPath: <absolute path>under<project dir>/workflows/generated/review/<session>/qwen-review-<digest>.js. The script parses (node --check), opens with a pure-literalmeta, contains oneAGENTSentry per role the plan requires, and itscheck-coverageprompt records matchagent-prompt --roster's byte for byte.QWEN_CODE_PROJECT_DIR). Expected: refused before any brief or record is written, naming the missing env contract.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 wholesrc/commands/review/directory: 104 files, 5,119 passed, 15 skipped.resolveSavedWorkflowScript({scriptPath})loaded it,listSavedWorkflowsdid not list it, andcompileWorkflowScriptfrom the real workflow sandbox compiled it withmeta.name = review-step-3a.Evidence (Before & After)
N/A — new subcommand, nothing user-visible changes until the skill routes to it.
Tested on
Environment (optional)
Unit tests (vitest, Node 22) plus the built CLI run locally.
Risk & Scope
agent-promptbecome exported so the emitter shares the builder instead of copying it; no behavior change for existing callers.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_CODE_PROJECT_DIR和QWEN_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-coverageprompt 记录与agent-prompt --roster逐字节一致。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 跳过。resolveSavedWorkflowScript({scriptPath})成功加载,listSavedWorkflows没有列出它,真实 workflow 沙箱的compileWorkflowScript编译通过,meta.name = review-step-3a。证据(前后对比)
N/A —— 新子命令,在 skill 路由到它之前没有用户可见的变化。
测试平台
环境(可选)
单元测试(vitest,Node 22)加本地构建的 CLI 运行。
风险与范围
agent-prompt中的两个函数改为导出,以便 emitter 共享构建器而不是复制它;现有调用方行为不变。关联 Issue
Part of #8769 —— Phase 1 的 emitter 一半。基于 #9987(生成脚本根目录)、#8971 与 #8972。从 #8943 重新落地 emitter,去掉其路由和清理两部分。
https://claude.ai/code/session_017cUwuTey4APA8wAyAM6ScS