Skip to content

fix(review): fix silent reverse-audit retirement failures and keep non-converged evidence - #9213

Merged
wenshao merged 9 commits into
QwenLM:mainfrom
wenshao:fix/review-retirement-9206
Aug 16, 2026
Merged

fix(review): fix silent reverse-audit retirement failures and keep non-converged evidence#9213
wenshao merged 9 commits into
QwenLM:mainfrom
wenshao:fix/review-retirement-9206

Conversation

@wenshao

@wenshao wenshao commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Makes the reverse-audit loop's retirement failures observable, and fixes the receipt shape that broke retirement on a real run. Three behaviour changes: (1) the dry-receipt parser now accepts sentence punctuation — period, comma, semicolon, in either width — as the separator between the all-clear phrase and the clause that proves the walk; dashes and colons were the only separators before. (2) A chunk audited twice that neither retired nor proved hot now gets a stderr diagnostic naming the certification condition that failed, round by round; and the schedule's fallback names itself when it cannot read the audit history instead of silently auditing everything. (3) Post-review cleanup keeps the prompt-record directory of a run whose loop stopped without converging — the stop marker proves the non-convergence — instead of deleting the only evidence that could diagnose it. A converged run clears its marker, so its history sweeps as before.

Why it's needed

Fixes the run reported in #9206: four chunks returned substantive dry receipts in both rounds 1 and 2, yet rounds 3, 4 and 5 rebuilt auditors for all 12 chunks, no retirement: note appeared, nothing said which condition refused the receipts, and Step 9 cleanup then deleted the record directory — forensics impossible. Reproduced end to end: receipts separated by a period (No new issues were found. Re-walked …) or a full-width comma (未发现新问题,重新走查了…) failed the dry classification, and the refusal was indistinguishable from "nothing was certifiable" — the loop ran to its round cap paying full auditor cost on territories it had already certified.

Reviewer Test Plan

How to verify

  • Run the regression suites: cd packages/cli && npx vitest run src/commands/review/issue-9206-repro.test.ts src/commands/review/lib/retirement.test.ts src/commands/review/cleanup.test.ts src/commands/review/agent-prompt.test.ts — 323 tests green. The repro file encodes the issue's expectation as a disjunction (a twice-dry chunk must produce EITHER a retirement note OR a certification diagnostic); 4 of its 5 tests failed before this change, all 5 pass now.
  • Behaviour invariants to confirm: canonical dash-separated receipts still retire their chunks from round 3 on; the bare stock sentence No issues found. still does NOT retire (the clause substance floor rejects the empty clause even through the widened separator class); a return that files a finding still outranks any receipt; cleanup of a run with no stop marker still deletes everything.

Evidence (Before & After)

Before, round 3 with twice-dry chunks whose receipts used a period/full-width comma: 4 auditors required this round — one per chunk. and nothing else — byte-identical across rounds 3–5; cleanup printed Removed temp file: …-fetch-prompts, destroying the certification history.

After: rounds 3 and 5 print 2 auditors required this round with retirement: chunk N — retired: dry in rounds 1 and 2, next cold check round 4 (round 4 is the designed alternating cold check, which re-retires); uncertified receipts produce a stderr NOTE naming the failed bar per chunk and round (e.g. chunk 1 — round 1: receipt clause not substantive; round 2: receipt clause not substantive) plus a pointer to the record directory; a cap-hit run's cleanup prints Kept …-fetch-prompts: this review's reverse audit stopped without converging (stop marker on disk) — … and leaves the record directory intact.

Tested on

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

Environment (optional)

Unit tests via vitest; E2E verification drove the bundled CLI (node dist/cli.js review agent-prompt/cleanup) with synthetic harness-shaped transcripts — no model calls involved.

Risk & Scope

  • Main risk or tradeoff: the widened separator admits more return shapes as receipts. The bars that actually prove the walk — successful tool calls, a read of the diff, overlap with the chunk's baked territory, and the clause substance floor — are unchanged, a yield still outranks any receipt, and a retired chunk is cold-checked again on every even round, so a wrongly-granted retirement self-corrects within one round. Diagnostics print on stderr only; stdout stays the paste-verbatim deliverable.
  • Not validated / out of scope: the real PR feat(review): adopt a round-aware convergence posture for posted findings #9118 receipts are unrecoverable — that is the harm this PR removes; the two repro receipt shapes are representative stand-ins that reproduce the symptom byte for byte. One unrelated pre-existing test failure (stale-bundle.test.ts, a SKILL.md quote-parity fixture) also fails on unmodified HEAD and involves none of the changed files.
  • Breaking changes / migration notes: none.

Linked Issues

Fixes #9206

中文说明

本 PR 做了什么

让反向审计循环的退役(retirement)失败可观测,并修复了导致真实运行中退役失效的 receipt 形状。三个行为变更:(1) dry-receipt 解析器现在接受句子标点——句号、逗号、分号(半角与全角均可)——作为"无问题"短语与证明走查过程的从句之间的分隔符;此前只接受破折号与冒号。(2) 一个被审计两次、既未退役也未证明有问题的 chunk,现在会在 stderr 输出诊断,逐轮指出是哪个认证条件失败;当调度器无法读取审计历史时,其回退路径也会自报原因,而不是静默地审计一切。(3) 评审结束后的清理会保留"循环未收敛即停止"的运行的 prompt-record 目录(停止标记即未收敛的证明),不再删除唯一可用于诊断的证据。已收敛的运行会清除自己的标记,其历史记录照旧被清理。

为什么需要

修复 #9206 报告的运行:四个 chunk 在第 1、2 轮都返回了实质性的 dry receipt,但第 3、4、5 轮仍为全部 12 个 chunk 重建审计 agent,没有出现任何 retirement: 注解,也没有任何输出说明是哪个条件拒绝了这些 receipt,Step 9 清理随后删除了 record 目录——事后无法取证。已端到端复现:以句号分隔(No new issues were found. Re-walked …)或全角逗号分隔(未发现新问题,重新走查了…)的 receipt 无法通过 dry 分类,而这种拒绝与"没有任何可认证的 chunk"完全无法区分——循环一路跑到轮数上限,在早已认证完成的 territory 上支付全部审计成本。

审阅者测试计划

如何验证

  • 运行回归套件:cd packages/cli && npx vitest run src/commands/review/issue-9206-repro.test.ts src/commands/review/lib/retirement.test.ts src/commands/review/cleanup.test.ts src/commands/review/agent-prompt.test.ts——323 个测试全绿。repro 文件把 issue 的期望编码为析取式(两次 dry 的 chunk 必须产生退役注解或认证诊断之一);改动前 5 个测试中 4 个失败,现在 5 个全部通过。
  • 需确认的行为不变量:破折号分隔的规范 receipt 仍从第 3 轮起退役;裸模板句 No issues found. 仍然不会退役(从句实质性下限拒绝空从句,即使分隔符类已放宽);提交 finding 的返回仍然压过任何 receipt;没有停止标记的运行的清理仍然删除一切。

证据(前后对比)

改动前:对 receipt 使用句号/全角逗号的两次 dry chunk,第 3 轮输出 4 auditors required this round — one per chunk.,再无其他——第 3–5 轮逐字节相同;清理输出 Removed temp file: …-fetch-prompts,销毁认证历史。

改动后:第 3、5 轮输出 2 auditors required this roundretirement: chunk N — retired: dry in rounds 1 and 2, next cold check round 4(第 4 轮是设计中的交替冷检查,冷检查后重新退役);未通过认证的 receipt 会在 stderr 输出 NOTE,逐 chunk、逐轮指出失败的门槛(如 chunk 1 — round 1: receipt clause not substantive; round 2: receipt clause not substantive),并附 record 目录路径;触发轮数上限的运行的清理输出 Kept …-fetch-prompts: this review's reverse audit stopped without converging (stop marker on disk) — …,record 目录原样保留。

测试环境

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

环境(可选)

单元测试通过 vitest;E2E 验证以合成的 harness 形状 transcript 驱动打包后的 CLI(node dist/cli.js review agent-prompt/cleanup)——不涉及模型调用。

风险与范围

  • 主要风险或权衡:放宽分隔符会接受更多形状的返回作为 receipt。真正证明走查的门槛——成功的工具调用、对 diff 的读取、与 chunk 烘焙 territory 的重叠、从句实质性下限——均未改变;yield 仍然压过任何 receipt;且已退役的 chunk 每个偶数轮仍会冷检查,因此错误授予的退役会在一轮内自我纠正。诊断只输出到 stderr;stdout 保持逐字粘贴的交付物不变。
  • 未验证 / 范围外:PR feat(review): adopt a round-aware convergence posture for posted findings #9118 的真实 receipt 已不可恢复——这正是本 PR 要消除的危害;repro 中的两种 receipt 形状是代表性替身,可逐字节复现该症状。另有一个不相关的预存测试失败(stale-bundle.test.ts,SKILL.md 引文一致性 fixture)在未改动的 HEAD 上同样失败,与本 PR 改动的文件无关。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

Fixes #9206

…erged evidence (QwenLM#9206)

A round-5 reverse audit (PR QwenLM#9118, 12 chunks) never retired a chunk:
four territories returned substantive dry receipts in BOTH rounds 1 and
2, yet rounds 3-5 rebuilt all 12 auditors, no retirement note appeared,
and nothing anywhere said which certification condition refused the
receipts. Step 9 cleanup then deleted the prompt-record directory of
the non-converged run, so the failure could never be diagnosed.

Two root causes, reproduced end to end (issue-9206-repro.test.ts):

- The dry-receipt separator class admitted dashes and colons only, but
  honest receipts arrive separated by sentence punctuation too — "No
  new issues were found. Re-walked …" and "未发现新问题,重新走查了…"
  both reproduce the never-retire loop byte for byte. The clause after
  the separator is the part that proves the walk; the separator only
  has to show it exists. Widen the class to period, comma and
  semicolon in either width. The substance floor, the tool-call bars
  and the territory bar are unchanged — the bare stock sentence still
  reads unknown, and a yield still outranks any receipt.

- Every certification refusal landed in the same silent unknown, and
  the schedule's catch swallowed every exception without a word.
  classifyReturn now reports the first bar that fell; the schedule
  carries one diagnostic line per twice-audited chunk that neither
  retired nor yielded, and the builder prints them on stderr (stdout
  stays the deliverable). The catch names itself: a round whose
  transcripts cannot be read audits every chunk AND says so.

And the evidence half: cleanup swept the record directory of a
non-converged run unconditionally — the round-cap marker that proves
the non-convergence sat inside the very directory deleted. A record
directory holding a same-run stop marker is now kept, with a note
naming it; a converged run clears its marker, so its history sweeps
as before.
@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Aug 15, 2026
@wenshao

wenshao commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

E2E test report (reproduction + post-fix verification)

Both phases ran headless, no model calls — the deterministic review agent-prompt / review cleanup subcommands driven with harness-shaped synthetic subagent transcripts (artifacts under /tmp/issue-9206-e2e/; regression pins in packages/cli/src/commands/review/issue-9206-repro.test.ts).

Reproduction (unfixed code, STATUS: REPRODUCED). Simulated the reported shape: 4 chunks, rounds 1–2 where two chunks return substantive dry receipts (verbatim delivery, successful tool calls, baked-territory diff reads) while two file findings. With an English receipt separated by a period (No new issues were found. Re-walked …) or a Chinese receipt separated by a full-width comma (未发现新问题,重新走查了…), rounds 3–5 each printed 4 auditors required this round — one per chunk. — zero retirement: notes, zero diagnostics, byte-for-byte the reported symptom. Control with the canonical dash receipt retired both cold chunks at round 3 (wiring sound). Round 6 hit the 5-round cap (exit 4, round-cap marker written inside the record dir); cleanup then printed Removed temp file: …-fetch-prompts and deleted the entire certification history.

Verification (fixed code, STATUS: VERIFIED) — against the rebuilt bundle, confirmed to contain the fix strings before use:

  1. Retirement fires. Both variant receipt shapes now print 2 auditors required this round in rounds 3 and 5 with chunk N — retired: dry in rounds 1 and 2, next cold check round 4; round 4's full fan-out is the designed alternating cold check, which re-retires. Byte-identical to the canonical control.
  2. Failures are named. Bare No issues found. still does not retire, but each round now emits a stderr NOTE per twice-audited chunk naming the failed bar per round (chunk 1 — round 1: receipt clause not substantive; round 2: receipt clause not substantive) plus a pointer to the record dir; the scheduler's catch prints NOTE: reverse-audit retirement unavailable this round — <error> — auditing every chunk. instead of swallowing exceptions. Unit tests pin the remaining bars (no matching transcript, no successful tool calls, no read of the diff, territory read missing, receipt not matched).
  3. Evidence survives. Cap-hit run's cleanup printed Kept …-fetch-prompts: this review's reverse audit stopped without converging (stop marker on disk) — … and left all 34 entries (prompts, briefs, round stamps, stop marker) on disk; a converged/marker-less run still sweeps as before.

Regression: repro suite 1/5 → 5/5; retirement 53 ✓, cleanup 29 ✓, agent-prompt 236 ✓; full review dir 2538/2539 (the single failure, stale-bundle.test.ts, also fails on unmodified HEAD — verified in a throwaway worktree, unrelated). Typecheck and ESLint clean.

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

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

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Re-run after the takeover rounds — this pass reviews 6d5b5e1a (round 8, critical-only fixes).

Template looks good ✓

Problem: Observed bug with full forensics. #9206 reports a real run (review of PR #9118, 12 chunks) where four twice-dry chunks never retired across rounds 3–5, no retirement: note ever appeared, and Step 9 cleanup deleted the record directory before the refusal could be diagnosed. Not theoretical: the issue names the run, the cost (≈12 auditor-runs re-walking provably cold territory), and the missing evidence.

Direction: Aligned. This repairs the retirement mechanism CHANGELOG entry #8498 shipped ("retiring dry chunks … for large pull requests") — the optimization exists exactly for the budgeted large-diff runs where it stopped firing, and the evidence destruction made the failure undiagnosable. Fixing both halves (observable retirement failures + preserved evidence) is the minimum that makes the mechanism trustworthy.

Size: Not applicable — all 11 files live in packages/cli/src/commands/review/; no core-module paths, single package. For scale: ~739 production lines vs ~1657 test lines.

Approach: The scope is larger than the issue's three asks (widen the separator class, name the failed certification bar, keep the record directory), but every increment traces to a leak shape the review rounds found and pinned — the line-spanning \s match, the fused Layer walked: label, hedge markers inside the clause, case-folded parroting. Each new refusal fails toward audit (the chunk stays hot), the module's declared failure direction. No drive-by changes spotted; the open Suggestions were deferred to #9259 under the post-round-5 critical-only policy instead of growing this diff further.

Risk: No high-risk paths matched (Stage 1e). The touched surface — the dry-receipt classifier and the reverse-audit scheduler — is fail-safe by construction: a classifier error costs an extra audit, never a skipped one.

Moving on to code review. 🔍

中文说明

接手轮次后的重新运行——本次审查 6d5b5e1a(第 8 轮,仅修 Critical)。

模板完整 ✓

问题:已观测到的 bug,取证完整。#9206 报告了一次真实运行(审查 PR #9118,12 chunks):4 个连续两轮干回执的 chunk 在第 3–5 轮始终未退役,任何 retirement: 注记都未出现,且 Step 9 清理在诊断前删除了 record 目录。不是理论问题:issue 写明了运行、代价(约 12 个审计员-run 重走已证冰冷的领地)与被销毁的证据。

方向:对齐。这修复的是 CHANGELOG 中 #8498 引入的退役机制("为大 PR 退役干 chunk……")——该优化恰恰为预算型大 diff 运行而生,却在这类运行上失效,且证据销毁使失败无法诊断。两半都修(退役失败可观测 + 证据保留),才能让该机制可信。

规模:不适用——全部 11 个文件都在 packages/cli/src/commands/review/ 内,无核心模块路径,单包。体量参考:生产代码约 739 行,测试约 1657 行。

方案:范围大于 issue 的三项诉求(放宽分隔符类、点名失败的认证条件、保留 record 目录),但每一处增量都能追溯到审查轮次发现并钉住的泄漏形状——跨行 \s 匹配、融合的 Layer walked: 标签、从句内的转折标记、大小写折叠的复读。每个新拒绝都倒向审计(chunk 保持热),即该模块声明的失败方向。未发现夹带改动;未决 Suggestion 按第 5 轮后的"只修 Critical"策略延期至 #9259,没有继续膨胀本 diff。

风险:未命中高风险路径(Stage 1e)。所触表面——干回执分类器与反向审计调度器——构造上即失败安全:分类器出错只会多花一次审计,绝不会跳过审计。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review

Before reading the diff, my independent proposal for #9206 was narrower: widen the separator class in the dry-receipt regex, return a failure reason from the classifier and print it on stderr, keep the record directory when a stop marker is present. The PR does all three, then goes further — and the further parts are earned, not speculative: the anchored, line-scoped receipt form (clause ends at the line, every whitespace element line-bound) closes a real leak in the old [\s\S]* capture — a matcher that spanned lines and borrowed its clause from the next one. The negation-marker and walk-verb tests close the hedge class the widened separator would otherwise have opened wider. The readBudgetStopUnfenced / fenced-reader split gives cleanup retention its own eyes without loosening the run-epoch fence the verdict side reads through, and clearBudgetStop unlinking only this run's marker is the matching half.

What I checked specifically:

  • Failure direction: every new refusal returns unknown with a named bar — no path retires a chunk on weaker evidence than before. receipt not alone means the widened separator admits more shapes only inside the exact one-line form the updated brief now mandates, so the parser and the prompt stay one contract.
  • Per-chunk build path: the diagnostic is correctly de-gated from the round stamp (a one-auditor-at-a-time round stamps on its first chunk build; chunks 2..N used to stay silent — the exact never-retire shape), while the converged refusal, the admission gate, and the topology note stay gated on !roundAdmitted, so stamped rebuilds behave as before plus the note.
  • Cleanup retention: hasPreviousRunRecords is an existential check with per-entry try/catch, so one broken symlink can't veto the older evidence; an unstatable plan reads -Infinity and a directory that survived one cleanup survives the next, until removed manually as the Kept line instructs. Converged runs clear their own marker and sweep as before.
  • Reuse: the layer-line strip reuses audit-layers' own LAYER_RECEIPT_LINE_RE, the inline cut mirrors the INLINE_BUDGET_GAP_RE precedent, and the NOTE writer is shared between the round builder and the per-chunk path so the spelling can't drift.

No critical findings. The standing Suggestions from the review ledger (negation-vocabulary residue, strip fence-awareness, test-label hygiene) are collected in #9259 — none blocks this change.

Test evidence — the PR's own CI on the reviewed commit, fetched via API (per policy this gate does not run PR code). No check on this head failed: 16 checks green, the rest conditional skips plus route orchestration jobs reading cancelled, their normal terminal state (the same pattern appears on merged PR heads).

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

The three skipped test jobs are pre-existing CI routing, not this PR's doing — the same checks read skipped on the head of recently merged PR #9252. The Linux unit suite passing on this commit is the verified test evidence; the author's local numbers (323 tests green across the four suites, with 4 of 5 repro tests failing before the change) are the author's claim, not independently re-run here.

Sandboxed verification would settle the behavioural remainder: @qwen-code /verify — that the widened form actually retires the #9206 receipt shapes while the executed leak families stay hot is asserted by tests this gate can read but not execute. A sponsored run is already in flight on this head (live progress); its report will land on this PR and should be read as the settling evidence.

Real-scenario testing: N/A on this CI path — nothing user-visible in a TUI sense; the change lives in the review orchestrator's stderr diagnostics, brief text, and cleanup output.

中文说明

代码审查

读 diff 之前,我对 #9206 的独立方案更窄:放宽干回执正则的分隔符类、让分类器返回失败原因并打到 stderr、存在停止标记时保留 record 目录。PR 做到了这三点,并走得更远——而多出的部分是有据的,不是推测:锚定、行作用域的回执形式(从句止于行尾、所有空白元素行绑定)堵住了旧 [\s\S]* 捕获的真实泄漏——匹配器跨行、从下一行借从句。否定标记与走查动词测试收住了放宽分隔符否则会敞开的 hedge 类。readBudgetStopUnfenced 与围栏读法的拆分,让清理保留判断有自己的"眼睛",又不松动裁决侧读取的 run-epoch 围栏;clearBudgetStop 只解本 run 的标记是配套的另一半。

具体核对:

  • 失败方向:每个新拒绝都带名返回 unknown——没有任何路径以比之前更弱的证据退役 chunk。receipt not alone 意味着放宽的分隔符只在更新后 brief 强制的单行形式内接纳更多形状,解析器与提示词保持同一契约。
  • 逐 chunk 构建路径:诊断与轮次戳记正确解耦(逐个构建审计员的轮次在第一个 chunk 构建时盖戳,chunk 2..N 过去因此静默——恰是"永不退役"的形状),而已收敛拒绝、准入门与拓扑注记仍以 !roundAdmitted 为门,盖章重建行为不变、只多一条 NOTE。
  • 清理保留hasPreviousRunRecords 是存在性判断且逐条目 try/catch,单个坏符号链接不能否决更旧的证据;无法 stat 的 plan 读作 -Infinity,躲过一次清理的目录下次仍存活,直到按 Kept 行指引手动移除。已收敛运行清除自己的标记,照常清扫。
  • 复用:layer 行剥离复用 audit-layers 自己的 LAYER_RECEIPT_LINE_RE,内联截断沿用 INLINE_BUDGET_GAP_RE 先例,NOTE 写入器在轮次构建器与逐 chunk 路径间共享,拼写不会漂移。

无 Critical 发现。审查台账遗留的 Suggestion(否定词表残留、剥离的围栏感知、测试标签卫生)已汇总至 #9259——均不阻断本变更。

测试证据——被审查提交上 PR 自己的 CI,经 API 获取(按策略,本 gate 不运行 PR 代码)。该提交无任何失败检查:16 项绿色,其余为条件性跳过与读作 cancelledroute 编排作业(其正常终态,已合并 PR 的 head 上同样可见)。三个跳过的测试作业为既有 CI 路由,非本 PR 所致——近期已合并的 #9252 head 上同样 skipped。Linux 单测套件在该提交上通过是已验证的测试证据;作者的本地数字(四个套件 323 全绿、复现测试改前 5 中 4 失败)为作者声明,未在此独立复跑。沙箱验证将落定剩余行为问题:一次 @qwen-code /verify 运行已在该 head 上在途,报告将发布在本 PR,应作为落定证据阅读。真实场景测试:CI 路径不适用——无 TUI 意义上的用户可见面,改动位于 review 编排器的 stderr 诊断、brief 文本与清理输出。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean re-run on the round-8 head; the two residual reservations are non-blocking (the cross-platform test matrix skipped per the repo's standing routing, and the deferred Suggestions now living in #9259).

Stepping back: my independent proposal was a ~250-line version of this fix, and the PR's extra production scope is not creep — each increment closes a leak shape the eight review rounds actually found and pinned (line-spanning match, fused layer label, hedge-in-clause, case-folded parrot), with the failure direction uniformly toward audit. The issue's three asks are all met: the separator widening retires the period / full-width-comma receipts #9206 reproduced, twice-audited chunks that fail certification now name the failed bar on stderr round by round, and a non-converged run's record directory survives cleanup. The code will read honestly in six months — the classifier's doc comments are dense, but that is this module's established style, and they carry the constraints (the polarity guard, the failure direction) a future editor must not break. Every change in the diff is load-bearing for the stated goal; the rest went to #9259 instead of bloating this PR.

CI is green on the reviewed head with no pull_request runs pending — approving now rather than deferring. The standing CHANGES_REQUESTED votes are this bot's own earlier review-round gates on superseded commits; round 6 found no Criticals and the round-8 fixes are test-pinned, so this approval supersedes them. The in-flight /verify run will report the behavioural A/B separately; if it surfaces something, that report is the place to act on it. 🚀

中文说明

置信度:4/5——第 8 轮 head 上的干净复审;剩余两点保留均为非阻断(跨平台测试矩阵按仓库既有路由跳过;延期 Suggestion 已收在 #9259)。

退一步看:我的独立方案约 250 行,PR 多出的生产范围不是膨胀——每一处增量都关闭了八轮审查真实发现并钉住的泄漏形状(跨行匹配、融合 layer 标签、从句内 hedge、大小写折叠复读),失败方向一致倒向审计。issue 三项诉求全部满足:分隔符放宽使 #9206 复现的句号/全角逗号回执得以退役;两次审计却未通过认证的 chunk 现在逐轮在 stderr 点名失败条件;未收敛运行的 record 目录在清理后保留。代码六个月后依然可读——分类器注释密集,但这是该模块既有风格,承载未来修改者不可破坏的约束(极性守卫、失败方向)。diff 中每处改动都对所述目标不可或缺,其余进了 #9259 而非膨胀本 PR。

审查 head 上 CI 全绿且无 pending 的 pull_request 运行——现在批准,不再等待。既有的 CHANGES_REQUESTED 是本 bot 早前审查轮次在已被取代提交上的门;第 6 轮起无 Critical,第 8 轮修复均有测试钉住,本批准予以取代。在途的 /verify 运行将另行报告行为 A/B;若发现问题,以该报告为处置依据。🚀

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/cleanup.ts Outdated
Comment thread packages/cli/src/commands/review/agent-prompt.ts Outdated
Comment thread packages/cli/src/commands/review/agent-prompt.ts Outdated
Comment thread packages/cli/src/commands/review/issue-9206-repro.test.ts Outdated
Comment thread packages/cli/src/commands/review/agent-prompt.ts Outdated
Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/cleanup.ts Outdated
@wenshao

wenshao commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

中文说明

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

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

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

Copy link
Copy Markdown
Collaborator

⚠️ AutoFix round 6 ended without publishing a reportview run.

中文说明

⚠️ AutoFix 第 6 轮结束但未发布报告 —— 查看运行

…wenLM#9213)

The widened receipt separator admitted clauses that contradict the
no-issues phrase ("…found, but I could not open the files") and let a
quoted phrase open a clause out of its own negation, retiring chunks on
their auditor's admission that nothing was checked. Judge polarity on
every separator path and admit sentence-punctuation separators only
when the phrase leads the return.

Cleanup's retention keyed on the run-epoch-fenced marker reader, so a
previous run's preserved evidence was swept by the next run — and
clearBudgetStop unlinked any marker, including a previous run's that
retention had just kept. Retention now reads the marker unfenced and
also keeps record directories carrying files older than the plan's own
capture (a killed run leaves no marker); convergence clears only its
own run's marker.

The per-chunk build path now prints its chunk's certification-failure
diagnostics like the round builder does.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Review round summary — PR #9213

Round 1 feedback: 2 Critical + 7 Suggestion findings. Eight findings are
resolved in code this round (commit 823281205c); one Suggestion is
explicitly deferred to keep the batch bounded (see its thread reply). No
conflicts — --conflict false, no merge performed.

Every defect claim was reproduced against the pre-round code first: the
nine new tests for the resolved findings FAILED on the pre-round tree
(4 retirement + 2 deadline + 2 cleanup-retention + 1 per-chunk
diagnostic), then pass after the fix. The coverage-only additions
(separator table, bar diagnostics, per-chunk catch test) pass on both,
as expected.

Critical findings

R1-1 — widened separator admits self-contradicting receipts as dry (rc:3789181830) — RESOLVED

Reproduced: No new issues were found, but I could not open the generated files and did not check them. (and the quoted-phrase negation shape) read
dry on the pre-round tree and retired the chunk — four failing tests
confirmed it. substantiveClause measured length/objects only, never
polarity.

Fix, both halves of the suggested shape:

  • A polarity/contrast guard (CONTRAST_RE) in substantiveClause refuses
    clauses led by or containing but / however / although / except /
    但是 / 不过 / 然而. It sits in the shared clause judge, so it covers
    the pre-existing dash/colon paths as well as the widened ones.
  • The sentence-punctuation separators ([.,;。,;]) now open a clause only
    when the phrase LEADS the return (DRY_RECEIPT_START_RE, start-anchored);
    the dash/colon/hyphen class keeps matching anywhere, unchanged. A
    quotation of the phrase inside a negation can therefore no longer borrow
    the receipt's clause.

The guard fails toward auditing (a refused clause reads unknown, the
chunk stays hot), the module's documented failure direction. Nine tests
pin it: four hedge/negation shapes (en comma-led, dash-led, zh, quoted
negation) plus the existing receipt suite, all green.

R1-2 — retention is same-run scoped; a previous run's preserved evidence is deleted by a later run (rc:3789181831) — RESOLVED

Reproduced both mechanisms on the pre-round tree (3 failing tests):
(a) a CI retry re-captures the plan at the same path, run A's marker fails
the run-epoch fence inside readBudgetStop, preserved stays empty and
the sweep deletes run A's history with no Kept line; (b) a converged
re-review's refuseConvergedclearBudgetStop unlinked run A's marker
unconditionally, so the NEXT sweep took the directory.

Fix, exactly the suggested shape:

  • deadline.ts now exports readBudgetStopUnfenced (shape-checked, never
    fenced) and cleanup's retention keys on it: a marker on disk from ANY
    run is a stop that never converged, and here a previous run's marker is
    the evidence to keep. The fenced readBudgetStop remains the verdict
    consumers' reader (compose-review, coverage unchanged).
  • clearBudgetStop is run-epoch aware: it removes only THIS run's marker
    (a previous run's — and an undateable — marker stays), matching the
    "same-run refusal" contract refuseConverged's docstring already
    claimed.
  • runEpochMs is exported so cleanup and deadline share one fence
    definition.

Suggestions

R1-3 — per-chunk build path discards schedule.diagnostics (rc:3789181833) — RESOLVED

Reproduced: a round-3 --chunk 13 build over twice-uncertified history
printed nothing pre-round. The NOTE is now shared by both paths through
noteUncertifiedChunks (one spelling, two call sites); the per-chunk path
filters the schedule's diagnostics to chunk <id> — lines. Test pins the
exact diagnostic line on stderr and that the block still builds.

R1-4 — degrade-catch diagnostic can EPIPE-crash when stderr is gone (rc:3789181837) — DEFERRED (batch bound)

Not resolved this round: this round implements 8 of the 9 findings — both
Criticals first — and the batch bound defers the remainder. This finding
is independent of the others (its fix — a one-time process.stderr
'error' guard in both degrade catches — touches no code this round
changes), and it is recorded in its thread reply so it is not silently
dropped.

R1-5 — recordOf hand-rolls the record-directory scan (rc:3789181839) — RESOLVED

Swapped to readRecordedPrompts(plan) with the reverse-audit--chunk-…
key-prefix match, exactly as the sibling harness in agent-prompt.test.ts
does; the now-unused readdirSync/readFileSync imports are gone.

R1-6 — no test asserts the --chunk catch-path diagnostic (rc:3789181840) — RESOLVED

Added the mirror of the all-chunks test: rounds 1–2 built, session id
deleted, { role: 'reverse-audit', chunk: 13, round: 3 } — asserts stderr
carries reverse-audit retirement unavailable this round / auditing the chunk. while stdout still builds the block and the record lands.

R1-7 — four of the seven new CertificationFailure names have no test (rc:3789181841) — RESOLVED

Four retirement.test.ts cases, each asserting diagnostics equals the
exact chunk 13 — round 1: <name>; round 2: <name> line: no read of the diff, territory read missing, receipt clause not substantive, and
launch matched multiple records (two same-round records + one transcript
handed both blocks).

R1-8 — six admitted separators, only two tested (rc:3789181843) — RESOLVED

An it.each table adds the four untested separators (ASCII comma, ASCII
semicolon, full-width period, full-width semicolon) with the same
dry-passing assertions as the period/full-width-comma cases. All receipts
lead their return, so they also exercise the new start-anchored path.

R1-9 — retention keys solely on the stop marker; killed runs leave none (rc:3789181844) — RESOLVED

Reproduced: a marker-less, backdated record directory was swept on the
pre-round tree. Retention now ALSO preserves a -prompts directory that
holds files older than the plan's own capture — records the current run
cannot have written (every run rewrites the plan at Step 1; nothing
clears the record dir). The comment block is rewritten to name both
signals and the marker's limit (only refusals write one), and the Kept
line no longer claims a marker is on disk.

Verification

All commands actually run, in order:

  • Pre-fix RED probes (reproduction):
    • npx vitest run src/commands/review/lib/retirement.test.ts — 4 failed | 61 passed (the four hedge/negation receipts retired pre-fix)
    • npx vitest run src/commands/review/lib/deadline.test.ts src/commands/review/issue-9206-repro.test.ts — 4 failed | 54 passed (marked retry swept, marker-less dir swept, clearBudgetStop unfenced)
    • npx vitest run src/commands/review/agent-prompt.test.ts — 1 failed | 237 passed (per-chunk path printed no diagnostic)
  • Post-fix GREEN runs:
    • the same five suites together (retirement, deadline, issue-9206-repro, cleanup, agent-prompt) — 5 files, 390 passed
    • npx vitest run src/commands/review (whole review directory, packages/cli) — 72 files, 2558 passed | 4 skipped, 0 failed (run twice: after the fix and after formatting)
  • npm run build — passed
  • npm run typecheck — passed (0 error TS after build; an initial run before npm run build showed only TS6305 stale-dist errors, cleared by the build)
  • npm run lint — passed (ESLint over . and integration-tests)
  • npx prettier --check on the nine touched files — passed (two files were formatted with prettier --write first; suites re-run green afterwards)

Integration tests were not run: the touched behavior is fully exercised by
the focused Vitest suites above (the handler-level harnesses this PR
already uses), not only through the bundled CLI.

中文说明

评审轮次总结 — PR #9213

第 1 轮反馈:2 个严重(Critical)+ 7 个建议(Suggestion)。本轮以代码解决了其中八个
(提交 823281205c);为控制单轮批量规模,显式推迟了一个建议(见该主题下的回复)。
无冲突 — --conflict false,未执行任何合并。

所有"行为错误"的指控都先在改动前的代码上复现:为已解决发现新增的九个测试在改动前的
代码树上失败(retirement 4 个 + deadline 2 个 + cleanup 保留 2 个 + per-chunk 诊断 1 个),
修复后全部通过。纯覆盖类新增(分隔符表、门槛诊断、per-chunk catch 测试)在改动前后都通过,
符合预期。

严重(Critical)发现

R1-1 — 放宽后的分隔符把自相矛盾的 receipt 判为 dry(rc:3789181830)— 已解决

已复现:No new issues were found, but I could not open the generated files and did not check them.(以及引用模板句加以否定的形态)在改动前的代码上被判为
dry 并使 chunk 退役 — 四个失败测试证实了这一点。substantiveClause 只衡量长度/对象,
从不判断极性。

修复,按建议修复的两个方面:

  • substantiveClause 中加入极性/转折守卫(CONTRAST_RE),拒绝以 but / however /
    although / except / 但是 / 不过 / 然而 开头或包含它们的从句。它位于共享的从句
    判定函数中,因此既覆盖新增的分隔符路径,也覆盖已有的破折号/冒号路径。
  • 句读分隔符([.,;。,;])现在只有当短语位于返回开头时才开启从句
    DRY_RECEIPT_START_RE,起始锚定);破折号/冒号/连字符类保持不变,仍可在任意位置匹配。
    于是引号内的短语在否定句中再也无法借用 receipt 的从句。

该守卫朝"继续审计"的方向失败(被拒的从句读作 unknown,chunk 保持热状态),
即本模块文档声明的失败方向。九个测试钉住了它:四个 hedge/否定形态(英文逗号引导、
破折号引导、中文、引用否定)加上既有的 receipt 套件,全部通过。

R1-2 — 保留判断按同一次运行限定范围;上一次运行保留的证据会被后续运行删除(rc:3789181831)— 已解决

在改动前的代码树上复现了两种机制(3 个失败测试):(a) CI 重试在同一路径重新捕获计划,
运行 A 的标记过不了 readBudgetStop 内部的运行纪元围栏,preserved 为空,清扫直接删除
运行 A 的历史且没有任何 Kept 输出;(b) 一次收敛的重新评审经由 refuseConverged
clearBudgetStop 无条件删除了运行 A 的标记,于是下一次清扫删除了该目录。

修复,完全按建议的形态:

  • deadline.ts 现在导出 readBudgetStopUnfenced(只做形状校验、不带围栏),cleanup 的
    保留判断改为基于它:磁盘上任何一次运行留下的标记都代表一次从未收敛的停止,而在这里,
    上一次运行的标记恰恰是要保留的证据。带围栏的 readBudgetStop 仍是裁决消费者的读取器
    (compose-review、coverage 不变)。
  • clearBudgetStop 感知运行纪元:只删除本次运行自己的标记(上一次运行的标记 — 以及无法
    判定日期的标记 — 保留),与 refuseConverged 文档字符串本来就声称的"同一次运行的拒绝"
    契约一致。
  • 导出 runEpochMs,使 cleanup 与 deadline 共享同一个围栏定义。

建议(Suggestion)

R1-3 — 按 chunk 构建的路径丢弃了 schedule.diagnostics(rc:3789181833)— 已解决

已复现:在两次审计均未通过认证的历史上,改动前的第 3 轮 --chunk 13 构建没有任何输出。
现在该 NOTE 通过 noteUncertifiedChunks 由两条路径共享(一份文本、两个调用点);
per-chunk 路径将 schedule 的诊断过滤为 chunk <id> — 行。测试钉住了 stderr 上的精确
诊断行,且块仍能构建成功。

R1-4 — stderr 不可用时,降级 catch 的诊断可能导致 EPIPE 崩溃(rc:3789181837)— 推迟(批量限制)

本轮未解决:本轮实现了 9 个发现中的 8 个 — 两个 Critical 优先 — 批量上限推迟了其余部分。
该发现与其他发现相互独立(其修复 — 在两个降级 catch 中对 process.stderr 做一次性
'error' 守卫 — 不涉及本轮改动的任何代码),并已记录在其主题回复中,不会被静默丢弃。

R1-5 — recordOf 手写了 record 目录扫描(rc:3789181839)— 已解决

改为调用 readRecordedPrompts(plan) 并匹配 reverse-audit--chunk-… 键前缀,
与 agent-prompt.test.ts 中姊妹 harness 的做法完全一致;不再使用的
readdirSync/readFileSync 导入已移除。

R1-6 — 没有测试断言 --chunk catch 路径的诊断(rc:3789181840)— 已解决

新增了 all-chunks 测试的镜像:构建第 1–2 轮后删除 session id,以
{ role: 'reverse-audit', chunk: 13, round: 3 } 调用 — 断言 stderr 包含
reverse-audit retirement unavailable this round / auditing the chunk.
同时 stdout 仍构建出块且 record 落盘。

R1-7 — 七个新增 CertificationFailure 名称中有四个没有测试(rc:3789181841)— 已解决

在 retirement.test.ts 中新增四个用例,各自断言 diagnostics 精确等于
chunk 13 — round 1: <name>; round 2: <name> 行:no read of the diff
territory read missingreceipt clause not substantive,以及
launch matched multiple records(同轮两条 record + 一个同时拿到两个块的 transcript)。

R1-8 — 接受六个分隔符却只测了两个(rc:3789181843)— 已解决

用一张 it.each 表补上四个未测分隔符(半角逗号、半角分号、全角句号、全角分号),
断言与句号/全角逗号用例相同。所有 receipt 都位于返回开头,因此同时也覆盖了新的
起始锚定路径。

R1-9 — 保留判断只依赖停止标记;被杀的运行不留标记(rc:3789181844)— 已解决

已复现:改动前的代码树上,无标记、时间回拨的 record 目录被清扫。保留判断现在还会保留
持有早于计划自身捕获时间的文件的 -prompts 目录 — 即当前运行不可能写出的 record
(每次运行都在 Step 1 重写计划;record 目录从不清理)。注释块已重写,同时说明两个信号
及标记的局限(只有拒绝分支才写标记),Kept 输出不再声称磁盘上有标记。

验证

实际执行的全部命令,按顺序:

  • 修复前的 RED 探针(复现):
    • npx vitest run src/commands/review/lib/retirement.test.ts — 4 失败 | 61 通过(四个 hedge/否定 receipt 在修复前会退役)
    • npx vitest run src/commands/review/lib/deadline.test.ts src/commands/review/issue-9206-repro.test.ts — 4 失败 | 54 通过(带标记的重试被清扫、无标记目录被清扫、clearBudgetStop 无围栏)
    • npx vitest run src/commands/review/agent-prompt.test.ts — 1 失败 | 237 通过(per-chunk 路径无诊断输出)
  • 修复后的 GREEN 运行:
    • 上述五个套件合并运行(retirementdeadlineissue-9206-reprocleanupagent-prompt)— 5 个文件,390 通过
    • npx vitest run src/commands/review(packages/cli 下整个 review 目录)— 72 个文件,2558 通过 | 4 跳过,0 失败(运行两次:修复后与格式化后)
  • npm run build — 通过
  • npm run typecheck — 通过(构建后 0 个 error TSnpm run build 之前的首次运行只出现 TS6305 过期 dist 错误,构建后消失)
  • npm run lint — 通过(ESLint 覆盖 .integration-tests
  • 对九个改动文件执行 npx prettier --check — 通过(其中两个文件先以 prettier --write 格式化;之后重新运行套件仍全绿)

未运行集成测试:改动的行为已由上面的聚焦 Vitest 套件(即本 PR 已使用的 handler 级
harness)完整覆盖,而非只能通过打包后的 CLI 验证。

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

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


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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

中文说明

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

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

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

Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/cleanup.ts
Comment thread packages/cli/src/commands/review/cleanup.ts Outdated
Comment thread packages/cli/src/commands/review/issue-9206-repro.test.ts
Comment thread packages/cli/src/commands/review/cleanup.ts
Comment thread packages/cli/src/commands/review/cleanup.ts
Comment thread packages/cli/src/commands/review/cleanup.ts Outdated
Comment thread packages/cli/src/commands/review/issue-9206-repro.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
…wenLM#9213)

The per-chunk gate stamped the round on its first chunk build and
gated the certification diagnostics behind that stamp, so chunks 2..N
of a one-auditor-at-a-time round re-audited in the exact silence the
note exists to end. Print the chunk's diagnostic on every build of the
round; only the convergence and budget rulings stay gated on
admission.

The polarity guard enumerated contrast words over unbounded prose, and
prose has no last hedge — though/Yet/unfortunately/只是 all retired
chunks on clauses admitting nothing was checked, while a hedge in the
phrase's filler never reached the clause-only test at all. Invert the
burden: a clause carrying a negation or incapacity marker contradicts
the phrase whatever its length, and a contrast word without one
contradicts nothing — an innocuous "but I re-verified" retires again,
and the guard's remaining gaps land on the audit side.

The anchored receipt matcher now leads (the unanchored one truncated
clauses at nested phrase occurrences), the judged text is re-trimmed
after the budget-gap strip, and cleanup keeps a record directory whose
plan a previous cleanup already swept and skips unstatable entries one
by one instead of letting one veto the previous-run evidence.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Review round summary — PR #9213

Round 3 addressed 9 of the 15 round-2 findings — both Criticals first, per
the per-round batch bound (~8 findings). Every addressed claim was
reproduced on the pre-round code before implementing (the new tests
failed there), and the fixes turn them green. Six findings are deferred to
the next round with replies on their threads. No base-conflict work
(--conflict false).

Critical

  • R2-1 — per-chunk diagnostics printed only on the round's FIRST chunk
    build
    (agent-prompt.ts): RESOLVED. The stamp gate that exempts
    repairs also gated the diagnostic block, so chunks 2..N of a
    one-auditor-at-a-time round re-audited in silence. The schedule read is
    now run on EVERY per-chunk build (read-only) and the chunk's own
    diagnostic prints stamped or not; the convergence and budget rulings stay
    gated on admission, and the "retirement unavailable" catch-NOTE stays
    with unstamped builds (the round's admission build already named that
    degradation — re-printing it on every repair would be noise; the pinned
    repair-exemption contract of zero stderr stands). New test builds TWO
    uncertified chunks in the same round and asserts each build's stderr
    carries its own chunk N — … line; it failed pre-round (chunk 14's
    stderr was empty).
  • R2-2 — polarity guard leaked in two executed directions
    (retirement.ts): RESOLVED, together with R2-6, by closing the class
    instead of extending the enumeration. The contrast-word list is gone
    from the clause test; the burden is inverted — a clause carrying ANY
    negation/incapacity marker (not, n't, never, no, cannot,
    未/没/无法) reads unknown, and a contrast word WITHOUT one
    contradicts nothing. The brief's own all-clear vocabulary (no gaps,
    未发现问题 — the shapes DRY_RECEIPT_PHRASE names) is stripped before
    the marker test so a walk narrated in the brief's words is not refused
    by the marker that catches could not open the files. The filler-position
    leak is closed by testing the contrast guard on the receipt's LEAD
    (phrase + filler), which the clause-only test never saw. All six executed
    witness shapes are pinned by a new it.each (they retired the chunk
    pre-round); the control direction (listed but/但是 clauses still refused)
    stays green. Remaining marker-list gaps land on the audit side — the
    direction the module declares safe.

Suggestions resolved

  • R2-6 — innocuous "but"/不过 regressed dry→unknown (retirement.ts):
    RESOLVED by the same guard inversion; two new tests pin the honest
    "already covered them, but I re-verified" receipts retiring again in EN
    and zh.
  • R2-3 — mtime-only retention died on a second bare cleanup
    (cleanup.ts): RESOLVED with the suggested condition — a record
    directory whose plan file is GONE is retained (the plan-missing shape is
    what the previous cleanup's Kept left behind). New real-fs test: first
    cleanup keeps the dir and sweeps the plan; second cleanup keeps it again.
    Failed pre-round (dir deleted).
  • R2-10 — one unstatable entry vetoed the whole previous-run scan
    (cleanup.ts): RESOLVED with the suggested per-entry try/catch. New
    real-fs test plants a broken symlink ahead of an older record; retention
    survives it. Failed pre-round.
  • R2-4 — negative direction of hasPreviousRunRecords untested:
    RESOLVED as part of R2-10's coverage — a new real-fs test writes records
    NEWER than the plan and asserts the directory is swept (pins the <
    comparison against a !== mutant).
  • R2-9 — unanchored matcher truncated clauses at nested phrase
    occurrences
    (retirement.ts): RESOLVED — the anchored
    DRY_RECEIPT_START_RE is tried first. New test pins both witness
    receipts (no gaps: none. / no issues — all cold.) retiring; refused
    pre-round. This subclass no longer collides with the R2-2 marker check
    because the saturated vocabulary is stripped first.
  • R2-15 — judged never re-trimmed after stripBudgetGapLines
    (retirement.ts): RESOLVED with the suggested .trim(). New test pins
    a gap line parted from the receipt by a blank line retiring; it stayed
    due pre-round.
  • R2-13 — Kept message overclaimed "a reverse audit" (cleanup.ts):
    RESOLVED with the suggested weaker truthful copy — "a review run stopped
    here without converging".

Deferred to the next round (replies posted on each thread)

  • R2-5 (per-chunk assertion split in the repro test), R2-8
    (Nothing-to-clean pin), R2-14 (isolation test for the unfenced
    marker read): test-only pins; deferred under the ~8-finding round bound.
  • R2-7 (bolded phrase + sentence punctuation): fails toward audit
    today; small follow-up ([*_~]* before the anchored phrase).
  • R2-12 (archive a stale marker before overwrite): moderate change to
    writeBudgetStop/writeRoundCapStop; next round with its own test.
  • R2-11 (drift/422 restart rewrites the plan mid-run): needs a
    dedicated design — cleanup holds no convergence signal to gate the
    mtime claim; the promising direction is dating records against the
    fetch report's auditSince (records older than the window opening are
    a previous run's; records between it and the plan are the abandoned
    same-window pass), plus a test pinning the restart-then-converge shape.

Diff growth

Net this round: source +109 / test +274 lines (all within the PR's
existing footprint — commands/review sources and their tests). The test
share is driven by the two Criticals' mandated failing-pre-round pins and
the six executed witness shapes.

Verification

Commands actually run this round (results):

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • prettier --check on touched files — passed (one reformat applied to a
    new test before commit)
  • vitest run src/commands/review (packages/cli) — 2572 passed, 4 skipped
    across 72 files
  • vitest run on the four touched test files — 353 passed
    (retirement.test.ts 75, agent-prompt.test.ts 239, cleanup.test.ts
    29, issue-9206-repro.test.ts 10)
  • vitest run (whole packages/cli) — 20078 passed, 30 failed. The 30
    failures (settings/config/UI/auth test files) reproduce IDENTICALLY on
    the pre-round HEAD (verified via stash + rerun of the same 9 files: 30
    failed there too) — pre-existing on this runner, outside this PR's
    footprint.
  • Integration tests: not run — the touched behavior is exercised by the
    unit suites above, not only through the bundled CLI/integration harness.
  • New tests failed on the pre-round code as the findings describe:
    retirement 10/10 new tests failed, agent-prompt diagnostic test failed
    (chunk 14 stderr empty), cleanup second-cleanup and broken-symlink tests
    failed.
中文说明

审查轮次总结 — PR #9213

第 3 轮处理了第 2 轮 15 项发现中的 9 项——按每轮批次上限(约 8 项)优先处理
两个 Critical。每项被处理的声明都在实现之前先在轮前代码上复现(新增测试
在轮前代码上失败),修复后转绿。6 项发现延迟到下一轮,并已在各自线程回复。
无基线冲突处理(--conflict false)。

Critical

  • R2-1 — 逐 chunk 认证诊断只在该轮第一个 chunk 构建时输出
    agent-prompt.ts):已解决。豁免修复构建的戳记门同时也把诊断块挡在门
    内,因此逐 auditor 构建的轮次中 chunk 2..N 在完全静默中被重审。现在每次
    逐 chunk 构建都执行只读的调度读取,无论是否已盖章都输出该 chunk 自己的诊
    断;收敛与预算裁决仍然只在准入时生效,"retirement unavailable" 的 catch
    NOTE 仍只在未盖章构建上输出(该轮的准入构建已经点名过该降级——在每个修复
    构建上重复输出只是噪音;已钉住的"修复豁免零 stderr"契约保持不变)。新增测
    试在同一轮构建两个未认证 chunk,断言每次构建的 stderr 各自带有
    chunk N — … 行;该测试在轮前失败(chunk 14 的 stderr 为空)。
  • R2-2 — 极性守卫在两个已执行方向上泄漏retirement.ts):已与 R2-6
    一并解决——封闭该类而非继续枚举。从句检查中的转折词表被移除,举证责任反
    转:携带任何否定/无能力标记(notn'tnevernocannot
    未/没/无法)的从句读作 unknown;没有这类标记的转折词不构成矛盾。
    brief 自身的全清词汇(no gaps、未发现问题——即 DRY_RECEIPT_PHRASE
    枚举的形状)在标记测试前被剥离,使以 brief 措辞叙述的走查不会被捕获
    could not open the files 的同一标记误拒。填充段位置的泄漏由对 receipt
    前导段(短语+填充段)运行转折守卫封闭——仅检查从句的旧测试从来看不到那
    里。全部六种已执行证据形状由新增 it.each 钉住(轮前它们会让 chunk 退
    役);对照方向(已列出的 but/但是 从句仍被拒绝)保持绿色。标记词表残余缺
    口落在审计一侧——本模块声明安全的方向。

已解决的建议

  • R2-6 — 无害的 "but"/不过 从 dry 退化为 unknownretirement.ts):
    由同一守卫反转解决;新增两个测试钉住诚实的 "already covered them, but I
    re-verified" receipt 在英文与中文下重新退役。
  • R2-3 — 仅 mtime 的保留信号无法在第二次裸清理中存活cleanup.ts):
    按建议条件解决——计划文件已消失的 record 目录被保留(计划缺失正是上一次
    清理 Kept 后留下的形态)。新增真实文件系统测试:第一次清理保留目录并清扫
    计划;第二次清理再次保留。轮前失败(目录被删)。
  • R2-10 — 单个不可 stat 条目否决整个"上一次运行"扫描cleanup.ts):
    按建议的逐条目 try/catch 解决。新增真实文件系统测试:在更旧记录之前植入
    一个断裂符号链接,保留仍然生效。轮前失败。
  • R2-4 — hasPreviousRunRecords 的负方向无测试:作为 R2-10 覆盖的一部
    分解决——新增真实文件系统测试先写计划、后写记录(记录更新),断言目录被
    清扫(钉住 < 比较,防 !== 变异体全绿上线)。
  • R2-9 — 未锚定匹配器在嵌套短语出现处截断从句retirement.ts):已
    解决——先尝试锚定的 DRY_RECEIPT_START_RE。新增测试钉住两个证据
    receipt(no gaps: none. / no issues — all cold.)退役;轮前被拒绝。
    该子类与 R2-2 标记检查的冲突因饱和词汇先被剥离而消除。
  • R2-15 — stripBudgetGapLines 后未重新 trim judged
    retirement.ts):按建议加 .trim() 解决。新增测试钉住 gap 行与
    receipt 隔一个空行时仍退役;轮前保持 due。
  • R2-13 — Kept 消息过度断言 "a reverse audit"cleanup.ts):按建议
    改为更弱但真实的文案——"a review run stopped here without converging"。

延迟到下一轮(已在各自线程回复)

  • R2-5(repro 测试的逐 chunk 断言拆分)、R2-8(Nothing-to-clean
    钉住)、R2-14(无围栏标记读取的隔离测试):纯测试钉住;按每轮约 8 项
    上限延迟。
  • R2-7(加粗短语+句子标点):当前失败方向为审计一侧;小的后续修改
    (锚定短语前允许 [*_~]*)。
  • R2-12(覆盖前归档陈旧标记):对
    writeBudgetStop/writeRoundCapStop 的中等改动;下一轮连同专属测试一
    并处理。
  • R2-11(drift/422 重启在运行中途重写计划):需要专门设计——清理阶段
    没有可用于给 mtime 声明加门的收敛信号;有希望的方向是以 fetch 报告的
    auditSince 给记录定界(早于窗口开端的记录属于上一次运行;介于其与计划
    之间的记录是被放弃的同窗口第一趟),并新增测试钉住"重启后收敛"形态。

差异增长

本轮净增:源码 +109 / 测试 +274 行(全部位于本 PR 已有足迹内——
commands/review 源码及其测试)。测试占比由两个 Critical 要求的"轮前失败"
钉住与六种已执行证据形状驱动。

验证

本轮实际执行的命令(结果):

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • 对改动文件执行 prettier --check — 通过(提交前对一个新增测试应用了一
    次重排)
  • vitest run src/commands/review(packages/cli)— 72 个文件中 2572 通
    过、4 跳过
  • 对四个改动测试文件执行 vitest run — 353 通过
    retirement.test.ts 75、agent-prompt.test.ts 239、
    cleanup.test.ts 29、issue-9206-repro.test.ts 10)
  • vitest run(packages/cli 全量)— 20078 通过、30 失败。这 30 个失败
    (settings/config/UI/auth 测试文件)在轮前 HEAD 上完全相同地复现(经
    stash + 对同 9 个文件重跑验证:那里同样是 30 失败)——本运行器上的既有
    失败,位于本 PR 足迹之外。
  • 集成测试:未运行——本次改动的行为由上述单元测试覆盖,并非仅经打包 CLI/
    集成测试框架才能验证。
  • 新增测试在轮前代码上按发现所述失败:retirement 新增 10/10 失败,
    agent-prompt 诊断测试失败(chunk 14 stderr 为空),cleanup 第二次清理与
    断裂符号链接测试失败。

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

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


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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

Not reviewed: build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

Not reviewed: build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

中文说明

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

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

未审查:build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

未审查:build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

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

Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/cleanup.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/cleanup.test.ts
…otes (QwenLM#9213)

The polarity guard read only the match's own prefix and the clause, so a
hedge BEFORE the phrase on the same line was invisible and retired the
chunk (`I could not check everything, but no new issues — re-walked…`),
and its marker list missed the incapacity/omission families the leak
probes executed — unable/failed/skipped/unchecked/skimmed, zh bare-不 and
跳过. Scope the guard to the LINE the receipt matched on — the match plus
any same-line text before the phrase, quoted spans exempted — and name the
executed marker families. Prose on any other line neither certifies nor
contradicts: scanning the whole return would refuse an honest receipt
beside an innocent "not re-reporting it" paragraph. The substance floor
now measures the phrase-STRIPPED clause (greedy with the phrase's filler),
so an echoed phrase cannot lend the floor its length, and the comment's
direction claim is corrected — a marker the list misses inside the line
still fails toward RETIREMENT, stated rather than papered over.

The parrot refusal compared case-sensitively, but the example clause
starts lowercase only because it continues the model receipt mid-sentence;
the widened sentence-punctuation separators let a parroting auditor open it
as a NEW sentence and capitalize it. Compare in any casing — no honest
clause contains the model clause verbatim in any casing.

The three new informational stderr writes — both schedule catch NOTEs and
the uncertified-chunks note — used the throwing writeStderrLine on the
CONTINUING build path. A headless retry with stderr redirected or closed
(the very QwenLM#9206 shape) made the write throw out of a catch with no outer
guard, abandoning the round that must audit every chunk. Use the Safe
writer, matching writeFindingsFile.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Address summary — PR #9213 review round

All three actionable Critical findings are resolved in code. Each was
reproduced first with a failing test on the unmodified tree, then fixed
minimally, and the new tests pass only with the fix. The ten non-Critical
Suggestion findings are excluded from this round by the critical-only brake
(the PR's test diff is past the counting window's budget) and remain open for
human follow-up — no code, thread, or reply changes for them this round.

Findings and decisions

  • [Critical] R2-2 — the polarity guard still enumerates over unbounded auditor prose, and every missed marker fails toward RETIREMENT (rc:3789948947) — RESOLVED. Reproduced all six executed leak families against the real scheduler on the unmodified tree: incapacity/omission admissions with no listed marker (unable / failed / skipped / unchecked / skimmed), zh bare-不 (打不开) and 跳过, a marker lost to the saturation strip-order, phrase echoes lending the substance floor their length, a hedge BEFORE the phrase the guard never saw, and filler hedges on both separator paths — every one read dry twice and retired the chunk. Fix, the finding's stopgap bundle within the current receipt shape: (1) the polarity test now reads the LINE the receipt matched on — the match, filler and clause included, plus any same-line text before the phrase, quoted spans exempted — so a hedge before the phrase contradicts the claim; prose on any other line neither certifies nor contradicts, because scanning the whole return would refuse an honest receipt beside an innocent not re-reporting it paragraph (the existing echoed-file-line test pins that); (2) the marker vocabulary names the executed families (unable, fail, skip, skim, un-checked/-tested/-verified/-read/-opened, 跳过, 不 with 不过 exempted as the pinned innocuous connective); (3) the substance floor measures the phrase-STRIPPED clause (the filler rides along, greedy), so echoed phrases cannot lend it their length; (4) the comment's direction claim is corrected — a marker the list misses inside the receipt's line still fails toward RETIREMENT, stated rather than papered over. Residual stated honestly in the comment: the marker list has no last word; what the line-scoped domain closes is every hedge the old clause-only test never saw.
  • [Critical] R3-1 — a case-shifted echo of the brief's model receipt defeats the case-sensitive parrot guard (rc:3789948949) — RESOLVED. Reproduced: No issues found. Re-walked the reconnect state machine and the two changed exports' call sites; every gap I checked was already in the list (the model receipt with the clause opened as a new sentence, capitalized) classified dry twice and retired the chunk on the unmodified tree. Fix exactly as suggested: precompute EXAMPLE_RECEIPT_CLAUSE.toLowerCase() beside the constant and compare case-insensitively — no honest clause contains the model clause verbatim in any casing.
  • [Critical] R3-2 — the three informational stderr writes use the throwing writeStderrLine on the continuing build path (rc:3789948950) — RESOLVED. Reproduced with the repo's own EPIPE model (writeStderrLine throws): on the unmodified tree all three sites threw write EPIPE out of the handler and the round built zero auditors. Fix exactly as suggested: writeStderrLineSafe for the schedule catch NOTE in the round builder, its per-chunk twin, and the noteUncertifiedChunks body — matching the writeFindingsFile precedent in the same module family. The five existing tests asserting those NOTE texts now read the Safe writer.

Verification

  • npm run build — passed
  • npm run typecheck — passed (all packages; a first run failed only on missing acp-bridge build artifacts in webui, cleared by the build)
  • npm run lint — passed
  • npx vitest run src/commands/review (packages/cli) — 2589 passed, 4 skipped (72 files), including all 22 new/changed tests this round
  • Pre-round reproduction: the same 22 tests were run against the unfixed tree first — 14 failed in retirement.test.ts and 8 failed in agent-prompt.test.ts, exactly the new witnesses and the flipped NOTE assertions
  • npx prettier --check on the four touched files — passed (after --write on two)
中文说明

处理摘要 — PR #9213 审查轮

三个可执行的 Critical 发现均已在代码中解决。每一条都是先在未修改的代码树上用失败测试复现,再做最小修复,且新测试只在修复后通过。十条非 Critical 的 Suggestion 发现被本轮的"仅处理 Critical"刹车排除(本 PR 的测试 diff 已超出计数窗口预算),保持开放、留待人工跟进——本轮不为它们改代码、不动线程、不写回复。

发现与决定

  • 【Critical】R2-2 — 极性守卫仍然在无界 auditor 散文上做枚举,词表漏掉的每个标记都倒向退役(rc:3789948947)— 已解决。 在未修改的代码树上,经真实调度器复现了全部六个已执行的泄漏家族:不含任何词表标记的无能力/遗漏自认(unable / failed / skipped / unchecked / skimmed)、中文裸"不"(打不开)与"跳过"、被饱和剥离顺序丢掉的标记、借短语回声凑出实质性下限长度的从句、守卫从未看到的短语之前的 hedge、以及两条分隔符路径上的填充段 hedge——每一条都两轮判 dry 并使 chunk 退役。修复采用该发现提出的"当前 receipt 形状内的止损"组合:(1) 极性测试现在读取 receipt 所匹配的整行——包括匹配本身(填充段与从句)以及短语之前同一行的文本,引号内容豁免——使短语前的 hedge 也能与声明矛盾;其他行的文字既不认证也不矛盾,因为扫描整个返回会把诚实 receipt 与其旁侧无辜的 not re-reporting it 段落一并拒绝(既有的"回声文件行"测试钉住了这一点);(2) 标记词表点名已执行的各家族(unable、fail、skip、skim、un-checked/-tested/-verified/-read/-opened、跳过、不——其中 不过 作为已钉住的无害连接词予以豁免);(3) 实质性下限改在剥离短语后的从句上测量(填充段随短语一并被贪婪剥离),使回声短语不能再借给下限长度;(4) 修正注释的方向性声明——词表在 receipt 行内漏掉的标记仍然倒向退役,如实写明而非粉饰。注释中如实写明残留:标记词表没有最后一个词;行域封闭的是旧的"仅看从句"测试从未看到的全部 hedge。
  • 【Critical】R3-1 — 大小写变化的模板 receipt 回声绕过大小写敏感的鹦鹉学舌守卫(rc:3789948949)— 已解决。 已复现:No issues found. Re-walked the reconnect state machine and the two changed exports' call sites; every gap I checked was already in the list(模板 receipt 以新句子形式大写开头)在未修改代码树上两轮均判 dry 并退役 chunk。修复完全按建议:在常量旁预计算 EXAMPLE_RECEIPT_CLAUSE.toLowerCase(),改用大小写不敏感比较——任何诚实从句都不会以任何大小写形式逐字包含模板从句。
  • 【Critical】R3-2 — 三处信息性 stderr 写入在继续构建路径上使用了会抛异常的 writeStderrLine(rc:3789948950)— 已解决。 用本仓库自身的 EPIPE 模型(令 writeStderrLine 抛异常)复现:未修改代码树上三处均从 handler 中抛出 write EPIPE,本轮构建出零个 auditor。修复完全按建议:round builder 的调度 catch NOTE、其逐 chunk 孪生处、以及 noteUncertifiedChunks 主体,三处改用 writeStderrLineSafe——与同模块族 writeFindingsFile 的先例一致。断言这些 NOTE 文本的五个既有测试现改从 Safe 写入器读取。

验证

  • npm run build — 通过
  • npm run typecheck — 通过(全部 package;首次运行的失败仅因 webui 缺少 acp-bridge 构建产物,build 后即消失)
  • npm run lint — 通过
  • npx vitest run src/commands/review(packages/cli)— 2589 通过、4 跳过(72 个文件),含本轮全部 22 个新增/修改测试
  • 轮前复现:同一批 22 个测试先在未修复树上运行——retirement.test.ts 中 14 个失败、agent-prompt.test.ts 中 8 个失败,恰为新增证据测试与翻转后的 NOTE 断言
  • 四个被改文件的 npx prettier --check — 通过(其中两个经 --write 修正后)

Deferred non-Critical feedback

Critical-only mode is active: the PR's diff grew src 203 / test 613 net lines beyond this counting window's baseline (budgets: 400/400). The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback continues to flow unaffected during a growth-only engagement (the per-author batch budget applies only after 5 change-producing rounds). (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:本计数窗口内 diff 净增长已达 源码 203 / 测试 613 行(预算 400/400)。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。纯增长触发期间维护者反馈照常流动(按作者的批次预算仅在完成 5 个产生改动的轮次后生效)。(评论 @qwen-code /retry 可开启新的计数窗口。)

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

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


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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

Not reviewed: build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

Not reviewed: build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

Not explored to full depth (tool budget reached): "agent reverse-audit (round 1)": none — no check was cut short..

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

中文说明

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

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

未审查:build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

未审查:build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 1)"none — no check was cut short.

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

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

Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/agent-prompt.test.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
…ipt form (QwenLM#9213)

The polarity guard enumerated hedges over unbounded auditor prose, and
every hedge the list missed failed toward RETIREMENT — the direction the
module header declares impossible. Round 5 executes the structural fix
the round-4 Critical named: one machine-parseable receipt form, and
everything else read as unknown.

The form: the phrase LEADS the return (anchor; the lead CONTRAST list is
deleted), the receipt line stands ALONE (Budget gap: and Layer walked:
lines stripped with audit-layers' own matcher; any other prose before or
after reads unknown, named for the side it fell at), and the clause
NAMES THE WALK (a verb from the form's vocabulary, or a named object —
misses fail toward audit, the opposite direction of a marker miss).
Within the form the marker test is repaired, not extended: the strip
uses the phrase core without the greedy tail that swallowed its own
listed markers, and the quoted-span exemption that blanked a
self-admission is deleted. Zero words added to the vocabulary.

Also closes R4-1: the clause capture ran to the END of the return, so
prose after the receipt line contradicted the phrase while identical
prose before it passed. The capture stops at the line; both sides now
read unknown, symmetrically named. The reverse-audit brief mandates the
form so compliant dry returns stay one line and keep retiring.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Address-review summary — PR #9213 (round 5, Critical-only)

Critical-only mode was active for this round (the counting window measured
src 262 / test 800 net lines against 400/400 budgets). Per the mode rules,
only the two Critical findings were actionable; the ten Suggestions in the
ledger sit in the deferred section and were left untouched, exactly as the
brake mandates. The round also resolves the declared base conflict.

Feedback points and decisions

[Critical] R2-2 — the polarity guard is an enumeration over unbounded prose (rc:3790561111) — FIXED

The finding executed four NEW entrance families at the round-4 commit that
all retired chunks on hedged returns: (a) the greedy phrase tail of the
saturation strip swallowed a LISTED marker before the marker test saw it
(skipped); (b) an admission on the line BEFORE the receipt line sat
outside the polarity domain; (c) the quoted-span exemption blanked a
self-admission (I "could not open" the generated files); (d) synonym
vocabulary outside the list (overlooked/missed/ignored/glanced/without
checking; zh 忽略|略过|遗漏; a minus lead) retired. The class is
unbounded — prose has no last hedge — so the round implements the
finding's own structural fix: one machine-parseable receipt FORM, with
everything else read as unknown.

The form, now mandated by the reverse-audit brief and enforced by the
classifier:

  1. Anchored — the phrase must LEAD the return. Prose before the phrase
    (family (b), the minus lead, any hedge in front) is a form violation,
    not a receipt lead: receipt not matched. The old CONTRAST list over
    the lead is deleted — the anchor supersedes it.
  2. Alone — after the brief's structured lines (Budget gap: and
    Layer walked:, stripped with audit-layers' own matcher) are removed,
    the receipt line must be the WHOLE return. Prose after the receipt line
    — an admission there reads exactly as one inside the clause — is
    receipt not alone, a new diagnostic member. This closes family (b)
    symmetrically with the anchor and makes the clause capture's line cut
    honest.
  3. Names the walk — the clause must carry a walk verb from the form's
    vocabulary (walk/verify/trace/examine — 走查/核对/复核/核查/复查/重走)
    or name an object. This closes family (d): overlooked, missed,
    ignored, without checking, 忽略 — no walk named, no retirement —
    and the test's misses fail toward AUDIT (a verb the vocabulary does not
    name reads unknown), the opposite direction of a marker miss and the
    only one the module header declares.
  4. Marker test repaired, not extended — families (a) and (c) were
    mechanism bugs, not missing words: the marker strip now uses the phrase
    CORE without the greedy filler tail (so a marker riding right after an
    echo keeps standing), and the quoted-span exemption is deleted (a
    quoted could not open contradicts the phrase exactly as a bare one).
    Not one word was added to the marker vocabulary; the residue a miss
    leaves is now bounded by the form and stated in the comment, not
    papered over.

Subtractive accounting: CONTRAST_RE, the quoted-span exemption, the
polarity-domain/line-start stitching, and the second (anchored) receipt
regex are all deleted; the two receipt regexes merge into one anchored
matcher whose clause captures to the END OF THE LINE.

[Critical] R4-1 — polarity domain contradicts the line-scope claim (rc:3790561114) — FIXED

The clause capture ran ([\s\S]*) to the END of the return, so prose on
lines AFTER the receipt line contradicted the phrase while identical prose
BEFORE it passed — an executed asymmetry under a line-scope claim the
comments stated twice, and honest trailing sentences were misrefused as
receipt clause not substantive. The form resolves all three claims:

  • the clause capture now stops at the end of the receipt line — the
    comment is true;
  • before/after are symmetric: identical prose on either side of the line
    reads unknown, named for the side it fell at (receipt not matched
    before, receipt not alone after) — pinned by a new two-chunk symmetry
    test;
  • the misattribution is gone: a non-form return is refused by the form's
    own names, never by the substance bar.

One honest note: the finding's flip witness (cutting judgedClause at the receipt line's end restores retired=true) made the multi-sentence shape
retire; under the form it reads receipt not alone instead. The
asymmetry, the false comment and the misdiagnosis — the finding's claims —
are structurally gone, and the brief now teaches auditors the one-line
form, so compliant dry returns retire while any narration beside the
receipt stays under audit: the direction this module declares its only
failure.

Deferred Suggestions (R4-3, R4-4, R4-5, R4-6, R4-7, R4-8, R4-9, R4-10, R4-11, R3-6) — NOT TOUCHED

Critical-only mode excludes them from this round's work; they remain open
for human follow-up per the deferred section. No code, no thread
resolution, no replies, per the mode rules.

Conflict resolution (--conflict true)

origin/main merged. Two conflicts, both the same shape — #9213's
per-chunk diagnostic work and main's #9242 topology note added at the same
sites in agent-prompt.ts / agent-prompt.test.ts:

Changed files

  • packages/cli/src/commands/review/lib/retirement.ts — the form:
    anchored single receipt matcher, clause cut at end of line,
    stand-alone check (receipt not alone), walk-verb gate, core-only
    marker strip, no quoted-span exemption, layer-line strip; CONTRAST
    list and domain stitching deleted.
  • packages/cli/src/commands/review/lib/agent-briefs.ts — the
    reverse-audit brief mandates the form: the dry return IS the receipt,
    one line, clause opening with the walk; anything unexamined means no
    receipt at all.
  • packages/cli/src/commands/review/lib/audit-layers.ts — exports
    LAYER_RECEIPT_LINE_RE so the retirement strip reads layer lines with
    audit-layers' own matcher (one source, no drift).
  • packages/cli/src/commands/review/lib/retirement.test.ts — four
    pinned shapes flipped to the form's behavior (an echoed file line's
    prose lead, two whole-entry quotations' prose lead, the quoted-marker
    clause — the exemption was one of R2-2's executed leaks), and new
    coverage: the four executed entrance families (8 cases), the
    before/after symmetry pair with per-side diagnostics, the walk gate,
    and layer-line coexistence.
  • packages/cli/src/commands/review/agent-prompt.ts /
    agent-prompt.test.ts — merge resolution only (both sides kept;
    stamp guard restored around the per-chunk topology note).

Verification

  • npm run build — passed
  • npm run typecheck — passed (no errors)
  • npm run lint — passed
  • npx vitest run src/commands/review (packages/cli, the touched area) — 77 files, 2751 passed | 4 skipped
  • npx vitest run src/commands/review/lib/retirement.test.ts — 100 passed
  • Pre-round gate probe: this round's changed/added tests run against the
    pre-round retirement.ts (7a9ccdc) — 14 failed there, confirming the
    tests pin behavior the round actually changed
  • Prettier over all changed files — applied, clean
  • npx vitest run (full packages/cli suite) — 20299 passed | 7 skipped | 30 failed. ALL 30 failures sit in config/ui test files (sandbox-image resolution, HOME-directory workspace scope, Windows-style paths, docker-indicator snapshots); none of those files imports anything from commands/review, and 21 of them reproduce identically on the base tree with this round's edits stashed — pre-existing environment failures on this runner, not caused by this round.
中文说明

处理审查总结 — PR #9213(第 5 轮,仅 Critical)

本轮处于仅 Critical 模式(计数窗口测得净增长 源码 262 / 测试 800 行,预算 400/400)。按模式规则,本轮仅处理两条 Critical 发现;清单中的十条 Suggestion 位于延迟区,按刹车机制的要求原样保留、不做处理。本轮同时解决已声明的基线冲突。

反馈点与决定

【Critical】R2-2 — 极性守卫是在无界散文上的枚举(rc:3790561111)— 已修复

该发现在第 4 轮提交上执行出四个新的入口家族,全部会使带 hedge 的返回退役:(a) 饱和剥离的贪婪短语尾部在标记测试之前吞掉了已列入词表的标记(skipped);(b) receipt 行上一行的自认不在极性域内;(c) 引号豁免把自我自认清空(I "could not open" the generated files);(d) 词表外同义词(overlooked/missed/ignored/glanced/without checking;中文 忽略|略过|遗漏;前导 minus)退役。该类无界——散文没有最后一个 hedge——因此本轮实现该发现自身给出的结构性修复:唯一可机器解析的 receipt 形式,其余一律读作 unknown

该形式现由 reverse-audit brief 规定、由分类器强制执行:

  1. 锚定——短语必须位于返回开头。短语之前的散文(家族 (b)、minus 前导、任何前置 hedge)是形式违规而非 receipt 前导:receipt not matched。旧的针对前导段的 CONTRAST 词表被删除——锚定取代了它。
  2. 独立——剥离 brief 规定的结构化行(Budget gap:Layer walked:,用 audit-layers 自己的匹配器剥离)后,receipt 行必须是返回的全部内容。receipt 行之后的散文——那里的自认与从句内的自认等价——读作 receipt not alone(新增的诊断名)。它与锚定对称地封闭家族 (b),并使从句捕获的行截断名副其实。
  3. 点名行走——从句必须携带形式词表中的行走动词(walk/verify/trace/examine — 走查/核对/复核/核查/复查/重走)或点名对象。这封闭家族 (d):overlookedmissedignoredwithout checking、忽略——未点名行走就不得退役——且该测试的漏判倒向审计侧(词表未收录的动词读作 unknown),与标记漏判的方向相反,也是模块头部声明的唯一失败方向。
  4. 标记测试修复而非扩词——家族 (a) 与 (c) 是机制缺陷而非缺词:标记剥离改用不含贪婪填充尾部的短语核心(紧随回声之后的标记得以保留),引号豁免被删除(引号内的 could not open 与裸写一样矛盾短语)。标记词表未新增一个词;漏判留下的残余现在被形式约束,并在注释中明示而非掩盖。

减法核算:CONTRAST_RE、引号豁免、极性域/行首拼接、第二个(锚定)receipt 正则全部删除;两个 receipt 正则合并为一个锚定匹配器,其从句捕获到行尾。

【Critical】R4-1 — 极性域与行域声明矛盾(rc:3790561114)— 已修复

从句捕获曾以 ([\s\S]*) 延伸到返回末尾,因此 receipt 行之后的散文可以矛盾短语,而之前的相同散文却通过——在注释两次声明的行域之下形成已执行的不对称,且诚实的尾随句子被误判为 receipt clause not substantive。形式一次性解决三项指控:

  • 从句捕获现在止于 receipt 行尾——注释变为真实;
  • 前后对称:行两侧相同的散文都读 unknown,并按所倒一侧命名(前侧 receipt not matched,后侧 receipt not alone)——由新的双 chunk 对称测试钉住;
  • 误归因消失:非形式返回由形式自身的名字拒绝,永不再由实质性门槛代报。

一点诚实说明:该发现的翻转证据("将 judgedClause 截断到 receipt 行末可恢复 retired=true")使多句形状退役;在形式之下它改读 receipt not alone。发现的三项指控——不对称、虚假注释、误诊断——已被结构性消除,且 brief 现在教导 auditor 单行形式:合规的 dry 返回照常退役,receipt 之外的任何叙述则保持审计——这正是本模块声明的唯一失败方向。

延迟的 Suggestion(R4-3、R4-4、R4-5、R4-6、R4-7、R4-8、R4-9、R4-10、R4-11、R3-6)— 未触碰

仅 Critical 模式将其排除在本轮工作之外;按延迟区规则留待人工跟进。不改代码、不解决线程、不回复。

冲突解决(--conflict true

已合并 origin/main。两处冲突,形状相同——#9213 的逐 chunk 诊断工作与 main 的 #9242 拓扑提示加在 agent-prompt.ts / agent-prompt.test.ts 的相同位置:

变更文件

  • packages/cli/src/commands/review/lib/retirement.ts — 形式:锚定的单一 receipt 匹配器、从句截至行尾、独立检查(receipt not alone)、行走动词门槛、仅核心的标记剥离、无引号豁免、layer 行剥离;CONTRAST 词表与域拼接删除。
  • packages/cli/src/commands/review/lib/agent-briefs.ts — reverse-audit brief 规定形式:dry 返回即 receipt 本身,单行,从句以行走开头;任何未检查的部分意味着完全不发 receipt。
  • packages/cli/src/commands/review/lib/audit-layers.ts — 导出 LAYER_RECEIPT_LINE_RE,使退役剥离用 audit-layers 自己的匹配器读 layer 行(单一来源,不漂移)。
  • packages/cli/src/commands/review/lib/retirement.test.ts — 四个钉住形状翻转为形式行为(echo 文件行的前导散文、两处整条引用的前导散文、引号标记从句——豁免正是 R2-2 已执行的泄漏之一),以及新覆盖:四个已执行入口家族(8 例)、前后对称对(含按侧命名诊断)、行走门槛、layer 行共存。
  • packages/cli/src/commands/review/agent-prompt.ts / agent-prompt.test.ts — 仅合并解决(两侧都保留;逐 chunk 拓扑提示恢复落戳守卫)。

验证

  • npm run build — 通过
  • npm run typecheck — 通过(无错误)
  • npm run lint — 通过
  • npx vitest run src/commands/review(packages/cli,触碰区域)— 77 个文件,2751 通过 | 4 跳过
  • npx vitest run src/commands/review/lib/retirement.test.ts — 100 通过
  • 轮前门槛探针:本轮改动/新增的测试在轮前 retirement.ts(7a9ccdc3)上运行 — 14 个失败,确认测试钉住的是本轮实际改变的行为
  • 对所有变更文件运行 Prettier — 已应用,干净
  • npx vitest run(packages/cli 全套件)— 20299 通过 | 7 跳过 | 30 失败。全部 30 个失败都在 config/ui 测试文件(sandbox 镜像解析、HOME 目录工作区作用域、Windows 风格路径、docker 指示符快照);这些文件没有任何一个导入 commands/review 的内容,且其中 21 个在本轮改动 stash 后的基线树上原样复现——本运行器上的既有环境失败,与本轮无关。

Deferred non-Critical feedback

Critical-only mode is active: the PR's diff grew src 262 / test 800 net lines beyond this counting window's baseline (budgets: 400/400). The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback continues to flow unaffected during a growth-only engagement (the per-author batch budget applies only after 5 change-producing rounds). (@qwen-code /retry starts a fresh counting window.)

中文说明

已进入仅处理 Critical 的模式:本计数窗口内 diff 净增长已达 源码 262 / 测试 800 行(预算 400/400)。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。纯增长触发期间维护者反馈照常流动(按作者的批次预算仅在完成 5 个产生改动的轮次后生效)。(评论 @qwen-code /retry 可开启新的计数窗口。)

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

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


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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

Not reviewed: build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

Not reviewed: build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

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

Not explored to full depth (tool budget reached): "agent reverse-audit (round 3)": none — wait, per the brief I must not write that line when nothing was cut short; withdrawing it. Nothing was cut short: the full chunk was read un-truncated, t….

Test Plan (not a blocker): 323 tests green — this review observed 20319 passed.

中文说明

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

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

未审查:build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

未审查:build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

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

未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 3)"none — wait, per the brief I must not write that line when nothing was cut short; withdrawing it. Nothing was cut short: the full chunk was read un-truncated, t…

Test Plan(非阻断):323 tests green — this review observed 20319 passed

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

Comment thread packages/cli/src/commands/review/lib/retirement.ts Outdated
Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/issue-9206-repro.test.ts
Comment thread packages/cli/src/commands/review/issue-9206-repro.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind main, so it merged current main in via update-branch and will retry on the next scan. A stale base (a dependency or symbol main already changed) can fail the build without being the fix's fault; if it still fails once current, it hands off to a human.

What I found before stopping:

Blocked: PR #9213 needs a maintainer decision — the review loop is not converging

The blocker

The workflow's deterministic growth brake has been over budget across rounds and
the diff is still not shrinking: this counting window measures source 286 /
test 948 net lines vs budgets of 400/400
(2 prior rounds already over budget).
The review findings are themselves driving the growth — five rounds of
fix-on-finding have accumulated 2,143 insertions, dominated by pinned tests
(562 lines in the #9206 repro test, 671 lines of retirement tests) — so
Critical-only mode cannot help: implementing the remaining Criticals adds more
pinned tests and pushes the over-budget test diff further over. Per the
address-review protocol this is a defer-to-human round: no code changes were
applied, no commit was made, and no review threads were resolved.

The decision needed

How to land PR #9213 without continuing unbounded review-driven growth.

Options

  1. Split / land the core, track the tail as follow-up issues. Merge the
    branch as it stands (it already contains five rounds of hardening: the
    line-scoped polarity guard, the single-receipt-form certification, the
    layer-receipt lines, and the NOTE-suppression guards), and file the remaining
    findings as follow-up issues.
  2. One bounded final round in a fresh counting window. A maintainer restarts
    the window (@qwen-code /retry) and allows exactly the two remaining
    Critical fixes — both are s

Run log: https://github.com/QwenLM/qwen-code/actions/runs/31926744017


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

@wenshao

wenshao commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Round 8 (takeover): Critical-only fixes, per the post-round-5 landing policy.

Fixed in 6d5b5e1ad3:

  • R5-9 — every whitespace element in the dry-receipt matcher is now line-bound ([ \t], never \s), so the matcher can no longer span lines and pull the clause in from a later line. All five executed line-broken shapes flip to unknown (receipt not alone / receipt not matched); the single-line honest control still retires.
  • R5-5 — the clause is cut at an inline Layer walked: marker beside the existing inlineGap cut (new INLINE_LAYER_WALKED_RE, mirroring the budget-gap precedent), so a fused layer label can no longer supply both the walk verb and the substance. The fused shape flips to receipt clause not substantive; the two-line honest form still retires.

Both fixes are pinned by new tests in retirement.test.ts (102/102 green, 325 green across the four related suites), mutant-verified (reverting the production change fails exactly the new tests), and lint/typecheck clean.

Verified already-addressed at HEAD and resolved: R2-1 (per-chunk diagnostics de-gated from the round stamp), the writeStderrLineSafe Critical (all three informational writes), and R2-2 (probe per executed family: strip-swallow / preceding-line EN+ZH / quoted self-admission / unlisted synonyms all read unknown; the one residue is stated in the NEGATION_MARKER_RE comment).

Deferred: all open Suggestions are collected in #9259 so nothing is silently dropped.

中文说明

第 8 轮(接手):按超过 5 轮后的落地策略,只修 Critical。

  • 本提交 6d5b5e1ad3 修复 R5-9(receipt 匹配器空白元素全部行绑定,五种断行形状翻转为 unknown,诚实单行仍退役)与 R5-5(从句在内联 Layer walked: 标记处截断,融合形式翻转为 receipt clause not substantive,两行诚实形式仍退役)。均有新测试钉住并通过变异验证;102/102 与四个相关套件 325 全绿,lint/typecheck 干净。
  • 已在 HEAD 复核并关闭:R2-1(逐 chunk 诊断与戳记门解耦)、writeStderrLineSafe Critical(三处信息性写入均已改用 Safe)、R2-2(逐家族探针确认全部读作 unknown,唯一残留已在注释中明示)。
  • 全部未决 Suggestion 已汇总延期至 review: deferred Suggestions from PR #9213 (retirement diagnostics follow-ups) #9259,无静默丢弃。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed. Suggestions are inline.

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

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

Not reviewed: build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

Not reviewed: build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only.

Test Plan (not a blocker): 323 tests green — this review observed 20367 passed.

中文说明

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

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

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

未审查:build-and-test — Test (macos-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

未审查:build-and-test — Test (windows-latest, Node 22.x) was skipped in CI; the unit suite ran on Linux only。

Test Plan(非阻断):323 tests green — this review observed 20367 passed

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

Comment thread packages/cli/src/commands/review/lib/retirement.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/agent-prompt.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/issue-9206-repro.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/retirement.test.ts
Comment thread packages/cli/src/commands/review/lib/deadline.test.ts
@wenshao

wenshao commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

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

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

Scripted assertions: 3027 passed · 0 failed · 3027 total

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

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

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

Verification report

PR #9213 deep verification

Verdict: merge-ready — 3027/3027 scripted assertions passed, 0 unexpected failures. Verified head: 6d5b5e1ad3e08d31abf71b9fb6dd5dfde9d91175 (merge ae2e239a0b over base 337da2143c). Two non-blocking Suggestion findings, both pre-existing (A/B-symmetric with base, not caused by this PR) — see Findings.

中文摘要
  • 结论merge-ready。3027 项脚本化断言全部通过,0 项意外失败;验证 head 6d5b5e1ad3
  • A/B 结论:中心主张成立。以真实 CLI 子进程驱动第 1–3 轮:句号分隔(No new issues were found. Re-walked …)与全角逗号分隔(未发现新问题,重新走查了…)的 receipt 在 head 上从第 3 轮起退役对应 chunk(1 auditors required + retirement: 注解),在 base 上则逐字节复现 /review: chunk retirement silently does not fire in the reverse-audit loop, and cleanup destroys the evidence #9206 症状(3 auditors required、无任何注解、stderr 静默);canonical 破折号 receipt 两臂均退役(接线对照)。cleanup 半边同样翻转:head 对未收敛运行的 record 目录输出 Kept … 并保留(含 marker 形、被 kill 无 marker 形、plan 已被清扫形、坏符号链接形),base 一律删除;收敛运行的清理两臂照旧清扫。
  • Findings:两条均为预存性能问题(与 base 完全同形、同量级,非本 PR 引入、未使其恶化):namesAnObject 的路径正则对无斜杠长从句二次方(20k 时 ~0.5s/次分类),以及锚定 receipt 匹配器对“短语与从句之间的长空格串”二次方(20k 时 ~15s)。前者附 18 例行为等价、566ms→0ms 的已测量一行修复;均不阻塞合并。另两条对 PR 描述的更正(测试计数、stale-bundle 预存失败声明在本容器不复现),见 Corrections。
  • 未覆盖范围:逐 commit 归因(depth-2 checkout,仅聚合 diff 可验证);仓库级 lint/typecheck(PR CI 已覆盖;本容器 core 有一个环境性 tsc 报错,与 PR 无关);真实模型调用 E2E(合成 harness 形 transcript 即该子系统的接缝);Windows/macOS;review 目录中 4 个 skip 的测试。

Central claim + A/B

Central claim: sentence-punctuation-separated dry receipts (EN period, full-width comma — the exact shapes #9206 reported) classify dry, so twice-dry chunks retire from round 3 on; on base they classify unknown and the loop re-audits every chunk in silence. Harness ab-retirement.mjs drives the real compiled CLI (review agent-prompt --all-chunks --round N) as a subprocess per round, writing harness-shaped transcripts between rounds (same seam the repro test uses; no mocks, no model calls). Witness: 01-ab-retirement-head.png / 02-ab-retirement-base.png.

cell oracle head base (control)
canonical dash receipt (wiring control) round-3 stdout 1 auditors required + retirement: chunks 13+14 identical (control passes on both arms)
No new issues were found. Re-walked … (period) round-3 stdout 1 auditors + retirement notes 13+14 3 auditors required, no note, silent stderr — the #9206 symptom
未发现新问题,重新走查了… (full-width comma) round-3 stdout 1 auditors + retirement notes 13+14 3 auditors required, no note, silent stderr
bare No issues found. twice round-3 stdout+stderr 3 auditors, stderr NOTE chunk 13 — round 1: receipt clause not substantive; round 2: … 3 auditors, stderr silent

head 14/14, base 13/13 (every base cell behaved as the expected-broken prediction; the base wiring control proves the base mechanism itself works).

Secondary claim 1 — observability & evidence retention. ab-cleanup.mjs drives review cleanup pr-9206 as a subprocess over constructed run dirs; stop markers written with each tree's own writeRoundCapStop. Witness: 03-ab-cleanup-head.png / 04-ab-cleanup-base.png.

shape head base
previous run's round-cap marker, retry re-captured plan after it Kept …-fetch-prompts … without converging, dir kept dir deleted, Removed, never Kept
killed run, no marker, records older than plan kept on mtime signal alone deleted
second cleanup after the first swept the plan still kept, Kept again n/a (gone after first)
broken symlink beside the older record kept (entry skipped, no veto) deleted
converged run, records newer than plan (negative control) swept, no false Kept swept

head 12/12, base 8/8. The per-chunk/per-round diagnostic path is additionally proven by the bare-phrase cell above (NOTE names both chunks and the bar per round) and by 05-classifier-probes-head.png (every refusal carries the named bar in diagnostics).

Secondary claim 2 — the widened form admits nothing it should not. classifier-probes.mjs drives the exported scheduleReverseAuditRound seam of the compiled dist with real records + transcripts (50/50 at head, witness 05-classifier-probes-head.png; 4/4 base sanity cells in logs/probes-base.txt):

  • all 15 separator/lead siblings retire: em/en/doubled dash, colon both widths, period, full-width comma/period, ASCII comma/semicolon, full-width semicolon, double hyphen, lone hyphen+space, bold lead, parenthesised filler;
  • all 14 hostile shapes stay hot and the diagnostic names the bar: bare EN/zh stock sentence, hedge in clause, comma+hedge, prose before the phrase (receipt not matched), prose after the receipt line (receipt not alone), parrot clause verbatim and capitalized, fused Layer walked: label, dangling separator at line end, quoted phrase not the lead, hedge riding the filler, zh incapacity, no-walk-verb clause;
  • positive invariants keep retiring: innocuous 不过, contrast word without a marker, inline Budget gap: cut, CRLF line form, named-object clause; a filed finding still outranks any receipt (yielded, no diagnostic).

Reviewer Test Plan, per step: (1) regression suites — executed, see Gates (453/453 on the named files; the plan's "323" is stale, see Corrections). (2) invariants — canonical dash retires from round 3 ✓ (A/B + probes); bare No issues found. does not retire ✓; a filing outranks any receipt ✓; cleanup with no stop marker deletes everything ✓ (negative control cell). (3) Before/After evidence — reproduced byte-for-byte on both arms (table above).

Corrections

  • The test plan says "323 tests green" and the repro file's header says "4 of its 5 tests failed before this change". Actual at this head: the five changed files run 453/453, and the repro file has 10 tests, 8 red at base (the file grew across the PR's review rounds after those sentences were written). Description staleness only; the load-bearing claims in both sentences hold.
  • The PR body names "one unrelated pre-existing test failure (stale-bundle.test.ts) also fails on unmodified HEAD". It does not reproduce in this container: 28/28 green at head and base (an A/A control). Likely an artifact of the author's local bundle state; not a defect and not carried forward.

Findings

F1 (Suggestion, pre-existing, non-blocking): namesAnObject's path regexes are quadratic on long slash-less clauses. Stage-bisect of classifyReturn (instrumented copy of the compiled module, logs/ scratch) puts the entire superlinear cost of the long-clause ladder between the substance check's entry and the dry return: /\w[\w.-]+\/[\w.$-]+\/\w/ and /\w[\w.-]+\/[\w$-]+\.\w+/ scan a clause with no / by failing at every start position after a greedy [\w.-]+ sweep — O(n²). Measured 29ms @​5k → 504ms @​20k per classification, head and base identical (base 505ms @​20k; the regex literals are byte-identical across arms — the PR only hoisted them into a helper). The input is an auditor's model return on the schedule's critical path; a 20k-char return is rare but not capped. Not caused by this PR, not worsened by it (ladder A/B 32/32 within tolerance, witness 06-ladder-ab-compare.png).

Measured minimal fix (apply in a scratch copy, not required for this PR)

Pre-filter on the characters the regexes require — a path match is impossible without /, a code span impossible without `:

function namesAnObject(clause: string): boolean {
  if (!clause.includes('/') && !clause.includes('`')) return false;
  return ( /* the three existing regexes, unchanged */ );
}

Measured in scratch-fix-candidate.mjs: 18-case equivalence corpus (benign clauses, real path shapes, N/A, and/or, hostile runs) — 0 mismatches; hostile 20k rung 566ms → 0.00ms. Behavior-identical by construction (both regexes contain the guarded character), proven on the corpus. Ship with a fixture: a 20k slash-less clause that times out today.

F2 (Suggestion, pre-existing, non-blocking): the anchored receipt matcher is quadratic on a long space run between phrase and clause. filler-spaces ladder: 165ms/363ms/1038ms/15.5s at 2k/3k/5k/20k on head (isolated DRY_RECEIPT_RE.exec: 75ms/457ms/7.3s); base's schedule-side numbers are the same class (130ms/286ms/807ms/13.1s) — the overlapping [ \t]* … [ \t]* whitespace partition around the optional emphasis class existed as \s* … \s* on base. The failing shape (phrase + thousands of spaces + clause) never matches, so it fails toward audit; cost only bites when an auditor emits a pathological return. Same disposition as F1: document, do not block. The other six ladder shapes (dash runs, echoed phrases, prose-before, zh markers, emphasis runs, nested parens) are flat (≤5ms @​20k) on both arms.

Not covered

  • Per-commit attribution: the checkout is depth 2 (1 commit reachable vs 8 in the metadata's commits array); all results are for the aggregate HEAD^1..HEAD diff. The round-by-round narrative in the commit messages was not individually exercised.
  • Repo-wide lint/typecheck: covered by the PR's own CI (no token here to observe it). Local tsc --build of the base worktree surfaced one environmental error (@lydell/node-pty declaration missing in this container's core build) — unrelated to the PR's files; JS emitted regardless.
  • Real model-call E2E: synthetic harness-shaped transcripts are this subsystem's seam (the PR's own E2E used the same shape); no live auditor runs were performed.
  • Windows/macOS: pure TS logic; the author marks both ⚠️ untested.
  • The 4 skipped tests inside the review directory (skipped by design, not by this round); the dist/cli.js bundle path (author's E2E used it; this round used dist/index.js, the same module graph).
  • The cold-check self-correction property (a wrongly-retired chunk re-audited on the next even round) is covered by the PR's own retirement.test.ts (green) but was not independently re-probed.

Methodology

Environment: node:22-bookworm CI container, merge-ref checkout (HEAD merge, HEAD^1 base, HEAD^2 head), npm ci+npm run build pre-run at head. Control: scratch worktree at HEAD^1 under tmp/base-tree, built with the repo's own build_package.js (root node_modules reused — the effective diff leaves package.json/lockfile and packages/core untouched, git diff --stat HEAD^1..HEAD -- packages/core = 0 lines; readlink -f from the base tree asserts @qwen-code/qwen-code-core resolves to the head tree's unchanged core; per-package node_modules symlinks carry third-party deps only). Harnesses (all in this artifact dir, rerunnable): ab-retirement.mjs / ab-cleanup.mjs spawn the real compiled CLI per round/cleanup with QWEN_CODE_PROJECT_DIR/QWEN_CODE_SESSION_ID pointed at scenario dirs; classifier-probes.mjs / ladder-rung.mjs drive the exported scheduleReverseAuditRound seam; ladder.mjs runs each rung under timeout 30; base-repro-expect.mjs / mutation-expect.mjs encode expected-red cells as passing assertions (vitest JSON reporter; mutation restores the source byte-identical, verified). Gates: npx vitest run src/commands/review at head = 77 files, 2802 passed / 4 skipped / 0 failed; the five changed files alone = 453/453; base repro suite = 8 red on behavioral assertions + 2 green controls (07-base-repro-red.png, logs/base-repro-expect.txt); fine mutation (revert only the separator class) kills exactly the 11 separator-named tests with 101 others green (08-mutation-positive-control.png). Raw logs in logs/; captures in evidence/.

Evidence images

01-ab-retirement-head

02-ab-retirement-base

03-ab-cleanup-head

04-ab-cleanup-base

05-classifier-probes-head

06-ladder-ab-compare

07-base-repro-red

08-mutation-positive-control

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

Qwen Code · sandboxed verification

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind main, so it merged current main in via update-branch and will retry on the next scan. A stale base (a dependency or symbol main already changed) can fail the build without being the fix's fault; if it still fails once current, it hands off to a human.

What I found before stopping:

PR #9213 — autofix round stopped: needs a maintainer's decision (PR is not converging)

The workflow's deterministic growth brake has been over budget across rounds and the diff is still not shrinking: this counting window measures source 319 / test 1022 net lines vs budgets 400/400, with 3 prior rounds already over budget. The overage is entirely in test lines. The findings themselves are driving the growth, so the loop cannot converge by patching — this round applied NO code changes and hands the decision to a maintainer.

The decision

How to close out PR #9213 (the #9206 fix: dry receipts retiring chunks that were never examined):

  1. Accept the current state with the tail deferred — recommended (rationale below).
  2. Split the PR — land the core production fix now, move part of the test harness to follow-up PRs.
  3. Redesign — replace the receipt-certification implementation with a smaller one.

Current state (verified at HEAD 6d5b5e1ad3 this round)

  • The two Critical findings re-listed this round (R5-9, R5-5) were already fixed by the round-8 commit, which IS the current HEAD. Re-verified in code this round rather than re-fixed:
    • R5-9 — every whitespace element in the dry-receipt matcher is line-bound (DRY_RECEIPT_TAIL and the separator run use [ \t], never \s); pinned by a receipt split across two lines is not dry — the matcher is line-bound (#9213).
    • R5-5 — the clause is cut at an inline Layer walked: marker (`INLIN

Run log: https://github.com/QwenLM/qwen-code/actions/runs/31937780281


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

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM — approving per maintainer request. Note: web-shell E2E smoke and review-pr checks were still in progress at approval time (18c6b65).

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.13.

doudouOUC pushed a commit to doudouOUC/qwen-code that referenced this pull request Aug 17, 2026
…n-converged evidence (QwenLM#9213)

* fix(review): name reverse-audit certification failures, keep non-converged evidence (QwenLM#9206)

A round-5 reverse audit (PR QwenLM#9118, 12 chunks) never retired a chunk:
four territories returned substantive dry receipts in BOTH rounds 1 and
2, yet rounds 3-5 rebuilt all 12 auditors, no retirement note appeared,
and nothing anywhere said which certification condition refused the
receipts. Step 9 cleanup then deleted the prompt-record directory of
the non-converged run, so the failure could never be diagnosed.

Two root causes, reproduced end to end (issue-9206-repro.test.ts):

- The dry-receipt separator class admitted dashes and colons only, but
  honest receipts arrive separated by sentence punctuation too — "No
  new issues were found. Re-walked …" and "未发现新问题,重新走查了…"
  both reproduce the never-retire loop byte for byte. The clause after
  the separator is the part that proves the walk; the separator only
  has to show it exists. Widen the class to period, comma and
  semicolon in either width. The substance floor, the tool-call bars
  and the territory bar are unchanged — the bare stock sentence still
  reads unknown, and a yield still outranks any receipt.

- Every certification refusal landed in the same silent unknown, and
  the schedule's catch swallowed every exception without a word.
  classifyReturn now reports the first bar that fell; the schedule
  carries one diagnostic line per twice-audited chunk that neither
  retired nor yielded, and the builder prints them on stderr (stdout
  stays the deliverable). The catch names itself: a round whose
  transcripts cannot be read audits every chunk AND says so.

And the evidence half: cleanup swept the record directory of a
non-converged run unconditionally — the round-cap marker that proves
the non-convergence sat inside the very directory deleted. A record
directory holding a same-run stop marker is now kept, with a note
naming it; a converged run clears its marker, so its history sweeps
as before.

* fix(review): refuse hedged receipts, keep cross-run audit evidence (QwenLM#9213)

The widened receipt separator admitted clauses that contradict the
no-issues phrase ("…found, but I could not open the files") and let a
quoted phrase open a clause out of its own negation, retiring chunks on
their auditor's admission that nothing was checked. Judge polarity on
every separator path and admit sentence-punctuation separators only
when the phrase leads the return.

Cleanup's retention keyed on the run-epoch-fenced marker reader, so a
previous run's preserved evidence was swept by the next run — and
clearBudgetStop unlinked any marker, including a previous run's that
retention had just kept. Retention now reads the marker unfenced and
also keeps record directories carrying files older than the plan's own
capture (a killed run leaves no marker); convergence clears only its
own run's marker.

The per-chunk build path now prints its chunk's certification-failure
diagnostics like the round builder does.

* fix(review): diagnose every chunk build, invert the polarity burden (QwenLM#9213)

The per-chunk gate stamped the round on its first chunk build and
gated the certification diagnostics behind that stamp, so chunks 2..N
of a one-auditor-at-a-time round re-audited in the exact silence the
note exists to end. Print the chunk's diagnostic on every build of the
round; only the convergence and budget rulings stay gated on
admission.

The polarity guard enumerated contrast words over unbounded prose, and
prose has no last hedge — though/Yet/unfortunately/只是 all retired
chunks on clauses admitting nothing was checked, while a hedge in the
phrase's filler never reached the clause-only test at all. Invert the
burden: a clause carrying a negation or incapacity marker contradicts
the phrase whatever its length, and a contrast word without one
contradicts nothing — an innocuous "but I re-verified" retires again,
and the guard's remaining gaps land on the audit side.

The anchored receipt matcher now leads (the unanchored one truncated
clauses at nested phrase occurrences), the judged text is re-trimmed
after the budget-gap strip, and cleanup keeps a record directory whose
plan a previous cleanup already swept and skips unstatable entries one
by one instead of letting one veto the previous-run evidence.

* fix(review): line-scope the polarity guard, case-blind parrot, safe notes (QwenLM#9213)

The polarity guard read only the match's own prefix and the clause, so a
hedge BEFORE the phrase on the same line was invisible and retired the
chunk (`I could not check everything, but no new issues — re-walked…`),
and its marker list missed the incapacity/omission families the leak
probes executed — unable/failed/skipped/unchecked/skimmed, zh bare-不 and
跳过. Scope the guard to the LINE the receipt matched on — the match plus
any same-line text before the phrase, quoted spans exempted — and name the
executed marker families. Prose on any other line neither certifies nor
contradicts: scanning the whole return would refuse an honest receipt
beside an innocent "not re-reporting it" paragraph. The substance floor
now measures the phrase-STRIPPED clause (greedy with the phrase's filler),
so an echoed phrase cannot lend the floor its length, and the comment's
direction claim is corrected — a marker the list misses inside the line
still fails toward RETIREMENT, stated rather than papered over.

The parrot refusal compared case-sensitively, but the example clause
starts lowercase only because it continues the model receipt mid-sentence;
the widened sentence-punctuation separators let a parroting auditor open it
as a NEW sentence and capitalize it. Compare in any casing — no honest
clause contains the model clause verbatim in any casing.

The three new informational stderr writes — both schedule catch NOTEs and
the uncertified-chunks note — used the throwing writeStderrLine on the
CONTINUING build path. A headless retry with stderr redirected or closed
(the very QwenLM#9206 shape) made the write throw out of a catch with no outer
guard, abandoning the round that must audit every chunk. Use the Safe
writer, matching writeFindingsFile.

* fix(review): close the polarity guard's unbounded class with one receipt form (QwenLM#9213)

The polarity guard enumerated hedges over unbounded auditor prose, and
every hedge the list missed failed toward RETIREMENT — the direction the
module header declares impossible. Round 5 executes the structural fix
the round-4 Critical named: one machine-parseable receipt form, and
everything else read as unknown.

The form: the phrase LEADS the return (anchor; the lead CONTRAST list is
deleted), the receipt line stands ALONE (Budget gap: and Layer walked:
lines stripped with audit-layers' own matcher; any other prose before or
after reads unknown, named for the side it fell at), and the clause
NAMES THE WALK (a verb from the form's vocabulary, or a named object —
misses fail toward audit, the opposite direction of a marker miss).
Within the form the marker test is repaired, not extended: the strip
uses the phrase core without the greedy tail that swallowed its own
listed markers, and the quoted-span exemption that blanked a
self-admission is deleted. Zero words added to the vocabulary.

Also closes R4-1: the clause capture ran to the END of the return, so
prose after the receipt line contradicted the phrase while identical
prose before it passed. The capture stops at the line; both sides now
read unknown, symmetrically named. The reverse-audit brief mandates the
form so compliant dry returns stay one line and keep retiring.

* fix(review): line-bind the dry-receipt matcher and cut fused layer labels (QwenLM#9213)

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
water-in-stone pushed a commit to water-in-stone/qwen-code that referenced this pull request Aug 21, 2026
…t breach (QwenLM#9262)

* feat(autofix): audit the approach instead of stopping on growth-budget breach

A growth-budget breach no longer escalates to a maintainer handoff that
stops the takeover. The breach now makes the round a growth-audit round:
the agent audits the PR's approach on two axes — KISS (name a simpler
alternative or prove each piece load-bearing) and minimal change (every
hunk traces to the problem, an accepted finding, or a failing check) —
and records a machine-readable verdict that the verification gate
requires. sound re-arms the counting window at the current size and the
loop keeps solving; drift simplifies first, then continues; conflict is
the only growth path to a human, parked idempotently until a trusted
human responds.

The old divergence ladder (over budget for N rounds and not shrinking →
stop) terminated takeovers whose remaining work could still fit: the
growth it punished was protocol-mandated pinned tests (QwenLM#9213 stalled at
round 5 with two small Criticals left). A size signal now triggers a
judgment, never a stop.

Design: docs/design/autofix-growth-audit.md

* fix(autofix): update the artifact-list pin for the growth-audit.json upload entry

* fix(autofix): surface conflict verdicts past the failure.md exits and strip verdict forgery channels (QwenLM#9262)

* fix(autofix): harden the growth-audit verdict pipeline and park wake set (QwenLM#9262)

* fix(autofix): close the verdict-pipeline forgeries and loop-generated wake entrances (QwenLM#9262)

* fix(ci): drop the retired divergence rationale records (af-046/af-047) from qwen-autofix.md

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
wenshao added a commit that referenced this pull request Aug 21, 2026
Fourth round on #9678, and one item is a gap the third round's own fix
left open.

`typeNote` was hoisted out of the worktree gate but still printed only
by `runRoster`. Step 4's verify shards and Step 5's audit rounds are
built by the other two emission paths, and those are both the most
numerous agents a high-effort review launches and the ones furthest from
SKILL.md's statement of the rule — an omitted `subagent_type` there
resolves to `general-purpose` at full cost, silently. It is now a module
constant carried by all three.

The single-block path carries it on stderr, not stdout. Its stdout IS
the block the orchestrator pastes verbatim, and the delivery check
compares that against the record, so appending to stdout made every
launch differ from its record — five existing tests caught it, which is
the guard working. stderr is the channel this command already uses for
operator-facing lines, and via the ...Safe writer, since #9213 pins that
a broken stderr must not refuse a build.

The SKILL.md paragraph is cut from 816 characters to 457. The skill's
own convention keeps narrative in DESIGN.md because SKILL.md is loaded
on every orchestrator turn — quoting four numbers already verbatim in
DESIGN.md was the opposite of this change's thesis. It also named the
tool set as "read, grep, glob, shell, write, edit", four labels matching
no registered name, while the next sentence asked the orchestrator to
judge what falls outside that set; it now names the registered tools and
a test pins the prose against the registry.

The `subagent_type` assertion is a set, not an ordered array. Pinning
count and order froze the document's shape, so restating the rule at
Steps 4 and 5 — strictly more correct — would have turned it red.
pull Bot pushed a commit to mcx/qwen-code that referenced this pull request Aug 22, 2026
* perf(review): give review agents their own subagent type

Review dimension agents were launched as `general-purpose`, which is the
only builtin subagent that declares no `tools` list. That sends it down
`AgentCore.prepareTools`' inherit-everything branch —
`getFunctionDeclarations({ includeDeferred: true })` — so every agent was
handed all 51 tool schemas, deferred ones included, and re-declared them
on every turn.

Measured with a recording endpoint on a 6-file / 115-line diff, driving
one real dimension agent through its four turns: 21,178 prompt tokens of
tool declarations per turn, of which the 35 `computer_use__*` schemas
alone were 11,011. A review names 13-14 such agents.

`review-agent` declares the six tools a dimension actually uses, which
takes the `getFunctionDeclarationsFiltered` branch that `Explore` and
`statusline-setup` already use. Same fixture, same launch prompt,
nothing else changed: 3,447 tokens per turn, and one agent's delivered
prompt fell from 139,013 to 55,733 (-59.9%) — about 1.08M tokens across
one roster.

The alternative — applying the deferral that trims the orchestrator to
subagents too — was measured at 84,537 and rejected: deferral does not
go below the ~14 core tools, and `revealDeferredTool` writes to the
registry the parent session shares, so one subagent's discovery would
rewrite the orchestrator's declarations and void its prompt-cache prefix.

* fix(review): correct the review-agent docs and pin its system prompt

Addresses the review on QwenLM#9678.

The `REVIEW_BUILTIN_SUBAGENT_TYPE` docstring described a fallback that
does not exist. `AgentTool.execute` substitutes the default only when
`subagent_type` is omitted, and `loadSubagent` ends at `getBuiltinAgent`
with no default, so an unknown non-empty type fails loudly with
`Subagent "<name>" not found` rather than silently reverting. It also
claimed the literal is the only input to the branch choice; resolution
runs session > project > user > extension > builtin and builtin names
are not reserved, so a user-authored `review-agent` shadows this entry —
deliberate, since that is how any builtin is customised, but worth
documenting where the cost is explained. The same wrong mechanism was
repeated in a SKILL.test.ts comment.

The system prompt had no content assertions: blanking it left every
test green while every dimension agent would have launched with no
instructions. Its contract lines are now pinned the way the sibling
builtins pin theirs.

The stale `coverage.ts` comment ("`agentName` is `general-purpose` for
all of them") is updated — it is now the launched type, so it is not a
value to match on.

Web Shell has per-type display labels for every other builtin; adds the
en/zh pair so review agents do not render as a raw kebab-case id.

DESIGN.md now carries the per-turn record the totals decompose from, and
states the second-order share as 12,476 of 83,280 (15%) with its
arithmetic: the skills catalogue lives in the first user message, which
is re-sent every turn, so its 3,119-token saving is charged four times.
The figure was right; the prose put a per-turn number next to a
four-turn ratio and invited the reading that it was not.

* fix(review): make the review-agent prompt role-neutral and its tests bite

Second round on QwenLM#9678.

The systemPrompt was written for a diff-reading dimension, but the same
type now serves every role the review launches, and it is
`systemInstruction` — it outranks the brief that arrives as a user turn.
Agent 7 reads no diff at all (`readsDiff: false`), `verify` rules on a
findings file, and `reverse-audit` exists to look outside what the first
pass covered, so a frame naming "your diff ranges" and bounding scope to
them contradicted three required roles. A blanket "silence is better
than noise" was worse: the finder briefs carry RECALL, whose whole point
is that a withheld half-believed candidate is unrecoverable, and the
verifier brief withholds RECALL deliberately — a confidence bar from
above breaks both halves. The prompt is now role-neutral and sends the
agent to its brief; the two restraint lines `general-purpose` carried
about a shared tree are restored. Its "absolute paths everywhere" note
also contradicted SKILL.md, which tells worktree-mode agents NOT to
prefix paths for `read_file`/`grep_search`; it now says what it means
about cwd and shell.

The `subagent_type` reminder sat inside the worktree-only `paramNote`,
so the three modes with no worktree were told nothing — and an omitted
type resolves to `general-purpose`, which is the entire cost this change
removes. It is now unconditional, with the non-worktree branch covered.

Three assertions had no teeth: `getBuiltinAgent` returns `null` and
`toBeDefined()` accepts it, `?.tools ?? []` satisfies every
`not.toContain` trivially, and `tools: ['*']` takes the
inherit-everything branch that the "declares a list" filter let through.
A renamed entry failed one test before and fails three now. The SKILL.md
guard was negative-only, so a reworded "Each is a general-purpose
subagent" passed; the set of `subagent_type` literals is now asserted
positively.

The Agent tool splices every type's description into its declaration in
every request of every session, so the description is one clause.

Documented rather than fixed: `agent`, `web_fetch` and MCP tools are
real losses against the inherited surface, not free savings; and a
user-authored `.qwen/agents/review-agent.md` shadows this builtin, which
also traps the file behind `deleteSubagent`'s builtin-name check.

Re-measured end to end on the rebuilt bundle: 55,669 delivered against
139,013 (-60.0%).

* fix(review): scope the cwd rule and correct the tool-search rationale

Third round on QwenLM#9678. One of these is a regression the previous round
introduced.

The rewritten prompt said "Never `cd`", which is broader than the rule
it was porting: SKILL.md forbids `cd` into the pinned working directory,
not everywhere. The Step 4 verifier is sent to its own scratch tree and
told to work there, and that tree is a SIBLING of the review worktree
(`<worktree>-scratch-<label>`), so `run_shell_command(directory:)` fails
the workspace check and `cd` is the only remaining route. A blanket ban
left the verifier running probes in the shared worktree — the QwenLM#9207
contamination the scratch tree exists to prevent — or demoting them. The
rule is scoped now, and a test pins the scoping in both directions.

The stated reason for excluding TOOL_SEARCH was wrong, and it was
load-bearing: it was also the argument for rejecting the deferral
alternative in DESIGN.md and in the PR description. A subagent does not
share the parent's registry — `rebuildToolRegistryOnOverride` builds one
per launch and rebinds `getToolRegistry` on the override config — so a
reveal cannot reach the orchestrator's declarations or its cache prefix.
The exclusion still stands on a closed list and on the schema costing
357 tokens/turn, more than two of the tools kept. The DESIGN.md
rejection now rests on the measurement and the blast radius, and records
the argument that did not hold.

Documented, not fixed, both pre-existing and both first surfaced by a
type with a restricted list: `coreToolScheduler`'s skill-activation
reminder gates on the registry rather than on the declared list, so it
is unconditionally true and announces a tool the agent does not have;
and `buildMcpServerInstructionsReminder` has no gate at all beside two
reminders that do. Each changes what every subagent receives, so each
belongs in its own change. The capability the second-order saving buys —
review parts can no longer invoke a project skill — is now named beside
the number rather than left implied.

Re-measured on the rebuilt bundle: 55,789 delivered against 139,013
(-59.9%).

* fix(review): state the launch type on every emission path

Fourth round on QwenLM#9678, and one item is a gap the third round's own fix
left open.

`typeNote` was hoisted out of the worktree gate but still printed only
by `runRoster`. Step 4's verify shards and Step 5's audit rounds are
built by the other two emission paths, and those are both the most
numerous agents a high-effort review launches and the ones furthest from
SKILL.md's statement of the rule — an omitted `subagent_type` there
resolves to `general-purpose` at full cost, silently. It is now a module
constant carried by all three.

The single-block path carries it on stderr, not stdout. Its stdout IS
the block the orchestrator pastes verbatim, and the delivery check
compares that against the record, so appending to stdout made every
launch differ from its record — five existing tests caught it, which is
the guard working. stderr is the channel this command already uses for
operator-facing lines, and via the ...Safe writer, since QwenLM#9213 pins that
a broken stderr must not refuse a build.

The SKILL.md paragraph is cut from 816 characters to 457. The skill's
own convention keeps narrative in DESIGN.md because SKILL.md is loaded
on every orchestrator turn — quoting four numbers already verbatim in
DESIGN.md was the opposite of this change's thesis. It also named the
tool set as "read, grep, glob, shell, write, edit", four labels matching
no registered name, while the next sentence asked the orchestrator to
judge what falls outside that set; it now names the registered tools and
a test pins the prose against the registry.

The `subagent_type` assertion is a set, not an ordered array. Pinning
count and order froze the document's shape, so restating the rule at
Steps 4 and 5 — strictly more correct — would have turned it red.

* test(review): give the review-agent pins teeth, and log the monitor loss

Fifth round on QwenLM#9678.

Four assertions did not hold what they claimed. The tools array was
pinned with `toEqual`, so alphabetising a list whose order carries no
semantics turned the suite red while changing nothing; it is a set plus
a length now. The SKILL.md tool-name pin ran one way only — registry
names must appear in the body — so shrinking the registry left the skill
advertising a capability the agent no longer has, green; it is now set
equality against the sentence itself. The stderr assertion joined every
accumulated mock call in the file and passed whether or not its own
invocation emitted anything, because the enclosing beforeEach clears
only stdout. And the third emission path — the reverse-audit round
header — was asserted nowhere, so dropping its note shipped green while
every Step 5 auditor launched untyped. Each is mutation-verified against
the case that used to pass.

`run_in_background: false` is now pinned too. Dropping it defaulted
every review agent to a background launch, whose findings never return
inline — the review stalls in Step 4 with nothing to aggregate, and no
test was red.

MONITOR joins the deliberately-absent ledger. It is the one removal the
agent is actively pointed at: `shell.ts` answers a blocked foreground
sleep with "use the Monitor tool", and it is not in the subagent
exclusion set, so a `general-purpose` review agent had it. Recorded with
the measurement that qualifies it — across both arms of a real A/B
review, neither the guidance nor the tool ever fired.

* docs(review): record how to re-run the tool-surface measurement

Sixth round on QwenLM#9678. The figures are load-bearing and quoted in five
places, but a reader had no way to re-derive them — only to re-check the
arithmetic against a table.

No script is committed, because none is needed: both halves of the
measurement run on commands this repository already ships. DESIGN.md now
carries the two-arm build, the per-turn capture, the run-level ledger
read-back, and the environment facts that are part of the result — the
51-tool arm is the product default (35 of them `computer_use__*`, on by
default), not a local quirk.

Two traps are named because both are easy to hit and silent. A second
run of the same PR is an incremental re-review, so an arm sharing a
working tree with the other reuses its findings and the comparison
measures nothing; and `review mock-provider` truncates its record at
8 KB, which is smaller than one tool block, so its log cannot be the
source for per-turn token counts.

Deliberately not a `###` heading: SKILL.test.ts requires every incident
heading under that section to carry a SKILL.md pointer, and a procedure
note is not an incident the orchestrator needs pointed at.

* fix(review): drop the stderr launch note, and stop asserting the brief is a file

Seventh round on QwenLM#9678. Both items are regressions this PR introduced.

The launch note on the single-block path was moved to stderr last round
because stdout is the block the orchestrator pastes verbatim. stderr is
not a second channel: `ShellExecutionService` returns
`stdout + separator + stderr` as one string and `ShellToolInvocation`
hands that back, so the note arrived inside the very text the caller is
told to copy. It failed the same recorded-prompt equality as stdout
would, except invisibly — the five tests that catch the stdout version
see nothing — and removing it by hand is the edit the delivery gate
forbids, so those launches could enter drift/relaunch repair. The note
now has no channel on that path, and the code says why so it is not
re-added. The rule still reaches those launches: SKILL.md states it for
every `agent` call, and the two paths whose note sits OUTSIDE the ─────
blocks — the roster header and the audit-round header — still carry it.

The role-neutral prompt asserted that the assignment is a brief on disk.
Agent 8 is the reachable exception: `buildWholeDiffBlock` deliberately
writes no brief and SKILL.md appends its domain brief inline, so a
specialist launched that way was told from `systemInstruction` — which
outranks its own launch prompt — to read a file that does not exist. It
is optional and outside `requiredAgents`, so a generic diff walk would
have passed coverage in its place. The instruction is conditional now:
read the brief when the launch names one, otherwise the inline
assignment is the brief.

Both are mutation-verified against the shape that used to pass.

* docs(review): re-measure after the Agent 8 prompt fix

The conditional-brief wording is 27 tokens/turn longer than the sentence
it replaced, so the delivered figure moved: 55,789 to 55,897, and the
gap it is measured against from 83,224 to 83,116. Every citation in the
tree is realigned, including the per-turn table's system-prompt column.

Correctness bought the difference, and it is worth it — the previous
wording sent Agent 8 after a brief that does not exist. Recorded because
a figure quoted in five places is only useful while it matches what the
built bundle actually delivers, and this one is re-measured from that
bundle rather than adjusted on paper.

* docs(review): revert the test file too in the A/B recipe

The re-run recipe's baseline-arm revert list named the review sources but
not `agent-prompt.test.ts`, which imports `REVIEW_BUILTIN_SUBAGENT_TYPE`.
Reverting core removes that export, so `build:packages` fails with
TS2724 — and because the recipe chains with `&&`, the bundle step is
skipped and whatever `dist/` was there before is copied as the baseline
arm.

Reproduced rather than reasoned about: the recipe as written exits 1 on
TS2724, and adding the test file to the list exits 0. That failure is
also what happened when this measurement was first taken; the bundle was
re-run by hand afterwards, which is why the arm was still correct and
the broken recipe went unnoticed.

The note says the general rule, since the specific file will drift:
reverting the sources is not enough, anything that references them has
to go back too.

* test(review): pin the agent-type labels, and fix a comment the delta outran

Both from the round-3 sandboxed verification.

The web-shell `agentType.review-agent` keys were consumed at a live read
site and pinned by nothing. `localizeAgentTypeName` falls back to the raw
id, so a missing key is invisible — the badge renders kebab-case beside
siblings showing "Explore" and "Status Line Setup" and no test fails. The
badge is also newly visible for review agents: both surfaces elide the
type prefix only for the default type, so giving the review its own type
turned a never-rendered label into one shown on every row.

The test goes through `getTranslator`, the lookup the component uses,
because that is what makes the nastiest miss catchable: it falls back to
the English table, so a key added to `en` and forgotten in `zh-CN` still
returns something other than the raw id. Comparing the two locales'
output is what catches it — mutation-verified both ways (zh key removed:
1 red; both removed: 3 red).

The other item is a comment this PR's own previous round outran: the test
that now asserts the single-block path emits the note on NO channel still
opened by saying it emits on stderr, and its name was a beat behind too.
Behaviour and assertions were correct; the prose contradicted them, which
is exactly what would mislead the next reader about what the test pins.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/review: chunk retirement silently does not fire in the reverse-audit loop, and cleanup destroys the evidence

4 participants