Skip to content

feat(review): say when the approach, not the patch, is the open question - #9340

Merged
wenshao merged 20 commits into
QwenLM:mainfrom
qqqys:feat/review-approach-signal
Aug 24, 2026
Merged

feat(review): say when the approach, not the patch, is the open question#9340
wenshao merged 20 commits into
QwenLM:mainfrom
qqqys:feat/review-approach-signal

Conversation

@qqqys

@qqqys qqqys commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds one advisory paragraph to a posted review, and one clause to the terminal verdict line, when a pull request has taken enough rounds and grown enough since the review first measured it that the shape of the change — rather than the current patch — looks like the open question.

It is disclosure only: no finding, no cap, no change to the event, and it never fires on an Approve.

Why it's needed

Every finding /review emits is anchored to a file:line inside the current diff. That is what a finding is — and it means a review can report where an approach leaks, but never that a different approach would retire all of the leaks at once.

Measured on this repository. One change to extractAndStripMeta took three attempts across two PRs:

PR approach rounds findings source diff
#9097 add a timeout to the vm call 3 18 (9 Critical) grew ~5x
#9136 walk inside the vm, then a child process per call 6 56 (12 Critical) 228 → 920
#9325 stop evaluating; parse the literal 1

All 74 findings were individually correct. Each round found a real hole the previous patch did not cover — a getter deferring its work to the host, a serializer sharing a lexical scope with the literal it walked, a promise reaction under microtaskMode, unbounded allocation. The review was not wrong at any point. It simply had no way to say the thing that ended it, and every one of those 74 findings went away with the mechanism.

The signal was already there, filed as the wrong kind of thing. did not converge within the reverse-audit round cap was emitted four times across the two PRs — as a coverage gap ("we did not finish looking") rather than as a conclusion about the change. Nothing was responsible for reading it as "stop patching".

Why it is not a finding

Findings are what the autofix loop consumes. That loop patching each finding in turn is exactly the pattern being interrupted, so a finding here would be fixed rather than read. This addresses the human deciding what happens next, which is why it is a body paragraph and a verdict-line clause instead.

For the same reason it must not block: a pull request that is legitimately large and legitimately iterated should pay nothing for a false positive beyond one paragraph. It mirrors lowSignal, the existing disclosure-only field, down to the "the event never moves on it" contract.

Why the baseline is a baseline

Ledger.src0 records the source-diff size at the first round that measured one and carries it forward unchanged. #9136 went 228 → 920 across six rounds — about 1.3x per round, which no per-round delta would ever notice, but 4.0x cumulatively. Re-measuring each round would also let a diff that later shrinks rewrite its own baseline and erase the growth already on record.

It is the one marker field that survives truncation. The existing ruling withholds sha from a partial finding list because a truncated work list must not certify a commit range; a measurement of the diff certifies nothing, and stays true either way.

What this deliberately does not do

Stated here rather than discovered in review, because two of these limit the fix to less than the incident that motivated it:

  • It cannot see across pull requests. Every cross-round mechanism is keyed to one PR: the marker rides that PR's review bodies, the side file is named for its number, and recovery walks only its reviews. fix(core): bound the workflow meta evaluation #9097fix(core): bound the workflow meta evaluation #9136fix(core): parse the workflow meta literal instead of evaluating it #9325 as three attempts at one fix is not detectable by any extension of this machinery. Of the motivating incident, only fix(core): bound the workflow meta evaluation #9136's own six rounds and 4x growth would have fired; fix(core): bound the workflow meta evaluation #9097, at three rounds, would not have.
  • It does not count repeated non-convergence. The round-cap marker is written per run and fenced to that run's plan epoch, precisely so a stale stop cannot cap a verdict that did not stop. Summing it across rounds needs a second persisted counter — a forgeable monotone tally with nothing to re-assert it against. The paragraph therefore says only that the reverse audit stopped at its cap this round, when it did.
  • It is retroactively blank. No PR in flight carries a baseline, and unknown marker keys are dropped on read, so the signal stays silent until a PR has posted two rounds after this ships. The src0 > 0 arm is what makes the absent case degrade to silence rather than to a false "no growth".
  • The round counter fails open. Any failure recovering the side file reads as round 1, so a force-push or an account switch silently disarms the signal. That direction is deliberate for an advisory signal.

Design doc: docs/design/2026-08-17-review-approach-signal.md.

Reviewer Test Plan

How to verify

cd packages/cli && npx vitest run src/commands/review/ — 3266 passed, 8 skipped, 1 failure.

That failure is run-ledger.test.ts > refuses to append over a ledger it could not read, and it is pre-existing and unrelated: I confirmed it fails identically on a clean tree with my changes stashed. It relies on chmod making a file unreadable, which does not bind when the suite runs as root. This PR does not touch lib/run-ledger.ts (verified to be CLI session bookkeeping, not review rounds).

Coverage added:

  • compose-review.test.ts — a new approach signal block (10 cases): it fires on a round-6 4x-grown REQUEST_CHANGES and reaches both the body and the verdict line; the event, baseEvent and caps are byte-identical to the same run without it (the disclosure-only contract); it is silent on APPROVE with identical state, silent with no baseline, silent at round 4 with 10x growth, silent at round 9 with 1.2x growth, silent below the source-diff floor; the operator threshold is honoured and 0 falls back to the built-in; and the baseline carries forward unchanged when the diff shrinks, while a round with no prior baseline measures one.
  • lib/ledger.test.ts — serialization, the explicit truncation ruling (a 400-finding ledger loses sha, keeps src0), rejection of every non-positive-integer baseline, and a round trip alongside the -- comment escaping.
  • lib/review-settings.test.ts — positive-integer re-validation of approachRounds and its dialog membership.
  • save-artifact.test.ts — the field round-trips through validateVerdict; absent still loads.

To see the shape it produces, the fixture in the first case is the real incident's numbers: round 6, src0: 228, srcDiffLines: 920.

Evidence (Before & After)

Before: a sixth round on a diff that has quadrupled reads exactly like a first round on a stable one — a list of line-anchored findings, and a coverage note saying the audit did not finish.

After, on the same run, one paragraph above the findings and one clause on the verdict line:

⚠️ Round 6, and the diff has grown 4.0x since this review first measured it (228 → 920 source diff lines); the reverse audit also stopped at its round cap without converging. The findings below are anchored to the current patch, so they can only say where this approach leaks — never that a different approach would retire all of them at once. Before fixing them, a human should decide whether the shape of the change is still right. Advisory only: this does not affect the verdict, and nothing here is a blocker.

The Chinese half ships in the same Bi object, so a bilingual body carries both.

Tested on

OS Status
🍏 macOS N/A
🪟 Windows N/A
🐧 Linux

Environment (optional)

Unit tests, Node 22.23.0 on Linux.

Risk & Scope

  • Main risk or tradeoff: a false positive on a PR that is legitimately large and legitimately iterated. Bounded by design — the cost is one paragraph, never a verdict — and by three arms that must all hold (round threshold, 3x growth, and an absolute 100-source-line floor). The threshold is settings-backed so an operator can push it out or silence it.
  • Second risk: src0 is untrusted body data, like every other marker field, and unlike a finding a bare number has nothing to re-assert it against. A forged small value fires the paragraph; a forged large one silences it. The blast radius is one advisory paragraph either way, and the field's doc comment says so.
  • Contestable choice worth a reviewer's opinion: growth is measured in srcDiffLines, which excludes tests — consistent with lowSignal and the topology metric, but it means a PR ballooning purely in test code will not trip the growth arm. diffLines would catch more and fire more often.
  • Not validated / out of scope: the existing round-cap → coverageEntries path is untouched, so its cap and its exact phrase (pinned by two tests) behave as before; no new ReviewEvent, no new cappedBy id, no Finding type change and therefore no Web Shell change.
  • Breaking changes / migration notes: none. Old composed artifacts without the field still load.

Linked Issues

None. The incident behind it is #9097 / #9136 / #9325.

中文说明

这个 PR 做了什么

当一个 PR 已经经过足够多轮、并且自本审查首次测量以来 diff 增长了足够多时,在发布的审查正文里加一段提示、并在终端判定行上加一句:真正待决的可能是这次改动的整体形态,而不是当前这版补丁。

它只是披露:不产生 finding、不加 cap、不改变判定结论,并且在 Approve 上永不触发。

为什么需要

/review 产出的每一条 finding 都锚定在当前 diff 的某个 file:line 上。finding 的定义本就如此——这意味着审查能指出一个方案在哪里漏了,却无法说明换一个方案就能一次性消除全部问题

在本仓库实测。对 extractAndStripMeta 的一处改动,跨两个 PR 尝试了三次:

PR 方案 轮次 findings 源码 diff
#9097 给 vm 调用加 timeout 3 18(9 条 Critical) 增长约 5 倍
#9136 把遍历挪进 vm,最后每次调用起子进程 6 56(12 条 Critical) 228 → 920
#9325 不再求值,改为解析字面量 1

这 74 条 finding 每一条都是对的。每一轮都找出了上一版补丁没覆盖到的真实漏洞——推迟到宿主侧执行的 getter、与所遍历字面量共享词法作用域的序列化器、microtaskMode 下的 promise reaction、无界分配。审查在任何一个环节都没有出错。它只是没有办法说出那句终结问题的话;而最终,这 74 条 finding 随着机制一起消失了。

信号其实早就存在,只是被归错了类。did not converge within the reverse-audit round cap 在两个 PR 中出现了四次——被当作覆盖缺口("我们没看完"),而不是关于这次改动的结论。没有任何环节负责把它读成"别再打补丁了"。

为什么不做成 finding

finding 正是 autofix 循环消费的东西。而"逐条修 finding"的循环,恰恰是这里要打断的模式——做成 finding 只会被修掉,而不会被读到。这段话是说给"接下来要做决定的人"听的,所以它是正文段落和判定行子句。

同理它绝不能阻断:一个确实很大、确实该多轮迭代的 PR,为一次误报付出的代价不应超过一个段落。它对标既有的 lowSignal 披露字段,连"判定结论永不因它移动"这条契约都一致。

为什么基线是"基线"

Ledger.src0 记录首次测量到的源码 diff 规模,并原样向后传递。#9136 六轮从 228 走到 920——每轮约 1.3 倍,任何"逐轮增量"都发现不了,但累计是 4.0 倍。而且每轮重新测量,会让一个后来缩小的 diff 改写自己的基线,抹掉已经记录在案的增长。

它是唯一在截断后仍然保留的 marker 字段。既有规则之所以在 finding 列表被截断时丢弃 sha,是因为部分工作列表不得认证一个 commit 范围;而"diff 有多大"这个测量本身不认证任何东西,无论如何都仍然为真。

有意不做的事

在此直接写明,而不是留给评审去发现——其中两条使本修复的覆盖范围小于促成它的那次事件:

  • 它看不到跨 PR。 所有跨轮机制都以单个 PR 为键:marker 依附于该 PR 的审查正文,side file 以其编号命名,恢复流程也只遍历该 PR 的审查。fix(core): bound the workflow meta evaluation #9097fix(core): bound the workflow meta evaluation #9136fix(core): parse the workflow meta literal instead of evaluating it #9325 这种"同一件事的三次尝试",靠这套机制的任何扩展都检测不到。就那次事件而言,只有 fix(core): bound the workflow meta evaluation #9136 自身的六轮与 4 倍增长会触发;fix(core): bound the workflow meta evaluation #9097 只有三轮,不会触发。
  • 它不统计反复不收敛。 轮数上限标记是按次运行写入、并以该次运行的 plan epoch 为界的,正是为了防止一个陈旧的停止去 cap 一个并未停止的判定。跨轮累加需要第二个持久化计数器——一个可伪造的单调计数,且没有任何代码能对它再断言。因此这段话只在本轮确实发生时,说"反向审计在本轮停在了上限"。
  • 它在存量上是空白的。 在飞的 PR 都不带基线,而未知的 marker 键在读取时会被丢弃,所以本改动上线后,一个 PR 要再发布两轮该信号才可能出现。src0 > 0 这一条正是让"缺失"退化为沉默、而不是退化为一个假的"没有增长"。
  • 轮次计数 fail open。 恢复 side file 的任何失败都读作第 1 轮,因此 force-push 或账号切换会静默解除该信号。对一个建议性信号来说,这个方向是刻意的。

设计文档:docs/design/2026-08-17-review-approach-signal.md

审阅者验证方案

如何验证

cd packages/cli && npx vitest run src/commands/review/——3266 通过、8 跳过、1 失败。

那个失败是 run-ledger.test.ts > refuses to append over a ledger it could not read,属于既有失败且与本改动无关:我把改动 stash 掉后在干净树上确认它同样失败。它依赖 chmod 让文件不可读,而测试以 root 运行时该前提不成立。本 PR 不触碰 lib/run-ledger.ts(已确认它是 CLI 会话簿记,与审查轮次无关)。

新增覆盖:

  • compose-review.test.ts——新增 approach signal 组(10 个用例):在第 6 轮、4 倍增长的 REQUEST_CHANGES 上触发,且同时到达正文与判定行;event、baseEvent 与 caps 与"没有该信号的同一次运行"逐字节一致(披露-only 契约);在状态相同的 APPROVE 上沉默、无基线时沉默、第 4 轮即便 10 倍增长仍沉默、第 9 轮仅 1.2 倍增长时沉默、低于源码 diff 下限时沉默;运维阈值生效且 0 回落到内置值;diff 缩小时基线原样保留,而无先前基线的一轮会测量出一个。
  • lib/ledger.test.ts——序列化、截断规则的明确断言(400 条 finding 的 ledger 丢掉 sha、保留 src0)、对一切非正整数基线的拒绝,以及与 -- 注释转义并存的往返。
  • lib/review-settings.test.ts——approachRounds 的正整数再校验及其对话框成员资格。
  • save-artifact.test.ts——该字段可通过 validateVerdict 往返;缺失时仍可加载。

想看它产出的形态:第一个用例的 fixture 就是真实事件的数字——第 6 轮、src0: 228srcDiffLines: 920

证据(前后对比)

修复前:一个 diff 已经翻了四倍的第六轮,读起来与一个稳定改动的第一轮完全一样——一串行锚定的 findings,外加一条"审计没跑完"的覆盖说明。

修复后,同一次运行,findings 之上多一段、判定行多一句:

⚠️ 第 6 轮,且自本审查首次测量以来 diff 已增长 4.0 倍(源码 diff 行数 228 → 920);反向审计也在轮数上限处停止且未收敛。下方的发现都锚定在当前这版补丁上,因此它们只能指出这个方案在哪里漏了,而无法说明换一个方案就能一次性消除全部问题。在动手修复之前,应由人来判断这次改动的整体形态是否仍然正确。仅供参考:本段不影响判定结论,其中也没有任何阻断项。

中文半边与英文放在同一个 Bi 对象里,因此双语正文两者兼有。

测试环境

OS Status
🍏 macOS N/A
🪟 Windows N/A
🐧 Linux

环境(可选)

单元测试,Linux 上的 Node 22.23.0。

风险与范围

  • 主要风险或取舍:在一个确实很大、确实该多轮迭代的 PR 上误报。其代价在设计上就被限死——只是一个段落,永远不是判定结论——并且需要三个条件同时成立(轮次阈值、3 倍增长、100 行源码的绝对下限)。阈值走设置项,运维方可以推后或直接静默它。
  • 第二个风险:src0 与其他 marker 字段一样是不可信的正文数据,而且与 finding 不同,一个裸数字没有任何东西可供再断言。伪造一个小值会触发该段落,伪造一个大值会让它沉默。两种情况下影响半径都只是一个建议性段落,字段的文档注释里也写明了这一点。
  • 值得评审表态的可争议选择:增长以 srcDiffLines 衡量,不含测试代码——这与 lowSignal 和拓扑指标保持一致,但也意味着一个纯粹在测试代码上膨胀的 PR 不会触发增长条件。改用 diffLines 会抓到更多,也会更常触发。
  • 未验证 / 不在范围内:既有的"轮数上限 → coverageEntries"路径完全未动,其 cap 与确切措辞(由两个测试固定)行为如常;不新增 ReviewEvent、不新增 cappedBy id、不改 Finding 类型,因此也不涉及 Web Shell 改动。
  • 破坏性变更 / 迁移说明:无。不含该字段的旧 composed 产物仍可加载。

关联 Issue

无。促成它的事件是 #9097 / #9136 / #9325

Every finding /review emits is anchored to a `file:line` in the current diff.
That is what a finding is — and it means a review can report where an approach
leaks, but never that a different approach would retire all of the leaks at
once.

Measured: one change to `extractAndStripMeta` took three attempts across two
PRs. QwenLM#9097 (3 rounds, 18 findings) added a timeout to the vm call; QwenLM#9136 (6
rounds, 56 findings) moved the walk inside the vm and ended up spawning a child
process per call, growing 228 -> 920 source diff lines. QwenLM#9325 landed it in one
commit by not evaluating the literal at all. All 74 findings were individually
correct, and every one of them went away with the mechanism.

The signal was already there and filed as the wrong kind of thing: `did not
converge within the reverse-audit round cap` appeared four times across the two
PRs, as a coverage gap — "we did not finish looking" — rather than as a
conclusion about the change. Nothing was responsible for reading it as "stop
patching".

Add one advisory paragraph, and one clause on the terminal verdict line, when a
non-Approve round is past the round threshold AND its source diff has grown at
least 3x since the review first measured it. This round's round-cap stop rides
along as corroborating text when present; it is never a trigger on its own.

It is deliberately not a finding. Findings are what the autofix loop consumes,
and that loop patching each finding in turn is the pattern being interrupted —
a finding here would be fixed rather than read. It addresses the human deciding
what happens next, so it is a body paragraph and a verdict-line clause, it adds
no cap, and it never moves the event.

The baseline is a baseline, not the previous round's size: 228 -> 920 across six
rounds is ~1.3x per round, which no per-round delta would notice, but 4.0x
cumulatively. `Ledger.src0` records the first measurement and is carried forward
unchanged, so a diff that later shrinks cannot rewrite its own baseline. It is
the one marker field that survives truncation — the ruling that withholds an
anchor from a partial finding list does not extend to a measurement of the diff.

Known limits, documented rather than papered over: it cannot see across pull
requests, so the three-attempt shape that motivated it would have fired only on
a second forgeable persisted counter; and it is retroactively blank, staying
silent until a PR has posted two rounds after this ships.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 17, 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 17, 2026

Copy link
Copy Markdown
Collaborator

Re-gate on the maintainer's /triage request. Since the last full pass (3c51d0ff), the branch added nothing of its own — only merges of main, which moved ~199 commits underneath it (including the SKILL.md split #9804 and other review work). Nine of the PR's sixteen files are byte-identical to the previously reviewed state; the other seven are those same files re-merged over an evolved main, and the current diff carries the full change intact.

Template looks good ✓ — all sections present, complete bilingual translation.

Problem: observed, not theoretical. The motivating incident is three real attempts at the same change in this repo (#9097, #9136, #9325 — verified by number on the first pass), and the structural gap it names — findings anchored to file:line can say where an approach leaks but never that a different approach would retire all the leaks at once — is a genuine property of line-anchored review, not a hypothesis. #9325 has merged since the last pass, so the "the mechanism was replaced and every finding went away" claim is now on record rather than anticipated.

Direction: aligned. It improves the bundled /review skill's output for the non-converging-review case the skill already half-detects (the round-cap coverage note), as advisory disclosure modelled on the existing lowSignal contract — no verdict movement, silent when data is absent, never on an Approve, and an operator knob to delay or silence it.

Size: 344 production / 765 test / 5 schema / 121 docs lines (additions + deletions). Core paths touched remain minor: the review.approachRounds settings entry (+10, packages/cli/src/config/**) and markdown in the bundled review skill (+10). Under every escalation threshold, and feat-type, so no Tier 1 block either.

Approach: unchanged since the last pass and still deliberate — a baseline carried forward rather than re-measured, growth measured over the full merge-base range on incremental rounds, and the disclosure pinned byte-identical to a no-signal run by its own tests. One standing follow-up from earlier passes (the user-facing settings table row) remains open; it gates nothing, so it is named in the final comment rather than here.

Risk: no elevated risk signals — no files match the high-risk revert-correlated paths.

Moving on to code review. 🔍

中文说明

应维护者的 /triage 请求重新 gate。自上一次完整审查(3c51d0ff)以来,本分支没有新增任何自有提交——只有对 main 的合并,底下 main 前进了约 199 个 commit(包括 SKILL.md 拆分 #9804 等其他审查相关改动)。PR 的 16 个文件中 9 个与上次审查状态逐字节一致;其余 7 个是同一批文件在演进后的 main 之上重新合并的结果,当前 diff 完整携带了全部改动。

模板完整 ✓ —— 各节齐全,中文翻译完整。

问题:真实观测,而非理论假设。动机事件是本仓库对同一改动的三次真实尝试(#9097#9136#9325,首轮已按编号核实),它指出的结构性盲区——锚定在 file:line 的 finding 能指出方案在哪里漏,却永远说不出换一个方案能一次性消掉所有漏——是行锚定式审查的真实属性,不是假设。#9325 在上次审查后已合并,因此"机制被替换、所有 finding 随之消失"的说法现在是已记录的事实,不再是预期。

方向:对齐。它改进了内置 /review 技能在"审查不收敛"场景下的输出——该场景技能本身已半检测到(轮数上限的覆盖说明),且以既有 lowSignal 披露契约为范本——不移动判定结论、数据缺失时沉默、永不作用于 Approve,并留有运维方可调高或静默的开关。

规模:生产 344 行 / 测试 765 行 / schema 5 行 / 文档 121 行(增+删)。触及的核心路径仍然很小:review.approachRounds 设置项(+10,packages/cli/src/config/**)与内置审查技能的 markdown(+10)。低于所有升级阈值,且为 feat 类型,不触发 Tier 1 阻断。

方案:与上次审查相比未变,仍是深思熟虑——基线向前携带而非重新测量、增量轮按完整 merge-base 范围测量增长、披露文本由自身测试钉住与无信号运行逐字节一致。早前各轮遗留的一个后续跟进项(面向用户的设置表缺行)仍未解决;因其不构成任何门禁,放在最终评论中点名而非此处。

风险:无升级风险信号——没有文件匹配与 revert 相关的高风险路径。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review

Third full pass. Nine of the sixteen files are byte-identical to the previously reviewed head; the rest changed only by merging an evolved main (the SKILL.md split and other review work landed in between), so this pass re-verified every seam against the CURRENT base rather than trusting the earlier attestation. Re-stating the independent baseline for an honest comparison: persist a first-measured source-diff baseline in the existing ledger marker, gate on non-Approve + round threshold + growth factor + the module's existing non-trivial floor, measure growth over the FULL merge-base range (an incremental round's delta is not comparable to a cumulative baseline), render as bilingual paragraph + verdict-line clause, and round-trip through the persisted artifact with lenient load semantics. The PR still matches it point for point.

Re-verified against the current base code, not just the diff:

  • The src0 persistence joint needs no production change, and that is now pinned. persistRecoveredLedger carries the baseline through both seams by construction — the identity-known write spreads the recovered ledger whole, and the anonymous higher-round advance destructures away only sha/model/commitId plus the volume and churn groups, neither of which contains src0 (checked VOLUME_FIELDS/CHURN_FIELDS in the current base). The two tests in pr-context-persist.test.ts guard exactly that joint against a future switch to named fields.
  • The settings-scope claim is real machinery, not prose. operatorReviewSettings reads through loadSettings(undefined, { skipWorkspaceSettings: true }), so approachRounds inherits the "a repository cannot set review policy" property the schema description claims — same as its reverseAuditRounds sibling. Re-validation accepts only positive integers; 0 (the schema default) falls back to the built-in 5.
  • "Never fires on an Approve" reads baseEvent — the verdict before caps AND downgrades — and every event transition only demotes, so nothing can be promoted into the guard's blind spot after it runs; the downgraded-Approve test pins it.
  • The incremental seam is sound: baseline and comparison use the identical full-range measurement expression in both ledgerMarkerFor and composeReviewBody; a legacy incremental plan with no full-range size degrades to silence without losing a baseline already on record. All four arms pinned by tests.
  • The truncation ruling holds on write: src0 is stamped inside render(), so it survives every re-render of the byte-drop cascade; the 400-finding test pins sha dropped, src0 kept.
  • Backward compatibility: validateVerdict refuses present-malformed approachSignal but absent/null still loads, so pre-signal artifacts keep saving; the round clamp (Math.min(prevRound + 1, LEDGER_MAX_ROUND)) matches the marker stamp and the deferred-suggestions clause, and the at-cap agreement is pinned.

No correctness blockers, no security holes, no convention violations found. The blast radius of the whole mechanism is one advisory paragraph: src0 is untrusted body data, but the worst a forged value can do is fire or silence that paragraph — stated in the code and the design doc, and true as far as I can trace.

The one open follow-up is unchanged from earlier passes: docs/users/configuration/settings.md still has no review.approachRounds row (schema, dialog, and VS Code schema all do). Non-blocking — recorded for a follow-up.

The flow, for navigation (unchanged from the last pass, re-checked against the current diff):

sequenceDiagram
    participant P1 as fetch-pr
    participant P2 as compose-review round N
    participant P3 as posted review body
    participant P4 as pr-context round N+1
    participant P5 as prev-ledger side file
    participant P6 as compose-review round N+1
    P1->>P2: plan with full-range source-diff measurement
    P2->>P3: ledger marker with src0 baseline
    P4->>P3: walk own reviews, parseLedger keeps src0
    P4->>P5: persist recovered ledger, src0 rides along
    P6->>P5: read round and src0
    P6->>P6: fire when rounds and growth both hold
Loading
Files changed (16 of 16 shown)
File What changed
packages/cli/src/commands/review/compose-review.ts The signal itself: predicate on baseEvent, rounds, growth and floor; bilingual paragraph and verdict-line clause; round clamped at the ledger cap; baseline measured once and carried forward in the marker
packages/cli/src/commands/review/compose-review.test.ts The approach signal block: firing arm, every silence arm including downgraded Approve and legacy incremental plans, exact-boundary arms (threshold, 3x, strict floor), ledger-cap agreement, zh rendering, byte-identical disclosure contract
packages/cli/src/commands/review/fetch-pr.ts Reports fullSrcDiffLines, the source-diff size over the full merge-base range, so incremental rounds compare like with like
packages/cli/src/commands/review/fetch-pr.test.ts Pins fullSrcDiffLines against a fixture whose full range grew beyond the delta
packages/cli/src/commands/review/lib/ledger.ts Ledger.src0 field, serialized unconditionally (survives truncation, unlike sha), parsed with positive-integer validation
packages/cli/src/commands/review/lib/ledger.test.ts Serialization, truncation ruling (400 findings: sha dropped, src0 kept), garbled baselines dropped, escaping round trip
packages/cli/src/commands/review/lib/review-settings.ts approachRounds operator setting with the same positive-integer re-validation as reverseAuditRounds
packages/cli/src/commands/review/lib/review-settings.test.ts Threshold pass-through, invalid values read as absent, dialog membership
packages/cli/src/commands/review/pr-context-persist.test.ts Pins both recovery seams (spread write and anonymous advance) keeping src0
packages/cli/src/commands/review/save-artifact.ts validateVerdict: positive integers for round/src0/srcDiffLines, non-negative growth, boolean nonConverged; absent still loads
packages/cli/src/commands/review/save-artifact.test.ts Populated fixture proves passthrough; every malformed shape refused; pre-signal files still save
packages/cli/src/config/settingsSchema.ts New review.approachRounds entry, default 0 meaning built-in 5, matching the sibling convention
packages/vscode-ide-companion/schemas/settings.schema.json Same entry mirrored for the IDE companion
packages/core/src/skills/bundled/review/SKILL.md One paragraph telling the orchestrator the signal is CLI-computed, neither written nor acted on
packages/core/src/skills/bundled/review/DESIGN.md Incident narrative behind the signal, referenced from SKILL.md
docs/design/2026-08-17-review-approach-signal.md Design doc: decisions, non-goals, and the trust analysis of src0 as untrusted body data

Testing

This is an unattended run (issue_comment), so the evidence below is the PR's own CI on the reviewed commit, fetched once via the API — nothing was built or re-run here. CI has fully settled on fd10856: every real leg is green, nothing pending at fetch time. The skipped legs (macOS/Windows unit, Integration Tests CLI No Sandbox) are merge-queue-only by ci.yml design, and skipped-by-design still satisfies their required check names — pre-existing CI configuration, not caused by this PR.

The behavioural surface is deterministic text composition over machine-local files, and the suite pins it directly: the byte-identical event/baseEvent/caps case proves the disclosure-only contract (a build with the signal removed fails its own tests), the full-range supply has its own fixture, and every firing and silence arm — no baseline, early round, no growth, below floor, legacy incremental, downgraded Approve — is pinned. 2b settled the central claim, so no sandboxed-lane trigger; separately, maintainer @wenshao deep-verified this exact head locally (17/17 scripted assertions, reported in-thread) and approved at fd10856 — cited as the maintainer's own evidence, not re-run here. The author's note about a local run-ledger.test.ts failure (chmod-based test not binding under root) remains the author's claim; CI runners are not root, and the green Linux suite below is the authoritative signal for it.

CI results for fd10856 at review time:

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

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

中文说明

代码审查

第三次完整审查。16 个文件中 9 个与上次审查的 head 逐字节一致;其余文件的变化全部来自合并演进中的 main(其间 SKILL.md 拆分等审查相关改动已落地),因此本次是对照当前 base 重新核验每一个接缝,而不是沿用先前的结论。重述独立草案以便对照:把首次测量的源码 diff 基线存进既有 ledger marker,以"非 Approve + 轮次阈值 + 增长倍数 + 模块既有的非平凡下限"为门控,增长按完整 merge-base 范围测量(增量轮的 delta 不能与累计基线相比),以双语段落加判定行子句呈现,并在持久化产物中宽松加载。PR 与该草案仍然逐点对应。

对照当前基线代码逐项核实,而非只看 diff:

  • src0 持久化接缝无需生产代码改动,且现在被测试钉住。 persistRecoveredLedger 在两个接缝上都按构造携带基线——身份已知写入整体 spread 恢复出的 ledger;匿名高轮次推进只解构掉 sha/model/commitId 及 volume、churn 字段组,而这两组都不含 src0(已在当前 base 核对 VOLUME_FIELDS/CHURN_FIELDS)。pr-context-persist.test.ts 的两个测试正是防止将来改用显式字段时丢掉它。
  • 设置作用域的说法对应真实机制,而非文案。 operatorReviewSettings 经由 loadSettings(undefined, { skipWorkspaceSettings: true }) 读取,因此 approachRounds 继承了 schema 描述所宣称的"仓库不能为审查者设定审查策略"属性——与兄弟项 reverseAuditRounds 一致。重校验只接受正整数;0(schema 默认值)回落到内置的 5。
  • "永不作用于 Approve"读的是 baseEvent——cap 与降级之前的判定——且所有事件转换只做降级,没有任何路径能在谓词之后被提升进它的盲区;被降级的 Approve 有测试钉住。
  • 增量轮接缝健全:基线测量点与比较点在 ledgerMarkerForcomposeReviewBody 中使用完全相同的全范围测量表达式;没有全范围尺寸的旧版增量计划降级为沉默而不丢失已记录的基线。四个分支均有测试钉住。
  • 截断裁决在写侧成立src0render() 内写入,字节丢弃级联的每一次重渲染都保留它;400 finding 的测试钉住 sha 被丢而 src0 保留。
  • 向后兼容validateVerdict 拒绝畸形的 present 值,但缺省/null 仍可加载,信号出现之前的产物照常保存;轮次钳制与 marker 戳印、延后建议子句一致,上限处的一致性有测试钉住。

未发现正确性阻断、安全漏洞或规范违规。整个机制的影响半径是一段建议性文字:src0 是不可信的正文数据,但伪造值最坏只能让该段落触发或沉默——代码与设计文档都如此声明,且在我能追踪的范围内属实。

唯一未关闭的后续跟进项与早前各轮一致:docs/users/configuration/settings.md 仍缺 review.approachRounds 一行(schema、对话框、VS Code schema 都有)。非阻断——记录供后续跟进。

测试

本次为无人值守运行(issue_comment),以下证据是被审 commit 上 PR 自身 CI 经 API 一次性抓取的结果——此处没有构建或重跑任何东西。CI 在 fd10856 上已全部结束:所有实际运行的检查全绿,抓取时无 pending。skipped 的项(macOS/Windows 单测、Integration Tests CLI No Sandbox)按 ci.yml 设计仅在 merge queue 运行,skipped-by-design 仍满足其必选检查名——既有 CI 配置,与本 PR 无关。

行为面是机器本地文件上的确定性文本组合,测试套件直接钉住了它:事件/baseEvent/caps 逐字节一致的用例证明了"仅披露"契约(移除信号的构建会在自身测试上失败),全范围供给有专门夹具,每一条触发与沉默分支——无基线、早期轮次、无增长、低于下限、旧版增量计划、被降级的 Approve——均有测试钉住。2b 已证实核心主张,故无沙箱验证触发条件;另外,维护者 @wenshao 已在本地对此 head 做了深度验证(17/17 断言通过,已在帖内报告)并在 fd10856 上批准——此处作为维护者自己的证据引用,并非本运行重跑。作者关于本地 run-ledger.test.ts 失败的说明(chmod 测试在 root 下不生效)仍只是作者的说法;CI runner 非 root,下方绿色的 Linux 套件才是权威信号。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean re-review at the merged-up-to-date head with a fully green CI; the single reservation is the one standing doc-table follow-up, which does not block.

Stepping back: the interesting thing about this pass is what DIDN'T happen. Since the last review the branch absorbed ~199 commits of main — including the SKILL.md split and a wave of other review work — and the PR's own code came through without a single byte of drift in nine of its sixteen files, with the other seven re-merging cleanly enough that the current diff carries the full mechanism intact. I re-verified the seams against the current base rather than trusting the prior attestation, and the two claims most likely to rot across that much base movement both held: the side-file persistence needs no code change because both write paths spread src0 through by construction, and the workspace-scope exclusion is real machinery the new setting inherits, not a prose promise.

On the substance my view is unchanged from the last two passes, and the motivation got stronger: the earlier reservation that #9325 was described as "landed" while still open is gone — it merged on 2026-08-22, so the incident narrative is now fully on record. What remains is the other reservation: docs/users/configuration/settings.md still lacks the review.approachRounds row. It is a one-line doc gap, deliberately left to a follow-up because the schema, the dialog, and the VS Code schema all carry the entry — the user-facing table is the only surface missing it.

My independent proposal before reading the diff is what this PR implements, and where it goes beyond the sketch it earns it: the truncation ruling (anchor shed, measurement kept), the fail-open recovery direction, and the retroactively-blank property that keeps every in-flight PR silent instead of false. The blast radius of a false positive is one advisory paragraph, and an operator can push the threshold out or silence it. In six months this reads as a small, documented mechanism with an off switch — not something to curse.

CI is fully green on the reviewed commit and nothing is pending, so no approve-on-green deferral. Approving now, pinned to the reviewed commit; this is the bot's vote beside the maintainer's existing approval at this head. ✅

中文说明

回顾全局:这一轮值得注意的是没有发生什么。自上次审查以来,分支吸收了约 199 个 main commit——包括 SKILL.md 拆分和一批其他审查相关改动——而 PR 自己的代码穿过这些合并后,16 个文件中 9 个没有任何字节漂移,其余 7 个的重新合并也足够干净,当前 diff 完整携带了整个机制。我对照当前 base 重新核验了各接缝,而不是沿用先前的结论;两个最容易在这么大的 base 变动中失效的说法都成立:side-file 持久化无需代码改动,因为两条写入路径都按构造携带 src0;workspace 作用域排除是新设置继承的真实机制,而非文案承诺。

实质判断与前两轮一致,且动机更强了:早前"设计文档称 #9325 已落地但它仍开放"的保留意见已消失——#9325 于 2026-08-22 合并,事件叙述现在完全有据可查。剩下的是另一条保留意见:docs/users/configuration/settings.md 仍缺 review.approachRounds 一行。这是一行文档缺口,刻意留作后续跟进——schema、对话框、VS Code schema 都已带该条目,面向用户的参考表是唯一缺失的面。

我在读 diff 之前的独立草案正是本 PR 所实现的;超出草案的部分都物有所值:截断裁决(丢锚、保测量)、恢复 fail open 的方向、以及让所有在途 PR 保持沉默而非误报的"追溯为空"属性。误报的影响半径是一段建议性文字,运维方可调高或静默阈值。六个月后回看,这是一个小而有界、带文档、带开关的机制——不会让人后悔。

CI 在被审 commit 上已全绿且无 pending,因此无需等绿批准。现在批准,钉死在被审 commit 上;这是机器人在维护者已有批准之外的第二票。✅

Qwen Code · qwen3.8-max

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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20210, 494, 1471, 1575, 3686, 507 passed.

中文说明

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

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

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20210, 494, 1471, 1575, 3686, 507 passed

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

Comment thread packages/cli/src/commands/review/compose-review.ts Outdated
// by a build predating the approach signal carries no field at all, and a
// mid-upgrade load must not fail over one that only affects display. A
// PRESENT value of the wrong shape is refused like every other field here.
const approachRaw = verdict['approachSignal'] ?? null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-1: The new approachSignal branches in validateVerdict — the refusal arms for a present-but-malformed value and the absent-means-null upgrade path (verdict['approachSignal'] ?? null) — have zero tests, while the sibling deferredCount block directly below has both shapes ("refuses a present deferredCount of the wrong shape" at save-artifact.test.ts:296, "reads an absent or null deferredCount as zero" at :315). — Failure scenario: delete any one refusal arm and all 27 save-artifact tests stay green (the copy test only proves a VALID object passes through), so a composed.json carrying approachSignal: { round: -1 } or growth: NaN would be persisted into the durable schema-v1 artifact instead of refused; separately, replacing ?? null with the strict access pattern the neighbouring lowSignal block uses would make every pre-feature composed file fail to save, uncaught. — Witness: mutant deleting the refusal loop → 26 passed | 1 skipped (all green); mirrored tests (7 wrong shapes + absent + null) 9/9 pass at HEAD, 4 failed | 5 passed against the mutant. Suggested fix: mirror the deferredCount pattern — an it.each of wrong shapes asserting toThrow(/approachSignal/), plus one case with the key deleted and one with approachSignal: null, asserting the artifact persists with verdict.approachSignal === null.

中文说明

validateVerdict 中新增的 approachSignal 分支——对"存在但形态错误"值的各拒绝分支,以及"缺失即 null"的升级兼容路径(verdict['approachSignal'] ?? null)——没有任何测试;而紧邻其下的同类字段 deferredCount 两种形态都有测试(save-artifact.test.ts:296 拒绝错误形态、:315 缺失/null 读作零)。— 失败场景:删除任意一个拒绝分支,全部 27 个 save-artifact 测试仍然全绿(copy 测试只证明合法对象能透传),于是携带 approachSignal: { round: -1 }growth: NaN 的 composed.json 会被原样持久化进 schema-v1 产物而不是被拒绝;另外,若把 ?? null 换成相邻 lowSignal 块使用的严格访问模式,所有旧版本产物都会在保存时抛错且无测试拦截。— 证据:删除拒绝循环的变异体 → 26 通过 | 1 跳过(全绿);镜像补测(7 种错误形态 + 缺失 + null)在 HEAD 上 9/9 通过,对变异体 4 失败 | 5 通过。建议修复:仿照 deferredCount 的模式——用 it.each 断言 toThrow(/approachSignal/),再补"键被删除"与 approachSignal: null 两个用例,断言产物以 verdict.approachSignal === null 持久化。

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

const approachRaw = verdict['approachSignal'] ?? null;
if (approachRaw !== null) {
const signal = object(approachRaw, 'Composed verdict.approachSignal');
for (const key of ['round', 'src0', 'srcDiffLines'] as const) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-2: This loop admits 0 for round, src0, and srcDiffLines (it checks < 0), but every producer invariant guarantees strictly positive values — the signal composes only when prevSrc0 > 0, src > LOW_SIGNAL_SRC_DIFF_LINES (100) and round = prevRound + 1 ≥ 1 (compose-review.ts:1950-1978) — and lib/ledger.ts validates the same-named src0 as strictly positive on both read and write. The artifact gate is looser than the semantics of the data it certifies. — Failure scenario: a hand-composed composed.json {"round": 0, "src0": 0, "srcDiffLines": 0, "growth": 4, "nonConverged": false} passes every check and is persisted as an internally impossible record ("grew 4x from a zero baseline, at round 0") that the verdict-line renderer would quote verbatim. — Witness: probe saving that exact object → saveReviewArtifact did not throw; the artifact persisted the all-zero signal verbatim. Suggested fix: reject <= 0 here ("must be a positive integer."), matching lib/ledger.ts's src0 rule; or, if zero is deliberately admissible in the artifact, say so in the comment so the asymmetry stops looking accidental.

中文说明

该循环对 roundsrc0srcDiffLines 允许 0(只检查 < 0),但所有生产者不变量都保证严格为正——信号仅在 prevSrc0 > 0src > LOW_SIGNAL_SRC_DIFF_LINES(100)且 round = prevRound + 1 ≥ 1 时才会生成(compose-review.ts:1950-1978)——而且 lib/ledger.ts 对同名字段 src0 在读写两侧都按严格正整数校验。产物校验门比它所认证数据的语义更宽松。— 失败场景:手工构造的 composed.json {"round": 0, "src0": 0, "srcDiffLines": 0, "growth": 4, "nonConverged": false} 能通过全部检查并被持久化为一条内部不可能的记录("从 0 基线增长 4 倍、在第 0 轮"),判定行渲染器还会原样引用它。— 证据:以该对象调用保存 → 未抛错,全零信号被原样持久化。建议修复:此处改为拒绝 <= 0("must be a positive integer."),与 lib/ledger.ts 的 src0 规则一致;若确实有意允许零,请在注释中说明,让这一不对称不再像无意疏漏。

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

// entry keeps its existing coverage cap untouched, and this flag is
// never a trigger on its own — only a clause appended when the signal
// has already fired on rounds and growth.
roundCapStopped = isRoundCap;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-3: The corroborating-clause half of the signal — the roundCapStopped wiring here and both en/zh nonConverged branches in the rendered paragraph — has no test: every firing test uses a plan with no stop file, so nonConverged is always false (the save-artifact fixture's nonConverged: true exercises only the artifact validator, never this rendering). — Failure scenario: delete roundCapStopped = isRoundCap; or the (approachSignal.nonConverged ? ... : '') branches in both language blocks and all 360 tests in the touched files stay green — the clause silently vanishes from exactly the incident shape that motivated this feature (round-cap stop + ballooned diff). — Witness: mutant removing the wiring → 280 passed; the case suggested below fails under the mutant (expected false to be true) and passes at HEAD. Suggested fix (writeRoundCapStop is already imported in the test file):

const planPath = ballooned();
writeRoundCapStop(planPath, 5, 6);
// assert r.approachSignal?.nonConverged === true and
// r.body contains 'the reverse audit also stopped at its round cap without converging'
中文说明

信号的佐证子句这一半——此处 roundCapStopped 的接线,以及渲染段落中 en/zh 两处 nonConverged 分支——没有测试:所有触发用例的 plan 都没有 stop 文件,因此 nonConverged 恒为 false(save-artifact fixture 里的 nonConverged: true 只过产物校验器,到不了这里的渲染)。— 失败场景:删除 roundCapStopped = isRoundCap; 或两种语言块中的 (approachSignal.nonConverged ? ... : '') 分支,改动文件的 360 个测试全部保持绿——该子句会从恰恰催生本功能的形态(轮数上限停止 + diff 膨胀)中悄悄消失。— 证据:删除接线的变异体 → 280 通过;下面的用例在变异体下失败(expected false to be true)、在 HEAD 上通过。建议修复(writeRoundCapStop 已被测试文件引入):ballooned()writeRoundCapStop(planPath, 5, 6),断言 nonConverged === true 且正文包含相应英文子句。

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

Comment on lines +962 to +965
src0:
Number.isInteger(prev.src0) && (prev.src0 as number) > 0
? (prev.src0 as number)
: 0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-4: The one link of the src0 chain this read depends on — src0 surviving pr-context's persistRecoveredLedger into the side file — is pinned by no test on either side: src0 appears nowhere in any pr-context test, and the new compose-review tests write the side file directly, bypassing that writer. The field flows today only via spread-based writers (verified), so this is a coverage gap, not a live bug. — Failure scenario: a future edit to persistRecoveredLedger that swaps its whole-object spreads for explicit field selection — the pattern its own anonymous-recovery branch already uses — drops src0 from the side file; prevLedgerFor then reads src0: 0 every round, the approach signal never fires again in production, and every existing test stays green — the feature dies silently, failing toward silence. — Witness: grep sweep — src0 matches in pr-context tests: 0; this is the only unpinned joint in the marker→parse→persist→read chain. Suggested fix: in pr-context-persist.test.ts, recover a ledger whose marker carries src0: 228, run persistRecoveredLedger, and assert the written side file still contains "src0": 228 — ideally also across the anonymous-over-existing branch.

中文说明

此处读取所依赖的 src0 链条中,有一环没有任何一侧的测试约束——src0 能否经由 pr-contextpersistRecoveredLedger 存活进 side file:所有 pr-context 测试中都搜不到 src0,而新增的 compose-review 测试是直接写 side file 的,绕过了那个写入器。该字段目前只靠基于展开(spread)的写入方式传递(已核实),因此这是覆盖缺口,不是现存缺陷。— 失败场景:未来若把 persistRecoveredLedger 的整体展开改为显式字段挑选——它自己的匿名恢复分支已经在用这种模式——src0 就会从 side file 中掉出;prevLedgerFor 此后每轮都读到 src0: 0,approach 信号在生产上永远不再触发,而所有现有测试依旧全绿——功能静默失效。— 证据:grep 扫描——pr-context 测试中 src0 匹配数为 0;这是 marker→parse→persist→read 链条上唯一没有测试钉住的接点。建议修复:在 pr-context-persist.test.ts 中恢复一个 marker 携带 src0: 228 的 ledger,运行 persistRecoveredLedger,断言写出的 side file 仍含 "src0": 228——最好连"匿名覆盖既有文件"分支也覆盖。

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

if (event !== 'APPROVE' && prevSrc0 > 0 && input.planPath) {
const round = prevRound + 1;
const rounds =
operatorReviewSettings().approachRounds ?? APPROACH_ROUNDS_DEFAULT;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-5: The fires-at-exactly-the-threshold boundary is untested: every firing case uses round 6 against threshold 5, so an off-by-one mutant on round >= rounds stays green. — Failure scenario: mutating >= to > leaves the whole suite green; a PR whose prev ledger records round 4 (this round = 5 = threshold) should fire per the design doc ("at or past the threshold") and the schema's "must reach", but under the mutant the signal silently waits an extra round with no test noticing. — Witness: mutant >=> → 280 passed (whole suite green); the boundary case below passes at HEAD and fails under the mutant. Suggested fix: one case with prevLedger(planPath, { v: 1, round: 4, findings: [], src0: 228 }) and srcDiffLines: 920, asserting approachSignal is non-null at round 5 === the default threshold.

中文说明

"恰好在阈值处触发"的边界没有测试:所有触发用例都用第 6 轮对阈值 5,因此对 round >= rounds 的差一变异(>=>)仍然全绿。— 失败场景:把 >= 变异为 > 后整套测试全绿;而 prev ledger 记录第 4 轮的 PR(本轮 = 5 = 阈值)按设计文档("达到或超过阈值")与 schema 的 "must reach" 应当触发,但在变异体下信号会无声地多等一轮且无测试察觉。— 证据:变异体 >=> → 280 通过(整套全绿);下述边界用例在 HEAD 上通过、在变异体下失败。建议修复:补一个 prevLedger(planPath, { v: 1, round: 4, findings: [], src0: 228 })srcDiffLines: 920 的用例,断言第 5 轮(=== 默认阈值)时 approachSignal 非空。

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

`of the change is still right. Advisory only: this does not affect the verdict, ` +
`and nothing here is a blocker.`,
zh:
`⚠️ 第 ${approachSignal.round} 轮,且自本审查首次测量以来 diff 已增长 ` +

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-6: The Chinese half of this paragraph has no test — no han: true case fires the signal, although the module's convention tests bilingual renderings of other clauses that way (lines 1098, 3633, 5871 of the test file). — Failure scenario: break or truncate the zh string (e.g. drop the 仅供参考 advisory-only sentence) and all tests stay green; a PR whose description carries Han characters renders the broken paragraph in the posted body and nothing ever saw it. — Witness: mutant deleting the zh advisory sentence → 280 passed; a ballooned({ han: true }) case asserting the body contains ⚠️ 第 6 轮 and 仅供参考 passes at HEAD and fails under the mutant. Suggested fix: add that one case (coveredPlan already threads han into prDescriptionHasHan).

中文说明

本段落的中文半边没有测试——没有任何 han: true 用例触发该信号,尽管本模块对其他子句的双语渲染正是这样测的(测试文件第 1098、3633、5871 行)。— 失败场景:破坏或截断中文字符串(例如删掉"仅供参考"那句),所有测试依旧全绿;而描述含中文的 PR 会在发布正文中渲染出损坏的段落,且从未有任何测试看到。— 证据:删除中文提示句的变异体 → 280 通过;ballooned({ han: true }) 用例断言正文包含 ⚠️ 第 6 轮仅供参考,在 HEAD 上通过、在变异体下失败。建议修复:补上这一个用例(coveredPlan 已把 han 传入 prDescriptionHasHan)。

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

srcDiffLines: 920,
...over,
});
prevLedger(planPath, { v: 1, round: 5, findings: [], src0: 228 });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-7: Neither comparison of the growth arm has a boundary test: every firing case uses 920/228 ≈ 4.04x against APPROACH_GROWTH_FACTOR (3) and src 920 against the 100-line floor, so mutants on growth >= APPROACH_GROWTH_FACTOR (compose-review.ts:1970) and src > LOW_SIGNAL_SRC_DIFF_LINES (:1969) stay green — the growth-arm twin of the round-threshold boundary gap. — Failure scenario: mutating >= to > leaves the suite green; a PR growing from baseline 100 to exactly 300 source lines — exactly the documented "grown by at least 3x" — never receives the advisory, and because the baseline ratchets (src0 carries forward unchanged; a shrink cannot raise it), a PR that later shrinks can never fire at all. Symmetrically, >>= on the floor fires at exactly 100 lines, contrary to "past the floor". — Witness: mutant >=> on the factor → suite green while a probe at (src0: 100, srcDiffLines: 300) flips to approachSignal=null (baseline: {growth: 3}); mutant >>= on the floor → suite green while a probe at srcDiffLines: 100 flips to firing (baseline: null). Suggested fix: a fires-at-exactly-3x case (prevLedger src0: 100, plan srcDiffLines: 300, round 6 → expect signal with growth: 3) and a floor-boundary case (srcDiffLines: 100 → expect null).

中文说明

增长条件的两个比较都没有边界测试:所有触发用例都用 920/228 ≈ 4.04 倍对 APPROACH_GROWTH_FACTOR(3)、src 920 对 100 行下限,因此对 growth >= APPROACH_GROWTH_FACTOR(compose-review.ts:1970)和 src > LOW_SIGNAL_SRC_DIFF_LINES(:1969)的变异仍全绿——这是轮次阈值边界缺口在增长条件上的孪生版。— 失败场景:把 >= 变异为 > 后套件全绿;一个从基线 100 恰好增长到 300 行源码的 PR——正是文档所说的"至少增长 3 倍"——永远收不到提示,而且由于基线只进不退(src0 原样传递,缩小不会抬升它),之后缩小的 PR 永远无法触发。对称地,把下限的 > 变异为 >= 会在恰好 100 行时触发,与"超过下限"的表述相悖。— 证据:对倍数做 >=> 变异 → 套件全绿,而(src0: 100srcDiffLines: 300)探针翻转为 approachSignal=null(基线:{growth: 3});对下限做 >>= 变异 → 套件全绿,而 srcDiffLines: 100 探针翻转为触发(基线:null)。建议修复:补一个恰好 3 倍的触发用例(prevLedger src0: 100、plan srcDiffLines: 300、第 6 轮 → 期望信号且 growth: 3)和一个下限边界用例(srcDiffLines: 100 → 期望 null)。

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

expect(r.approachSignal).toBeNull();
});

it('honours the operator round threshold, and falls back to the built-in on 0', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-8: This test pins neither semantics of its title. The mock replaces only loadSettings (lines 57-83), so the real operatorReviewSettings re-validation runs and {approachRounds: 0} genuinely becomes absent → default 5 (verified end-to-end: at round 2 the signal is null). But the round-6 assertion .not.toBeNull() fires identically under "falls back to 5" and "gate disabled (round >= 0)", and compose-review's own ?? APPROACH_ROUNDS_DEFAULT (compose-review.ts:1953) does NOT fall back on a literal 0 — the 0→absent conversion happens only in lib/review-settings.ts. The title states a contract the unit under test does not implement. — Failure scenario: if the settings-layer re-validation ever regresses, a hand-edited approachRounds: 0 — the natural operator "off switch" — reaches compose-review and the signal fires from the first round carrying a baseline; this test stays green through the whole regression because at round 6 both semantics fire. — Witness: probe approachRounds: 0 at round 2 → approachSignal=null (fallback holds end-to-end today); the advertised contract is unobservable in this test and unimplemented in this unit. Suggested fix: make the semantics observable — compose a below-default round (prevLedger round 1, compose round 2) under approachRounds: 0 and assert null; or give compose-review an explicit positive-integer guard; or drop the 0 arm here and leave it to review-settings.test.ts, which already owns the 0→absent conversion.

中文说明

这个测试对标题所宣称的两种语义都没有钉住。mock 只替换了 loadSettings(第 57-83 行),因此真实的 operatorReviewSettings 再校验仍会运行,{approachRounds: 0} 确实会变成缺失 → 默认 5(已端到端验证:第 2 轮时信号为 null)。但第 6 轮的 .not.toBeNull() 断言在"回落到 5"和"门被禁用(round >= 0)"两种语义下同样触发;而且 compose-review 自己的 ?? APPROACH_ROUNDS_DEFAULT(compose-review.ts:1953)对字面量 0 并不回落——0→缺失的转换只发生在 lib/review-settings.ts。标题宣称的契约在被测单元中并未实现。— 失败场景:若设置层再校验将来回归,手工写入的 approachRounds: 0——运维最自然的"关闭开关"——会到达 compose-review,信号将从第一个有基线的轮次起就触发;而本测试在整个回归过程中保持绿色,因为第 6 轮在两种语义下都触发。— 证据:探针在 approachRounds: 0、第 2 轮时 → approachSignal=null(回落语义目前在端到端成立);标题宣称的契约在本测试中不可观测、在本单元中未实现。建议修复:让语义可观测——在 approachRounds: 0 下构造一个低于默认阈值的轮次(prevLedger round 1、compose round 2)并断言 null;或给 compose-review 显式的正整数守卫;或删掉此处的 0 分支,交给已经拥有 0→缺失转换的 review-settings.test.ts。

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

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.

Deferred to the next round — this round hit the ~8-finding batch bound after the merge from main (three conflicted files resolved) plus the two Criticals' re-verification and eight implemented findings. This one is next in line: the plan is the finding's first option, composing a below-default round (prev ledger round 1, compose round 2) under approachRounds: 0 and asserting null, so the fallback semantics the title claims become observable at a round where the two semantics diverge.

中文说明

延后到下一轮——本轮在合并 main(解决三个冲突文件)、复查两个 Critical 之外已实施八条 finding,达到单轮约 8 条的批次上限。本条排在下一轮首位:计划采用该 finding 的第一个方案,在 approachRounds: 0 下构造低于默认阈值的轮次(prev ledger 第 1 轮、compose 第 2 轮)并断言 null,使标题宣称的回落语义在两种语义分叉的轮次上可观测。

"type": "number",
"default": 0
},
"approachRounds": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-10: The new review.approachRounds setting is added to the CLI schema, the generated vscode editor schema, and the settings dialog, but gets no row in the exhaustive #### review table in docs/users/configuration/settings.md — which lists all five pre-existing review.* settings, including the sibling review.reverseAuditRounds — so the only operator knob this feature introduces is undocumented in the canonical settings reference. — Failure scenario: an operator consulting that reference finds no mention of review.approachRounds; the setting is discoverable only by reading schema descriptions, and a future settings-doc sweep has no way to know the row is owed. Local convention: the sibling reverseAuditRounds got its docs row in its own feature PR (PR 9203). — Witness: not run — documentation-omission claim settled by reading settings.md:110-118, settingsSchema.ts:711-795, and git history at the reviewed commit. Suggested fix: add a review.approachRounds row to the #### review table (type number, default 0 (unset)): advisory paragraph threshold, fires only with matching growth and never on Approve, 0 keeps the built-in 5, non-whole/non-positive values ignored.

中文说明

新增的 review.approachRounds 设置已加入 CLI schema、生成的 vscode 编辑器 schema 和设置对话框,但 docs/users/configuration/settings.md 中详尽的 #### review 表格——列出了全部五个既有 review.* 设置,包括同类的 review.reverseAuditRounds——没有它的行,因此本功能引入的唯一运维开关在权威设置参考中是无文档的。— 失败场景:查阅该参考的运维人员找不到 review.approachRounds;这个设置只能通过读 schema 描述发现,而未来的设置文档清理也无从知道欠了这一行。本地惯例:同类设置 reverseAuditRounds 在它自己的功能 PR(PR 9203)中就补上了文档行。— 证据:未运行——文档缺失的结论通过阅读被审 commit 上的 settings.md:110-118、settingsSchema.ts:711-795 与 git 历史得出。建议修复:在 #### review 表格中补一行 review.approachRounds(类型 number,默认 0(未设置)):提示段落阈值,仅在增长匹配时触发、Approve 上永不触发,0 保留内置的 5,非整数/非正值被忽略。

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

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.

Deferred to the next round — this round hit the ~8-finding batch bound after the merge from main plus the two Criticals' re-verification. The row is owed exactly as described and will be added to the #### review table in docs/users/configuration/settings.md next round (type number, default 0 unset, with the threshold/growth/Approve-exemption semantics the finding spells out).

中文说明

延后到下一轮——本轮在合并 main 与复查两个 Critical 之外已达到约 8 条的批次上限。该文档行确实欠着、描述无误,下一轮将按 finding 给出的语义(类型 number、默认 0 未设置,含阈值/增长/Approve 豁免说明)补入 docs/users/configuration/settings.md 的 #### review 表格。

@qqqys
qqqys dismissed qwen-code-ci-bot’s stale review August 17, 2026 14:18

已被后续 commit 取代,当前 head 需重新 review

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20310, 494, 1541, 1597, 3723, 529 passed.

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

  • packages/cli/src/commands/review/compose-review.ts:2676 — [probe] COMMENT rendering path for a fired signal never exercised
  • packages/cli/src/commands/review/compose-review.ts:965 — [probe] prevLedgerFor's garbled-src0 guard has no test
  • packages/vscode-ide-companion/schemas/settings.schema.json:263 (+2 locations) — [review] approachRounds declares number where integer is supported
  • docs/design/2026-08-17-review-approach-signal.md:98 — [probe] doc claims force-push disarms the signal; recovery re-arms it
中文说明

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

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

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20310, 494, 1541, 1597, 3723, 529 passed

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

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

Comment thread packages/cli/src/commands/review/compose-review.ts Outdated
// by a build predating the approach signal carries no field at all, and a
// mid-upgrade load must not fail over one that only affects display. A
// PRESENT value of the wrong shape is refused like every other field here.
const approachRaw = verdict['approachSignal'] ?? null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-1 (still standing since round 1): The new approachSignal branches in validateVerdict — the refusal arms for a present-but-malformed value and the absent-means-null upgrade path (verdict['approachSignal'] ?? null) — still have zero tests, while the sibling deferredCount block directly below has both shapes ("refuses a present deferredCount of the wrong shape", "reads an absent or null deferredCount as zero"). — Failure scenario: delete any one refusal arm and every save-artifact test stays green (the copy test only proves a VALID object passes through), so a composed.json carrying approachSignal: { round: -1 } or growth: NaN persists into the durable schema-v1 artifact instead of being refused; replacing ?? null with a presence-required pattern makes every pre-feature composed file fail to save, uncaught. — Witness (probe at this commit): mutant deleting the refusal block → 4 probe tests fail while all 26 existing tests stay green; mutant making the field presence-required → only the absent/null probe fails. Suggested fix: mirror the deferredCount pattern — an it.each of wrong shapes asserting toThrow(/approachSignal/), plus one case with the key deleted and one with approachSignal: null, asserting the artifact persists with verdict.approachSignal === null.

中文说明

validateVerdict 中新增的 approachSignal 分支——对"存在但形态错误"值的各拒绝分支,以及"缺失即 null"的升级兼容路径(verdict['approachSignal'] ?? null)——仍然没有任何测试(自第 1 轮起未处理);而紧邻其下的同类字段 deferredCount 两种形态都有测试。— 失败场景:删除任意一个拒绝分支,全部 save-artifact 测试仍然全绿(copy 测试只证明合法对象能透传),于是携带 approachSignal: { round: -1 }growth: NaN 的 composed.json 会被原样持久化进 schema-v1 产物而不是被拒绝;若把 ?? null 换成要求字段存在的模式,所有旧版本产物都会在保存时抛错且无测试拦截。— 证据(在本 commit 上探测):删除拒绝块的变异体 → 4 个探针用例失败而既有 26 个测试全绿;"字段必须存在"变异体 → 仅缺失/null 探针失败。建议修复:仿照 deferredCount 的模式——it.each 断言 toThrow(/approachSignal/),再补"键被删除"与 approachSignal: null 两个用例,断言产物以 verdict.approachSignal === null 持久化。

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

Comment on lines +232 to +237
for (const key of ['round', 'src0', 'srcDiffLines'] as const) {
if (
typeof signal[key] !== 'number' ||
!Number.isInteger(signal[key]) ||
(signal[key] as number) < 0
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-2 (still standing since round 1): This loop admits 0 for round, src0, and srcDiffLines (it checks < 0), but every producer invariant guarantees strictly positive values — the signal composes only when prevSrc0 > 0, src > LOW_SIGNAL_SRC_DIFF_LINES (100) and round = prevRound + 1 ≥ 1 — and lib/ledger.ts validates the same-named src0 as strictly positive on both read and write. The artifact gate is looser than the semantics of the data it certifies; unchanged since round 1, still present at this commit. — Failure scenario: a hand-composed composed.json {"round": 0, "src0": 0, "srcDiffLines": 0, "growth": 4, "nonConverged": false} passes every check and persists as an internally impossible record ("grew 4x from a zero baseline, at round 0") that the verdict-line renderer would quote verbatim. — Witness: round-1 probe saving that exact object → saveReviewArtifact did not throw and persisted the all-zero signal verbatim; round-2 re-read confirms the loop still checks < 0. Suggested fix: reject <= 0 here, matching lib/ledger.ts's src0 rule; or, if zero is deliberately admissible in the artifact, say so in the comment so the asymmetry stops looking accidental. (The error message just below sits outside the anchored range and needs the matching update to "must be a positive integer.")

Suggested change
for (const key of ['round', 'src0', 'srcDiffLines'] as const) {
if (
typeof signal[key] !== 'number' ||
!Number.isInteger(signal[key]) ||
(signal[key] as number) < 0
) {
for (const key of ['round', 'src0', 'srcDiffLines'] as const) {
if (
typeof signal[key] !== 'number' ||
!Number.isInteger(signal[key]) ||
(signal[key] as number) <= 0
) {
中文说明

该循环对 roundsrc0srcDiffLines 允许 0(只检查 < 0),但所有生产者不变量都保证严格为正——信号仅在 prevSrc0 > 0src > LOW_SIGNAL_SRC_DIFF_LINES(100)且 round = prevRound + 1 ≥ 1 时才会生成——而且 lib/ledger.ts 对同名字段 src0 在读写两侧都按严格正整数校验。产物校验门比它所认证数据的语义更宽松;自第 1 轮起未改动,本 commit 上仍然存在。— 失败场景:手工构造的 composed.json {"round": 0, "src0": 0, "srcDiffLines": 0, "growth": 4, "nonConverged": false} 能通过全部检查并被持久化为一条内部不可能的记录("从 0 基线增长 4 倍、在第 0 轮"),判定行渲染器还会原样引用它。— 证据:第 1 轮以该对象调用保存 → 未抛错且全零信号被原样持久化;第 2 轮复查确认循环仍检查 < 0。建议修复:此处改为拒绝 <= 0,与 lib/ledger.ts 的 src0 规则一致;若确实有意允许零,请在注释中说明,让这一不对称不再像无意疏漏。(紧邻其下的错误消息在锚定范围之外,需一并改为 "must be a positive integer."。)

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

Comment on lines +1242 to +1243
// has already fired on rounds and growth.
roundCapStopped = isRoundCap;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-3 (still standing since round 1): The corroborating-clause half of the signal — the roundCapStopped wiring here and both en/zh nonConverged branches in the rendered paragraph — still has no test: every firing test uses a plan with no stop file, so nonConverged is always false (the save-artifact fixture's nonConverged: true exercises only the artifact validator, never this rendering). — Failure scenario: delete roundCapStopped = isRoundCap; or the (approachSignal.nonConverged ? ... : '') branches in both language blocks and every test stays green — the clause silently vanishes from exactly the incident shape that motivated this feature (round-cap stop + ballooned diff). — Witness (probe at this commit): two mutants (wiring deleted; both clause ternaries deleted) → each gives 1 probe failure with all 288 existing tests green. Suggested fix (writeRoundCapStop is already imported in the test file):

const planPath = ballooned();
writeRoundCapStop(planPath, 5, 6);
// assert r.approachSignal?.nonConverged === true and
// r.body contains 'the reverse audit also stopped at its round cap without converging'
中文说明

信号的佐证子句这一半——此处 roundCapStopped 的接线,以及渲染段落中 en/zh 两处 nonConverged 分支——仍然没有测试:所有触发用例的 plan 都没有 stop 文件,因此 nonConverged 恒为 false(save-artifact fixture 里的 nonConverged: true 只过产物校验器,到不了这里的渲染)。— 失败场景:删除 roundCapStopped = isRoundCap; 或两种语言块中的 (approachSignal.nonConverged ? ... : '') 分支,所有测试保持全绿——该子句会从恰恰催生本功能的形态(轮数上限停止 + diff 膨胀)中悄悄消失。— 证据(在本 commit 上探测):两个变异体(删接线;删两处子句三元)→ 各产生 1 个探针失败,既有 288 个测试全绿。建议修复(writeRoundCapStop 已被测试文件引入):ballooned()writeRoundCapStop(planPath, 5, 6),断言 nonConverged === true 且正文包含相应英文子句。

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

Comment on lines +960 to +966
return {
round: Number.isInteger(prev.round) && prev.round > 0 ? prev.round : 0,
src0:
Number.isInteger(prev.src0) && (prev.src0 as number) > 0
? (prev.src0 as number)
: 0,
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-4 (still standing since round 1): The one link of the src0 chain this read depends on — src0 surviving pr-context's persistRecoveredLedger into the side file — is pinned by no test on either side: src0 appears nowhere in any pr-context test (re-verified by grep at this commit: 0 matches), and the compose-review tests write the side file directly, bypassing that writer. The field flows today only via spread-based writers (the chain is intact at this commit), so this is a coverage gap, not a live bug. — Failure scenario: a future edit to persistRecoveredLedger swapping its whole-object spreads for explicit field selection — the pattern its own anonymous-recovery branch already uses — drops src0 from the side file; prevLedgerFor then reads src0: 0 every round, the approach signal never fires again in production, and every existing test stays green — the feature dies silently, failing toward silence. — Witness: grep over pr-context-persist.test.ts at the reviewed commit: 0 matches for src0; this is the only unpinned joint in the marker→parse→persist→read chain. Suggested fix: in pr-context-persist.test.ts, recover a ledger whose marker carries src0: 228, run persistRecoveredLedger, and assert the written side file still contains "src0": 228 — ideally also across the anonymous-over-existing branch.

中文说明

此处读取所依赖的 src0 链条中,有一环仍然没有任何一侧的测试约束——src0 能否经由 pr-contextpersistRecoveredLedger 存活进 side file:所有 pr-context 测试中都搜不到 src0(在本 commit 上复查:0 处匹配),而 compose-review 测试是直接写 side file 的,绕过了那个写入器。该字段目前只靠基于展开(spread)的写入方式传递(本 commit 上链条完好),因此这是覆盖缺口,不是现存缺陷。— 失败场景:未来若把 persistRecoveredLedger 的整体展开改为显式字段挑选——它自己的匿名恢复分支已经在用这种模式——src0 就会从 side file 中掉出;prevLedgerFor 此后每轮都读到 src0: 0,approach 信号在生产上永远不再触发,而所有现有测试依旧全绿——功能静默失效。— 证据:在被审 commit 上对 pr-context-persist.test.ts grep src0:0 处匹配;这是 marker→parse→persist→read 链条上唯一没有测试钉住的接点。建议修复:在 pr-context-persist.test.ts 中恢复一个 marker 携带 src0: 228 的 ledger,运行 persistRecoveredLedger,断言写出的 side file 仍含 "src0": 228——最好连"匿名覆盖既有文件"分支也覆盖。

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

Comment on lines +2462 to +2463
zh:
`⚠️ 第 ${approachSignal.round} 轮,且自本审查首次测量以来 diff 已增长 ` +

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-6 (still standing since round 1): The Chinese half of this paragraph has no test — no han: true case fires the signal, although the module's convention tests bilingual renderings of other clauses that way. — Failure scenario: break or truncate the zh string (e.g. drop the 仅供参考 advisory-only sentence) and all tests stay green; a PR whose description carries Han characters renders the broken paragraph in the posted body and nothing ever saw it. — Witness (probe at this commit): one-character zh mutant () → Tests 281 passed (281); adding one ballooned({ han: true }) case asserting ⚠️ 第 6 轮 → 1 failed | 281 passed against the mutant, 282 passed pristine. Suggested fix: add that one case — const planPath = ballooned({ han: true }); and assert r.body contains the zh fragments (第 6 轮, 228 → 920, 仅供参考) inside the fold (coveredPlan already threads han into prDescriptionHasHan).

const planPath = ballooned({ han: true });
// assert r.body contains '⚠️ 第 6 轮' and '仅供参考' inside the 中文说明 fold
中文说明

本段落的中文半边仍无测试——没有任何 han: true 用例触发该信号,尽管本模块对其他子句的双语渲染正是这样测的。— 失败场景:破坏或截断中文字符串(例如删掉"仅供参考"那句),所有测试依旧全绿;而描述含中文的 PR 会在发布正文中渲染出损坏的段落,且从未有任何测试看到。— 证据(在本 commit 上探测):中文单字变异()→ 281 个测试全部通过;补一个 ballooned({ han: true }) 用例断言 ⚠️ 第 6 轮 后 → 对变异体 1 失败 | 281 通过,原始代码 282 通过。建议修复:补上这一个用例(coveredPlan 已把 han 传入 prDescriptionHasHan)。

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

Comment on lines +1967 to +1971
if (
Number.isFinite(src) &&
src > LOW_SIGNAL_SRC_DIFF_LINES &&
growth >= APPROACH_GROWTH_FACTOR
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-7 (still standing since round 1): Neither comparison of the growth arm has a boundary test: every firing case uses 920/228 ≈ 4.04x against APPROACH_GROWTH_FACTOR (3) and src 920 against the 100-line floor, so mutants on growth >= APPROACH_GROWTH_FACTOR and src > LOW_SIGNAL_SRC_DIFF_LINES stay green. — Failure scenario: mutating the factor comparison >= to > leaves the suite green and a PR growing from baseline 100 to exactly 300 source lines — exactly the documented "grown by at least 3x" — never receives the advisory, and because the baseline ratchets (src0 carries forward unchanged; a shrink cannot raise it), a PR that later shrinks can never fire at all. Symmetrically, >>= on the floor fires at exactly 100 lines, contrary to "past the floor". — Witness (probe at this commit): factor mutant → only the (src0: 100, srcDiffLines: 300) probe fails; floor mutant → only the src-exactly-100 probe fails; all 281 existing tests green under both mutants. Suggested fix: a fires-at-exactly-3x case (prev ledger src0: 100, plan srcDiffLines: 300, round 6 → expect signal with growth: 3) and a floor-boundary case (srcDiffLines: 100 → expect null).

// fires at exactly 3x: prevLedger src0: 100, plan srcDiffLines: 300 → growth: 3
// floor boundary: plan srcDiffLines: 100 → expect approachSignal null
中文说明

增长条件的两个比较仍无边界测试:所有触发用例都用 920/228 ≈ 4.04 倍对 APPROACH_GROWTH_FACTOR(3)、src 920 对 100 行下限,因此对 growth >= APPROACH_GROWTH_FACTORsrc > LOW_SIGNAL_SRC_DIFF_LINES 的变异仍全绿。— 失败场景:把倍数比较 >= 变异为 > 后套件全绿;一个从基线 100 恰好增长到 300 行源码的 PR——正是文档所说的"至少增长 3 倍"——永远收不到提示,而且由于基线只进不退(src0 原样传递,缩小不会抬升它),之后缩小的 PR 永远无法触发。对称地,把下限的 > 变异为 >= 会在恰好 100 行时触发,与"超过下限"的表述相悖。— 证据(在本 commit 上探测):倍数变异体 → 仅(src0: 100srcDiffLines: 300)探针失败;下限变异体 → 仅 src 恰好 100 的探针失败;两种变异下既有 281 个测试全绿。建议修复:补一个恰好 3 倍的触发用例(prev ledger src0: 100、plan srcDiffLines: 300、第 6 轮 → 期望信号且 growth: 3)和一个下限边界用例(srcDiffLines: 100 → 期望 null)。

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

Comment on lines +6412 to +6413
reviewSettingsMock.mockReturnValue({ approachRounds: 0 });
expect(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-8 (still standing since round 1): This test pins neither semantics of its title. The mock replaces only loadSettings, so the real operatorReviewSettings re-validation converts {approachRounds: 0} → undefined → default 5 — the fallback the title claims lives in lib/review-settings.ts, not in the compose-review seam under test, whose ?? APPROACH_ROUNDS_DEFAULT does not fall back on a literal 0. At round 6, .not.toBeNull() fires identically under "falls back to 5" and "gate disabled". — Failure scenario: if the settings-layer re-validation ever regresses, a hand-edited approachRounds: 0 — the natural operator off-switch — reaches compose-review and the signal fires from the first round carrying a baseline; this test stays green through the whole regression because at round 6 both semantics fire. — Witness (probe at this commit): with the settings-layer regression injected, the shipped test stayed green while a round-3 discriminator fired ({"round":3,"src0":228,"srcDiffLines":920,…}); reverted, the same probe read null — with the shipped test passing in both states. Suggested fix: make the semantics observable — compose a below-default round (prev ledger round 1, compose round 2) under approachRounds: 0 and assert null; or give compose-review an explicit positive-integer guard at this seam; or drop the 0 arm here and leave it to review-settings.test.ts, which already owns the 0→absent conversion.

中文说明

这个测试对标题所宣称的两种语义仍未钉住。mock 只替换 loadSettings,因此真实的 operatorReviewSettings 再校验会把 {approachRounds: 0} 转成 undefined → 默认 5——标题宣称的回落语义位于 lib/review-settings.ts,而不在被测的 compose-review 接缝;后者的 ?? APPROACH_ROUNDS_DEFAULT 对字面量 0 并不回落。在第 6 轮,.not.toBeNull() 在"回落到 5"和"门被禁用"两种语义下同样触发。— 失败场景:若设置层再校验将来回归,手工写入的 approachRounds: 0——运维最自然的"关闭开关"——会到达 compose-review,信号将从第一个有基线的轮次起就触发;而本测试在整个回归过程中保持绿色,因为第 6 轮在两种语义下都触发。— 证据(在本 commit 上探测):注入设置层回归后,出厂测试依旧全绿,而第 3 轮判别器触发({"round":3,"src0":228,"srcDiffLines":920,…});撤销注入后同一判别器读作 null——两种状态下出厂测试都通过。建议修复:让语义可观测——在 approachRounds: 0 下构造一个低于默认阈值的轮次(prev ledger round 1、compose round 2)并断言 null;或给 compose-review 在此接缝显式的正整数守卫;或删掉此处的 0 分支,交给已经拥有 0→缺失转换的 review-settings.test.ts。

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

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.

Deferred to the next round — same finding (R1-8), reported again on the round-2 re-post; the round-1 reply on this thread applies: this round hit the ~8-finding batch bound after the merge from main plus the Criticals' re-verification. Planned fix is unchanged: make the fallback observable at a below-default round under approachRounds: 0.

中文说明

延后到下一轮——同一 finding(R1-8)在第 2 轮重发;本线程第 1 轮的回复仍然适用:本轮在合并 main 与复查 Critical 之外已达到约 8 条的批次上限。计划修复不变:在 approachRounds: 0 下用低于默认阈值的轮次使回落语义可观测。

Comment on lines +781 to +782
approachRounds: {
type: 'number',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-10 (still standing since round 1): The new review.approachRounds setting is added to the CLI schema, the generated vscode editor schema, and the settings dialog, but gets no row in the exhaustive #### review table in docs/users/configuration/settings.md — which lists all five pre-existing review.* settings, including the sibling review.reverseAuditRounds — so the only operator knob this feature introduces is undocumented in the canonical settings reference. Re-verified at this commit: the table (settings.md:112-118) has exactly five rows, the diff never touches the file, and no doc/schema sync test exists. — Failure scenario: an operator consulting that reference finds no mention of review.approachRounds; the setting is discoverable only by reading schema descriptions, and a future settings-doc sweep has no way to know the row is owed. Local convention: the sibling reverseAuditRounds got its docs row in its own feature PR (PR 9203). Suggested fix: add a review.approachRounds row to the #### review table — type number, default 0 (unset): advisory paragraph threshold; fires only with matching growth and never on an Approve; 0 keeps the built-in 5; non-whole/non-positive values ignored.

中文说明

新增的 review.approachRounds 设置已加入 CLI schema、生成的 vscode 编辑器 schema 和设置对话框,但 docs/users/configuration/settings.md 中详尽的 #### review 表格——列出了全部五个既有 review.* 设置,包括同类的 review.reverseAuditRounds——仍没有它的行,因此本功能引入的唯一运维开关在权威设置参考中是无文档的。在本 commit 上复查:该表格(settings.md:112-118)恰好五行、diff 从未触碰该文件、也不存在文档/schema 同步测试。— 失败场景:查阅该参考的运维人员找不到 review.approachRounds;这个设置只能通过读 schema 描述发现,而未来的设置文档清理也无从知道欠了这一行。本地惯例:同类设置 reverseAuditRounds 在它自己的功能 PR(PR 9203)中就补上了文档行。建议修复:在 #### review 表格中补一行 review.approachRounds(类型 number,默认 0(未设置)):提示段落阈值,仅在增长匹配时触发、Approve 上永不触发,0 保留内置的 5,非整数/非正值被忽略。

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

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.

Deferred to the next round — same finding (R1-10), reported again on the round-2 re-post; the round-1 reply on this thread applies. The settings-reference row is scheduled for the next round.

中文说明

延后到下一轮——同一 finding(R1-10)在第 2 轮重发;本线程第 1 轮的回复仍然适用。设置参考文档行已排入下一轮。


It fires when all of:

- the composed event is not `APPROVE`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R2-2: The design doc's firing list says the signal fires when "the composed event is not APPROVE", but the implementation gates on baseEvent — the verdict BEFORE caps and presubmit downgrades (compose-review.ts:1950) — so a capped or presubmit-downgraded Approve, whose composed event is COMMENT, never fires. This bullet was accurate when round 1 wrote it; round 1's own R1-9 fix (the one-line change at compose-review.ts:1950) made it stale, so the doc is newly wrong at this commit. — Failure scenario: round 6, baseline grown 228 → 920, zero findings this round, composed COMMENT via cap or downgrade — all five documented conditions hold per the doc (the composed event is COMMENT, not APPROVE), yet nothing is emitted; a maintainer following the committed design doc would conclude the machinery malfunctioned. The exclusion is deliberate code behavior, pinned by the test "never fires when an APPROVE is downgraded to COMMENT", so the doc is the side that must change. — Witness: that pinned test was run at the reviewed commit: 1 passed (asserts baseEvent APPROVE, event COMMENT, approachSignal null).

Suggested change
- the composed event is not `APPROVE`
- this round confirmed at least one finding (the pre-cap verdict is not `APPROVE`)
中文说明

设计文档的触发条件列表写的是"合成事件不是 APPROVE",但实现的门是 baseEvent——即 caps 与 presubmit 降级之前的判定(compose-review.ts:1950)——因此一个被 cap 或被降级的 Approve(其合成事件为 COMMENT)永不触发。该条目在第 1 轮写入时是准确的;第 1 轮自己的 R1-9 修复(compose-review.ts:1950 处的一行改动)使它过时,因此在被审 commit 上文档是新近变错的。— 失败场景:第 6 轮、基线 228 → 920、本轮零发现、因 cap 或降级合成 COMMENT——按文档五个条件全部成立(合成事件是 COMMENT 而非 APPROVE),却什么也不会输出;按设计文档推理的维护者会认为机制出了故障。排除行为是代码的刻意设计,并由测试 "never fires when an APPROVE is downgraded to COMMENT" 钉住,因此该改的是文档。— 证据:在被审 commit 上运行该测试:1 通过(断言 baseEvent 为 APPROVE、event 为 COMMENT、approachSignal 为 null)。

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

@qqqys
qqqys dismissed qwen-code-ci-bot’s stale review August 17, 2026 21:19

已被后续 commit 取代,当前 head 需重新 review

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed. Suggestions are inline.

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20310, 494, 1541, 1597, 3723, 529 passed.

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

  • packages/cli/src/commands/review/compose-review.ts:2689 — [probe] COMMENT rendering path for a fired signal never exercised
  • docs/design/2026-08-17-review-approach-signal.md:97 — [review] doc claims force-push disarms the signal; recovery re-arms it
  • packages/core/src/skills/bundled/review/SKILL.md:773 — [review] SKILL.md says "on a non-Approve round"; the gate is baseEvent
  • packages/core/src/skills/bundled/review/DESIGN.md:779 — [review] narrative: "two PRs" lists three; "#9325 landed" is unmerged
中文说明

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

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

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20310, 494, 1541, 1597, 3723, 529 passed

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

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

Comment on lines +229 to +230
const approachRaw = verdict['approachSignal'] ?? null;
if (approachRaw !== null) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-1 (still standing since round 1): The new approachSignal branches in validateVerdict — the refusal arms for a present-but-malformed value and the absent-means-null upgrade path (verdict['approachSignal'] ?? null) — still have zero tests, while the sibling deferredCount block directly below has both shapes ("refuses a present deferredCount of the wrong shape", "reads an absent or null deferredCount as zero"). — Failure scenario: delete any one refusal arm and every save-artifact test stays green (the copy test only proves a VALID object passes through), so a composed.json carrying approachSignal: { round: -1 } or growth: NaN persists into the durable schema-v1 artifact instead of being refused; replacing ?? null with a presence-required pattern makes every pre-feature composed file fail to save, uncaught. — Witness (probe at this commit): mutant deleting the refusal block → 4 probe tests fail while all 26 existing tests stay green; mutant making the field presence-required → only the absent/null probe fails. Suggested fix: mirror the deferredCount pattern — an it.each of wrong shapes asserting toThrow(/approachSignal/), plus one case with the key deleted and one with approachSignal: null, asserting the artifact persists with verdict.approachSignal === null.

中文说明

validateVerdict 中新增的 approachSignal 分支——对"存在但形态错误"值的各拒绝分支,以及"缺失即 null"的升级兼容路径(verdict['approachSignal'] ?? null)——仍然没有任何测试(自第 1 轮起未处理);而紧邻其下的同类字段 deferredCount 两种形态都有测试。— 失败场景:删除任意一个拒绝分支,全部 save-artifact 测试仍然全绿(copy 测试只证明合法对象能透传),于是携带 approachSignal: { round: -1 }growth: NaN 的 composed.json 会被原样持久化进 schema-v1 产物而不是被拒绝;若把 ?? null 换成要求字段存在的模式,所有旧版本产物都会在保存时抛错且无测试拦截。— 证据(在本 commit 上探测):删除拒绝块的变异体 → 4 个探针用例失败而既有 26 个测试全绿;"字段必须存在"变异体 → 仅缺失/null 探针失败。建议修复:仿照 deferredCount 的模式——it.each 断言 toThrow(/approachSignal/),再补"键被删除"与 approachSignal: null 两个用例,断言产物以 verdict.approachSignal === null 持久化。

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

Comment thread packages/cli/src/commands/review/save-artifact.ts Outdated
// entry keeps its existing coverage cap untouched, and this flag is
// never a trigger on its own — only a clause appended when the signal
// has already fired on rounds and growth.
roundCapStopped = isRoundCap;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-3 (still standing since round 1): The corroborating-clause half of the signal — the roundCapStopped wiring here and both en/zh nonConverged branches in the rendered paragraph — still has no test: every firing test uses a plan with no stop file, so nonConverged is always false (the save-artifact fixture's nonConverged: true exercises only the artifact validator, never this rendering). — Failure scenario: delete roundCapStopped = isRoundCap; or the (approachSignal.nonConverged ? ... : '') branches in both language blocks and every test stays green — the clause silently vanishes from exactly the incident shape that motivated this feature (round-cap stop + ballooned diff). — Witness (probe at this commit): two mutants (wiring deleted; both clause ternaries deleted) → each gives 1 probe failure with all 288 existing tests green. Suggested fix (writeRoundCapStop is already imported in the test file):

const planPath = ballooned();
writeRoundCapStop(planPath, 5, 6);
// assert r.approachSignal?.nonConverged === true and
// r.body contains 'the reverse audit also stopped at its round cap without converging'
中文说明

信号的佐证子句这一半——此处 roundCapStopped 的接线,以及渲染段落中 en/zh 两处 nonConverged 分支——仍然没有测试:所有触发用例的 plan 都没有 stop 文件,因此 nonConverged 恒为 false(save-artifact fixture 里的 nonConverged: true 只过产物校验器,到不了这里的渲染)。— 失败场景:删除 roundCapStopped = isRoundCap; 或两种语言块中的 (approachSignal.nonConverged ? ... : '') 分支,所有测试保持全绿——该子句会从恰恰催生本功能的形态(轮数上限停止 + diff 膨胀)中悄悄消失。— 证据(在本 commit 上探测):两个变异体(删接线;删两处子句三元)→ 各产生 1 个探针失败,既有 288 个测试全绿。建议修复(writeRoundCapStop 已被测试文件引入):ballooned()writeRoundCapStop(planPath, 5, 6),断言 nonConverged === true 且正文包含相应英文子句。

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

Comment on lines +962 to +965
src0:
Number.isInteger(prev.src0) && (prev.src0 as number) > 0
? (prev.src0 as number)
: 0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-4 (still standing since round 1): The one link of the src0 chain this read depends on — src0 surviving pr-context's persistRecoveredLedger into the side file — is pinned by no test on either side: src0 appears nowhere in any pr-context test (re-verified by grep at this commit: 0 matches), and the compose-review tests write the side file directly, bypassing that writer. The field flows today only via spread-based writers (the chain is intact at this commit), so this is a coverage gap, not a live bug. — Failure scenario: a future edit to persistRecoveredLedger swapping its whole-object spreads for explicit field selection — the pattern its own anonymous-recovery branch already uses — drops src0 from the side file; prevLedgerFor then reads src0: 0 every round, the approach signal never fires again in production, and every existing test stays green — the feature dies silently, failing toward silence. — Witness: grep over pr-context-persist.test.ts at the reviewed commit: 0 matches for src0; this is the only unpinned joint in the marker→parse→persist→read chain. Suggested fix: in pr-context-persist.test.ts, recover a ledger whose marker carries src0: 228, run persistRecoveredLedger, and assert the written side file still contains "src0": 228 — ideally also across the anonymous-over-existing branch.

中文说明

此处读取所依赖的 src0 链条中,有一环仍然没有任何一侧的测试约束——src0 能否经由 pr-contextpersistRecoveredLedger 存活进 side file:所有 pr-context 测试中都搜不到 src0(在本 commit 上复查:0 处匹配),而 compose-review 测试是直接写 side file 的,绕过了那个写入器。该字段目前只靠基于展开(spread)的写入方式传递(本 commit 上链条完好),因此这是覆盖缺口,不是现存缺陷。— 失败场景:未来若把 persistRecoveredLedger 的整体展开改为显式字段挑选——它自己的匿名恢复分支已经在用这种模式——src0 就会从 side file 中掉出;prevLedgerFor 此后每轮都读到 src0: 0,approach 信号在生产上永远不再触发,而所有现有测试依旧全绿——功能静默失效。— 证据:在被审 commit 上对 pr-context-persist.test.ts grep src0:0 处匹配;这是 marker→parse→persist→read 链条上唯一没有测试钉住的接点。建议修复:在 pr-context-persist.test.ts 中恢复一个 marker 携带 src0: 228 的 ledger,运行 persistRecoveredLedger,断言写出的 side file 仍含 "src0": 228——最好连"匿名覆盖既有文件"分支也覆盖。

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

const round = prevRound + 1;
const rounds =
operatorReviewSettings().approachRounds ?? APPROACH_ROUNDS_DEFAULT;
if (round >= rounds) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-5 (still standing since round 1): The fires-at-exactly-the-threshold boundary is untested: every firing case uses round 6 against threshold 5, so an off-by-one mutant on round >= rounds stays green. — Failure scenario: mutating >= to > leaves the whole suite green; a PR whose prev ledger records round 4 (this round = 5 = threshold) should fire per the design doc ("at or past the threshold") and the schema's "must reach", but under the mutant the signal silently waits an extra round with no test noticing. — Witness (probe at this commit): mutant >=> → 1 probe failure with all 288 existing tests green (probe: prev ledger round 4 → round 5 = exactly the default threshold). Suggested fix: one case with a prev ledger { v: 1, round: 4, findings: [], src0: 228 } and srcDiffLines: 920, asserting approachSignal is non-null at round 5 === the default threshold.

prevLedger(planPath, { v: 1, round: 4, findings: [], src0: 228 });
// compose round 5 === APPROACH_ROUNDS_DEFAULT → expect r.approachSignal non-null
中文说明

"恰好在阈值处触发"的边界仍无测试:所有触发用例都用第 6 轮对阈值 5,因此对 round >= rounds 的差一变异(>=>)仍然全绿。— 失败场景:变异后整套测试全绿;而 prev ledger 记录第 4 轮的 PR(本轮 = 5 = 阈值)按设计文档("达到或超过阈值")与 schema 的 "must reach" 应当触发,但在变异体下信号会无声地多等一轮且无测试察觉。— 证据(在本 commit 上探测):变异体 >=> → 1 个探针失败,既有 288 个测试全绿(探针:prev ledger 第 4 轮 → 本轮 5 === 默认阈值)。建议修复:补一个 prev ledger 为 { v: 1, round: 4, findings: [], src0: 228 }srcDiffLines: 920 的用例,断言第 5 轮(=== 默认阈值)时 approachSignal 非空。

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

expect(r.approachSignal).toBeNull();
});

it('honours the operator round threshold, and falls back to the built-in on 0', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-8 (still standing since round 1): This test pins neither semantics of its title. The mock replaces only loadSettings, so the real operatorReviewSettings re-validation converts {approachRounds: 0} → undefined → default 5 — the fallback the title claims lives in lib/review-settings.ts, not in the compose-review seam under test, whose ?? APPROACH_ROUNDS_DEFAULT does not fall back on a literal 0. At round 6, .not.toBeNull() fires identically under "falls back to 5" and "gate disabled". — Failure scenario: if the settings-layer re-validation ever regresses, a hand-edited approachRounds: 0 — the natural operator off-switch — reaches compose-review and the signal fires from the first round carrying a baseline; this test stays green through the whole regression because at round 6 both semantics fire. — Witness (probe at this commit): with the settings-layer regression injected, the shipped test stayed green while a round-3 discriminator fired ({"round":3,"src0":228,"srcDiffLines":920,…}); reverted, the same probe read null — with the shipped test passing in both states. Suggested fix: make the semantics observable — compose a below-default round (prev ledger round 1, compose round 2) under approachRounds: 0 and assert null; or give compose-review an explicit positive-integer guard at this seam; or drop the 0 arm here and leave it to review-settings.test.ts, which already owns the 0→absent conversion.

中文说明

这个测试对标题所宣称的两种语义仍未钉住。mock 只替换 loadSettings,因此真实的 operatorReviewSettings 再校验会把 {approachRounds: 0} 转成 undefined → 默认 5——标题宣称的回落语义位于 lib/review-settings.ts,而不在被测的 compose-review 接缝;后者的 ?? APPROACH_ROUNDS_DEFAULT 对字面量 0 并不回落。在第 6 轮,.not.toBeNull() 在"回落到 5"和"门被禁用"两种语义下同样触发。— 失败场景:若设置层再校验将来回归,手工写入的 approachRounds: 0——运维最自然的"关闭开关"——会到达 compose-review,信号将从第一个有基线的轮次起就触发;而本测试在整个回归过程中保持绿色,因为第 6 轮在两种语义下都触发。— 证据(在本 commit 上探测):注入设置层回归后,出厂测试依旧全绿,而第 3 轮判别器触发({"round":3,"src0":228,"srcDiffLines":920,…});撤销注入后同一判别器读作 null——两种状态下出厂测试都通过。建议修复:让语义可观测——在 approachRounds: 0 下构造一个低于默认阈值的轮次(prev ledger round 1、compose round 2)并断言 null;或给 compose-review 在此接缝显式的正整数守卫;或删掉此处的 0 分支,交给已经拥有 0→缺失转换的 review-settings.test.ts。

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

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.

Deferred to the next round — same finding (R1-8), reported again on the round-3 re-post; see the round-1 reply on this thread. Batch bound engaged this round (merge from main + eight implemented findings); this is scheduled next with the below-default-round discriminator approach.

中文说明

延后到下一轮——同一 finding(R1-8)在第 3 轮重发;见本线程第 1 轮的回复。本轮受批次上限约束(合并 main + 实施八条 finding);本条已排入下一轮,采用低于默认阈值轮次的判别方案。

'Lower the reverse-audit loop\'s round cap for every high-effort review. The cap is normally chosen from the diff topology (10 small / 5 chunked; a huge diff is 3 when the run has a review deadline and 5 when it does not, because that reduction answers a CI ceiling and applies only where one exists) because a round costs one agent on a small diff and ~90 minutes on a huge one; this setting can only LOWER whichever tier applies, never raise it — a value that is not a whole number above zero, or that is out of range (below 3, or above the plan\'s own tier), is ignored and leaves the tier alone — JSON Schema has no integer type here, so a fraction validates in an editor and is then discarded at runtime. Understand what it buys before enabling: the loop ends on two consecutive dry rounds, so cutting the cap does not make reviews converge sooner, it makes them stop before converging more often — and every such stop is disclosed as unreviewed scope and caps the verdict at Comment, so a cheaper review is also one that can no longer Approve. To spend LESS on reviews generally, prefer "effort". Nothing here makes a loop run LONGER: a review deadline bounds a run rather than extending it, and on a huge diff setting one lowers the cap from 5 to 3 rather than raising it. Only honored from User, System, and SystemDefaults settings scopes; values set in Workspace settings are ignored, so a repository cannot set review policy for its reviewers.',
showInDialog: true,
},
approachRounds: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-10 (still standing since round 1): The new review.approachRounds setting is added to the CLI schema, the generated vscode editor schema, and the settings dialog, but gets no row in the exhaustive #### review table in docs/users/configuration/settings.md — which lists all five pre-existing review.* settings, including the sibling review.reverseAuditRounds — so the only operator knob this feature introduces is undocumented in the canonical settings reference. Re-verified at this commit: the table (settings.md:112-118) has exactly five rows, the diff never touches the file, and no doc/schema sync test exists. — Failure scenario: an operator consulting that reference finds no mention of review.approachRounds; the setting is discoverable only by reading schema descriptions, and a future settings-doc sweep has no way to know the row is owed. Local convention: the sibling reverseAuditRounds got its docs row in its own feature PR (PR 9203). Suggested fix: add a review.approachRounds row to the #### review table — type number, default 0 (unset): advisory paragraph threshold; fires only with matching growth and never on an Approve; 0 keeps the built-in 5; non-whole/non-positive values ignored.

中文说明

新增的 review.approachRounds 设置已加入 CLI schema、生成的 vscode 编辑器 schema 和设置对话框,但 docs/users/configuration/settings.md 中详尽的 #### review 表格——列出了全部五个既有 review.* 设置,包括同类的 review.reverseAuditRounds——仍没有它的行,因此本功能引入的唯一运维开关在权威设置参考中是无文档的。在本 commit 上复查:该表格(settings.md:112-118)恰好五行、diff 从未触碰该文件、也不存在文档/schema 同步测试。— 失败场景:查阅该参考的运维人员找不到 review.approachRounds;这个设置只能通过读 schema 描述发现,而未来的设置文档清理也无从知道欠了这一行。本地惯例:同类设置 reverseAuditRounds 在它自己的功能 PR(PR 9203)中就补上了文档行。建议修复:在 #### review 表格中补一行 review.approachRounds(类型 number,默认 0(未设置)):提示段落阈值,仅在增长匹配时触发、Approve 上永不触发,0 保留内置的 5,非整数/非正值被忽略。

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

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.

Deferred to the next round — same finding (R1-10), reported again on the round-3 re-post; see the round-1 reply on this thread. The settings-reference row remains scheduled for the next round.

中文说明

延后到下一轮——同一 finding(R1-10)在第 3 轮重发;见本线程第 1 轮的回复。设置参考文档行仍排在下一轮。


It fires when all of:

- the composed event is not `APPROVE`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R2-2: The design doc's firing list says the signal fires when "the composed event is not APPROVE", but the implementation gates on baseEvent — the verdict BEFORE caps and presubmit downgrades (compose-review.ts:1950) — so a capped or presubmit-downgraded Approve, whose composed event is COMMENT, never fires. This bullet was accurate when round 1 wrote it; round 1's own R1-9 fix (the one-line change at compose-review.ts:1950) made it stale, so the doc is newly wrong at this commit. — Failure scenario: round 6, baseline grown 228 → 920, zero findings this round, composed COMMENT via cap or downgrade — all five documented conditions hold per the doc (the composed event is COMMENT, not APPROVE), yet nothing is emitted; a maintainer following the committed design doc would conclude the machinery malfunctioned. The exclusion is deliberate code behavior, pinned by the test "never fires when an APPROVE is downgraded to COMMENT", so the doc is the side that must change. — Witness: that pinned test was run at the reviewed commit: 1 passed (asserts baseEvent APPROVE, event COMMENT, approachSignal null).

Suggested change
- the composed event is not `APPROVE`
- this round confirmed at least one finding (the pre-cap verdict is not `APPROVE`)
中文说明

设计文档的触发条件列表写的是"合成事件不是 APPROVE",但实现的门是 baseEvent——即 caps 与 presubmit 降级之前的判定(compose-review.ts:1950)——因此一个被 cap 或被降级的 Approve(其合成事件为 COMMENT)永不触发。该条目在第 1 轮写入时是准确的;第 1 轮自己的 R1-9 修复(compose-review.ts:1950 处的一行改动)使它过时,因此在被审 commit 上文档是新近变错的。— 失败场景:第 6 轮、基线 228 → 920、本轮零发现、因 cap 或降级合成 COMMENT——按文档五个条件全部成立(合成事件是 COMMENT 而非 APPROVE),却什么也不会输出;按设计文档推理的维护者会认为机制出了故障。排除行为是代码的刻意设计,并由测试 "never fires when an APPROVE is downgraded to COMMENT" 钉住,因此该改的是文档。— 证据:在被审 commit 上运行该测试:1 通过(断言 baseEvent 为 APPROVE、event 为 COMMENT、approachSignal 为 null)。

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

expect(r.body).toMatch(/"src0":228/);
});

it('compares the full-range size on an incremental round', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R3-1: Baseline carry-forward (src0 preserved unchanged in the emitted ledger marker) is pinned by a test only for non-incremental rounds — none of the incremental tests added by the R2-1 fix asserts the marker keeps the previous src0. The shrink-carry-forward test above uses a non-incremental plan; this test and the signal-null case with src0: 100 carry a previous baseline but assert only r.approachSignal, never the emitted marker in r.body. — Failure scenario: a mutant/refactor of ledgerMarkerFor's src0 = prevSrc0 > 0 ? prevSrc0 : measured that re-measures when the round is incremental leaves the whole suite green — the same-round signal reads prevSrc0 from the PREVIOUS ledger file, so same-round assertions still pass — while the marker the NEXT round reads is corrupted: the baseline is rewritten to the current full-range size, erasing the growth record the carry-forward rule exists to protect; a PR that grew 4x over six rounds can then never reach the 3x gate again and the advisory silently never fires. — Witness (probe at the reviewed commit, flips both ways): the mutant leaves 343 tests green; adding the assertion below with the mutant still applied fails (expected '⚠️ Round 6…' to match /"src0":228/ — the mutant emits "src0":920); mutant reverted → green. Suggested fix — add the marked line, plus one legacy-incremental case with a previous baseline asserting the same:

    expect(r.approachSignal).toMatchObject({
      src0: 228,
      srcDiffLines: 920,
    });
    expect(r.body).toMatch(/"src0":228/); // marker keeps the previous baseline
中文说明

基线进位(发射的 ledger marker 原样保留先前的 src0)只有非增量轮次的测试钉住——R2-1 修复新增的增量用例没有一个断言 marker 保留先前的 src0。上方的"缩小仍进位"用例使用非增量 plan;本用例与 src0: 100 的信号沉默用例都带有先前基线,却只断言 r.approachSignal,从不断言 r.body 中发射的 marker。— 失败场景:对 ledgerMarkerForsrc0 = prevSrc0 > 0 ? prevSrc0 : measured 做"增量轮重新测量"的变异(或重构)后整套测试仍全绿——同轮信号读的是上一轮 ledger 文件中的 prevSrc0,同轮断言依旧通过——而下一轮读到的 marker 已被污染:基线被改写为当前全量大小,抹掉进位规则本要保护的增长记录;一个六轮增长 4 倍的 PR 将永远无法再达到 3 倍门槛,提示永远静默。— 证据(在被审 commit 上探测,双向翻转):变异体下 343 个测试全绿;变异体仍在时补上下方断言则失败(expected '⚠️ Round 6…' to match /"src0":228/——变异体发射 "src0":920);撤销变异后全绿。建议修复:补上标记行断言,并补一个带先前基线的旧版增量(无 full-range 值)用例作同样断言。

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

Comment thread packages/cli/src/commands/review/fetch-pr.test.ts Outdated

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20310, 494, 1541, 1597, 3723, 529 passed.

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

  • packages/cli/src/commands/review/save-artifact.ts:229 — [probe] R1-1 (still standing): approachSignal validator absence/shape arms have no test
  • packages/cli/src/commands/review/compose-review.ts:1249 — [probe] R1-3 (still standing): nonConverged clause + COMMENT rendering path have no test pairing
  • packages/cli/src/commands/review/compose-review.ts:965 — [review] R1-4 (still standing): src0 survival through pr-context persistRecoveredLedger pinned by no test
  • packages/cli/src/commands/review/compose-review.ts:1960 — [probe] R1-5 (still standing): round-threshold boundary (>=) unpinned by any test
  • packages/cli/src/commands/review/compose-review.ts:2476 — [probe] R1-6 (still standing): Chinese half of the approach paragraph has no test
  • packages/cli/src/commands/review/compose-review.ts:1983 — [probe] R1-7 (still standing): growth-factor and floor boundaries unpinned by any test
  • packages/cli/src/commands/review/compose-review.test.ts:6409 — [review] R1-8 (still standing): threshold-fallback test pins neither semantics of its title
  • packages/cli/src/config/settingsSchema.ts:781 — [review] R1-10 (still standing): review.approachRounds missing from the settings reference table
  • docs/design/2026-08-17-review-approach-signal.md:39 — [review] R2-2 (still standing): design doc firing list says 'composed event'; the gate is baseEvent
  • packages/cli/src/commands/review/compose-review.test.ts:6458 — [review] R3-1 (still standing): incremental rounds never assert marker baseline carry-forward
  • packages/cli/src/commands/review/compose-review.ts:1968 — [review] load-bearing measurement expression hand-inlined twice (baseline stamp and growth numerator)
  • packages/cli/src/commands/review/compose-review.ts:3560 — [probe] verdict-line rendering only half-paired with tests
  • packages/core/src/skills/bundled/review/DESIGN.md:779 (+4 locations) — [probe] '#9325 landed' asserted of an unmerged PR; 'two PRs' miscounts three
  • packages/core/src/skills/bundled/review/SKILL.md:773 (+3 locations) — [review] SKILL.md + approachRounds descriptions describe composed verdict; the gate is baseEvent
  • packages/cli/src/commands/review/compose-review.ts:2476 — [probe] zh '已增长 X 倍' states an increment; the interpolated value is a ratio
  • packages/cli/src/commands/review/lib/ledger.ts:75 — [probe] cross-account recovery seam drops the src0 baseline (foreign old-build winner)
  • packages/cli/src/commands/review/compose-review.test.ts:6440 — [probe] baseline ratchet pinned only in the shrink direction; upward rewrite ships green
  • packages/cli/src/config/settingsSchema.ts:787 — [review] setting description drops the 'source diff' growth qualifier
中文说明

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

未审查: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。

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20310, 494, 1541, 1597, 3723, 529 passed

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

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

qqqys and others added 2 commits August 18, 2026 15:13
Resolves the one conflict, in fetch-pr.test.ts: both sides added an import
to the same block — `buildDiffPlan` here, `makeDiff` from the new
lib/test-utils.js on main. Both are used, so both are kept.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@qqqys

qqqys commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /review

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

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

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20502, 494, 1578, 1597, 3747, 529 passed.

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

  • packages/cli/src/commands/review/compose-review.ts:124 — [probe] APPROACH_ROUNDS_DEFAULT docstring misdescribes the round-boundary semantic the code implements
中文说明

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

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

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20502, 494, 1578, 1597, 3747, 529 passed

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

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

@qqqys

qqqys commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

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

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

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

  • R13-1 prevLedgerFacts src0 round-gate gap — already recorded in the round-11 and round-12 deferral lists (compose-review.ts:1875, R12-9)
  • R13-2 load-bearing measurement expression hand-inlined twice — already recorded in rounds 4/7/8/9/11/12 deferral lists (R12-2)
  • R13-3 nonConverged false direction unpinned — already recorded in rounds 7-10 deferral lists (R12-4)
  • R13-4 advisory paragraph carries no trim rank — already recorded in the round-10 and round-11 deferral lists (compose-review.ts:3454)
  • R13-5 COMMENT-event render path has no fired-signal test — already recorded in rounds 2/3/7/8/9/10/12 deferral lists
  • R13-6 approachSignal block detaches the deferredCount comment — already recorded in the round-8 and round-11 deferral lists (save-artifact.ts:226)
  • R13-7 prevLedger helper hardcodes the side-file name — already recorded in the round-12 deferral list (compose-review.test.ts:10123)
  • R13-8 zh round-cap corroborating clause untested — already recorded in rounds 8-10 deferral lists (R10-7)
  • R13-10 review.approachRounds missing from the settings reference table — already reported inline in rounds 1-3 (comments 3795294579, 3798586864, 3799521882; lineage R1-10 / R10-1 / R12-5)

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.

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20991, 495, 1685, 1653, 4164, 599 passed.

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

  • packages/cli/src/commands/review/pr-context-persist.test.ts:1043 — [review] src0 survival pinned on only two of the four persistRecoveredLedger write outcomes
  • packages/cli/src/commands/review/compose-review.test.ts:10471 — [probe] APPROACH_ROUNDS_DEFAULT pinned only to (3, 5]; mutant default 4 ships green
  • packages/cli/src/commands/review/compose-review.test.ts:10526 — [probe] APPROACH_GROWTH_FACTOR pinned only from above; mutant factor 2 ships green
  • packages/cli/src/commands/review/compose-review.test.ts:10551 — [probe] LOW_SIGNAL_SRC_DIFF_LINES constrained only to [100, 300); mutant floor 299 ships green
  • packages/cli/src/commands/review/compose-review.test.ts:10571 — [probe] round-cap stop 'never a trigger on its own' contract pinned by no fixture
  • packages/cli/src/commands/review/compose-review.ts:3539 — [probe] suggestions-only COMMENT baseEvent firing arm has no test

Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)

中文说明

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

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

未审查: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。

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 20991, 495, 1685, 1653, 4164, 599 passed

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

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

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

⚠️ Downgraded from Approve to Comment: CI failing: Test (ubuntu-latest, Node 22.x). Partially reviewed — gaps disclosed.

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.

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 21036, 495, 1685, 1654, 4190, 599 passed.

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

  • packages/cli/src/commands/review/compose-review.ts:3554 — [review] load-bearing measurement expression hand-inlined twice (baseline stamp and growth numerator)
  • packages/cli/src/commands/review/compose-review.ts:4895 — [probe] COMMENT-event render path of a fired approach signal has no test
  • packages/cli/src/commands/review/fetch-pr.ts:1504 — [probe] fetch-pr's !scopedDelta supplier of fullSrcDiffLines (full-range and upToDate rounds) has no test
  • packages/cli/src/commands/review/compose-review.ts:1862 — [probe] prevLedgerFacts admits src0 without the round === 0 gate its sibling facts carry
  • packages/cli/src/commands/review/compose-review.test.ts:10411 — [probe] signal-side incremental measurement arm unpinned in both directions; two surviving mutants
  • packages/cli/src/commands/review/compose-review.test.ts:10591 — [probe] zh round-cap corroborating clause untested
  • packages/cli/src/commands/review/compose-review.ts:3571 — [probe] nonConverged false direction and time-budget-stop discrimination unpinned; two surviving mutants
  • packages/cli/src/commands/review/compose-review.test.ts:10601 — [probe] zh growth figure (4.0 倍) and number framing asserted by no test
  • packages/cli/src/commands/review/compose-review.test.ts:10160 — [probe] approach paragraph's middle sentence asserted by no test in either language
  • packages/cli/src/commands/review/pr-context-persist.test.ts:1044 — [probe] cross-account recovery seam drops the src0 baseline when the foreign winner carries none

Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)

中文说明

⚠️ 已从批准降级为评论:CI failing: Test (ubuntu-latest, Node 22.x)。 仅完成部分审查,审查缺口已披露。

未审查: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。

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 21036, 495, 1685, 1654, 4190, 599 passed

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

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

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

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

  • advisory paragraph carries no trim rank — already recorded in rounds 10/11/13/14 deferral lists (compose-review.ts)
  • cross-account recovery seam drops the src0 baseline when the foreign winner carries none — already recorded in rounds 4/12/14 deferral lists (lib/ledger.ts / pr-context.ts)
  • approachSignal block detaches the deferredCount comment — already recorded in rounds 8/11/13 deferral lists (save-artifact.ts)
  • suggestions-only COMMENT baseEvent firing arm has no test — already recorded in the round-13 deferral list (compose-review.ts:3539)
  • COMMENT-event render path of a fired signal has no test — already recorded in rounds 2/3/7/8/9/10/12/13/14 deferral lists
  • nonConverged false direction and time-budget-stop discrimination unpinned — already recorded in rounds 7-14 deferral lists (compose-review.ts)
  • threshold constants' silence side unpinned (growth-factor-2 and rounds-4 mutants ship green) — already recorded in the round-13 deferral list (compose-review.test.ts)
  • doc claims force-push disarms the signal; recovery re-arms it — already recorded in rounds 2/3 deferral lists (design doc)
  • zh round-cap corroborating clause untested — already recorded in rounds 8-10/13/14 deferral lists (R10-7 / R13-8)
  • prevLedgerFacts admits src0 without the round === 0 gate — already recorded in rounds 11-14 deferral lists (R12-9 / R13-1)
  • review.approachRounds missing from the settings reference table — already reported inline in rounds 1-3 (comments 3795294579, 3798586864, 3799521882; R1-10)
  • src0 riding the marker on a capped round unpinned — already recorded in the round-11 deferral list (compose-review.test.ts)
  • verdict-line numeric clause unpinned — already recorded in rounds 9/10/12/14 deferral lists (R12-10)
  • zh growth figure (4.0 倍) asserted by no test — already recorded in rounds 9/10/12/14 deferral lists (R12-7)

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.

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory.

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

  • packages/cli/src/commands/review/compose-review.test.ts:10212 — [probe] src0 marker carry-forward on APPROVE rounds pinned by no test
  • packages/cli/src/commands/review/compose-review.test.ts:10415 — [probe] 100-line floor's input (full-range vs incremental delta) unpinned by any test

Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)

中文说明

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

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

未审查: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。

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory

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

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory.

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

  • packages/cli/src/commands/review/compose-review.ts:1862 — [probe] src0 recovered without the round gate its sibling facts carry
  • packages/cli/src/commands/review/save-artifact.ts:287 — [review] approachSignal block detaches the deferredCount comment from its code
  • packages/cli/src/commands/review/compose-review.ts:4895 — [probe] COMMENT-event render path of a fired signal has no test
  • packages/cli/src/commands/review/compose-review.ts:3576 — [probe] nonConverged false direction unpinned by any test
  • packages/cli/src/commands/review/compose-review.test.ts:10123 — [review] prevLedger helper hardcodes the side-file name independently of prNumber
  • packages/vscode-ide-companion/schemas/settings.schema.json:263 — [review] review.approachRounds missing from the settings reference table
  • packages/cli/src/commands/review/compose-review.ts:4588 — [probe] advisory paragraph carries no trim rank
  • packages/cli/src/commands/review/compose-review.ts:3563 — [probe] foreign-winner recovery seam carries a stranger-planted src0 baseline
  • packages/cli/src/commands/review/compose-review.test.ts:10331 — [probe] threshold-fallback test's 0-half pins neither semantics

Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)

中文说明

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

未审查: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。

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory

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

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

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

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

⚠️ This change was NOT pushed — any commit referenced below was made only in the runner workspace and has been discarded. What the agent reported:
Qwen failed during address-review: status 137.

See the Qwen Autofix agent step logs for model/tool output.

中文说明

🤖 AutoFix 更新了一个过期的 base —— 修复未通过验证,但本 PR 落后于 main,因此已通过 update-branch 合入当前 main,并将在下次扫描时重试。过期的 base(main 已改动的依赖或符号)可能让构建失败而并非修复本身的错;若 base 更新后仍然失败,将移交人工处理。

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


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

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

  • R17-1 load-bearing measurement expression hand-inlined twice (baseline stamp and growth numerator) — already recorded in rounds 4/7/8/9/11/12/14/16 deferral lists (compose-review.ts)
  • R17-2 COMMENT-event render path of a fired approach signal has no test — already recorded in rounds 2/3/7/8/9/10/12/13/14/16 deferral lists (compose-review.ts:4895)
  • R17-3 zh growth figure and number framing asserted by no test — already recorded in rounds 9/10/12/14 deferral lists (compose-review.test.ts)
  • R17-4 APPROACH_ROUNDS_DEFAULT pinned only to (3, 5]; mutant default 4 ships green — already recorded in the round-13 deferral list (compose-review.test.ts:10471)
  • R17-5 advisory paragraph carries no trim rank — already recorded in rounds 10/11/13/14/16 deferral lists (compose-review.ts)
  • R17-6 approachSignal growth validator admits Infinity, persists null — already recorded in rounds 6/10/12 deferral lists (save-artifact.ts:244, R10-5)
  • R17-7 nonConverged false direction and time-budget-stop discrimination unpinned — already recorded in rounds 7-14/16 deferral lists (compose-review.ts/test.ts)
  • R17-8 prevLedger helper hardcodes the side-file name independently of prNumber — already recorded in rounds 12/16 deferral lists (compose-review.test.ts:10123)
  • R17-9 prevLedgerFacts comment claims force-push/account-switch disarms; recovery re-arms — already recorded in rounds 2/3 deferral lists (design-doc instance of the same claim)
  • R17-10 verdict-line numeric clause pinned by no test — already recorded in rounds 9/10/12/14/16 deferral lists (R12-10)
  • R17-11 APPROACH_GROWTH_FACTOR pinned only from above; mutant factor 2 ships green — already recorded in the round-13 deferral list (compose-review.test.ts:10526)
  • R17-12 prevLedgerFacts admits src0 without the round === 0 gate — already recorded in rounds 11/12/14/16 deferral lists (R12-9/R13-1, compose-review.ts:1875)
  • R17-13 !scopedDelta supplier of fullSrcDiffLines has no test — already recorded in rounds 8/10/14/16 deferral lists (fetch-pr.ts:1515, R10-4)
  • R17-14 review.approachRounds missing from the settings reference table — already reported inline in rounds 1-3 (comments 3795294579, 3798586864, 3799521882; R1-10)
  • R17-15 legacy-incremental measurement arm unpinned with a baseline — already recorded in rounds 8/9 deferral lists (compose-review.test.ts:8508/8711)

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

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 21226, 496, 1685, 1654, 4206, 626 passed.

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

  • packages/cli/src/commands/review/compose-review.test.ts:10157 — [probe] COMMENT-branch standalone-paragraph placement unpinned (new facet of the COMMENT render path family)

Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)

中文说明

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

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

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

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

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 21226, 496, 1685, 1654, 4206, 626 passed

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

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round summary — no code change

This round's feedback contained no new findings: all six automated review bodies are COMMENTED, and every finding they list is explicitly confirmed as already reported on this PR and not repeated (prior-round deferral lists and inline comments). The convergence-posture deferral lists are explicitly "recorded, not requested in this round", so they were not worked. There are no inline comments and no issue-level comments to address. The only actionable item was the failed check web-shell E2E Smoke (ubuntu-latest, Node 22.x). No code change was made, for the reasons below.

Diagnosis of the failed web-shell E2E Smoke check

1. This PR has no code path into the failing suite. The PR's footprint is docs/design/, packages/cli/src/commands/review/**, packages/cli/src/config/settingsSchema.ts, two markdown files under packages/core/src/skills/bundled/review/, and the generated VS Code schema. The web-shell smoke suite drives a Vite dev server whose module graph is packages/web-shell/client/** + ../webui/src + ../sdk-typescript/src (vite.config.ts aliases). Verified:

  • packages/webui/src and packages/sdk-typescript/src contain no imports of the cli review code this PR changes.
  • The one cross-package edge, sdk-typescript@qwen-code/qwen-code-core/transcriptRecords, resolves to packages/core/dist/src/utils/transcript-records.js, built from a source file this PR does not touch. The core skill markdown this PR edits is copied into core's dist as assets but is not part of web-shell's module graph.

2. The failing run tested a merge that had just pulled in a web-shell feature from main. The failing job (CI run 32685594239) tested merge commit c98e3ad771, created 2026-08-24 03:11 UTC by merging main at 747dbf00c2 — which is feat(web-shell): compact agent activity summaries (#9657), landed on main at 02:46 UTC, 25 minutes earlier. #9657 changed exactly the rendering components the smoke specs assert against (MessageList.tsx, ToolGroup.tsx, transcriptToMessages.ts, MessageItem.tsx), while adding/updating no @smoke spec. If the failure is a deterministic assertion break rather than an environment flake, #9657 (main-side code) is the plausible cause — outside this PR's footprint.

3. Local evidence on the merged HEAD.

  • The dev server command used by the Playwright config (npm run dev -- --host 127.0.0.1 --port <port>) boots in ~2 s and serves.
  • A recursive crawl of the served module graph transformed 894 modules with zero transform/resolve errors at HEAD. (An initial resolution failure for three @qwen-code/acp-bridge/* subpaths was traced to this runner's stale local checkout missing packages/acp-bridge/dist; building acp-bridge locally resolved it. CI does a clean npm ci, whose prepare script builds all packages, so CI is not affected.)
  • Browser-level reproduction is not possible on this runner: Playwright's chromium fails to launch with error while loading shared libraries: libglib-2.0.so.0: cannot open shared object file, and the runner has no sudo/root/apt to install system libraries. Per the workflow rules, an exact CI check unavailable on the current runner is not a failed runnable check.
  • The Test (ubuntu-latest, Node 22.x) check that caused the round-14 downgrade is now SUCCESS in the same run.

4. No CI logs are available in this environment (no GitHub credentials), so the exact failing test cannot be named; the conclusions above rest on dependency-graph analysis and local probes rather than the CI log.

Disposition

  • No code change within this PR's footprint can influence the web-shell smoke suite, so there is no in-scope fix to attempt. A web-shell code fix (if one is needed) belongs to web-shell's own footprint, i.e. a separate change — and nothing is verified-real yet, so no deferred-finding record is created.
  • The repository's own mechanisms cover this failure: the dev-bot previously verified this exact check as passing on current main before its base update (comment 2026-08-23), the CI flaky patrol (qwen-ci-flaky-rerun.yml, every 10 min) classifies and re-runs stale PR check failures, and the next automated base update will re-trigger CI on a fresher main (origin/main has already advanced past the merged revision).
  • Review-side findings from earlier rounds remain recorded in the PR thread; none were re-litigated this round per the reviewer's explicit "recorded, not requested" deferral.

Verification

No code was changed this round (git status clean at HEAD c98e3ad771 before and after), so the gate commands (npm run build / typecheck / lint / focused Vitest) have nothing new to validate; the same HEAD's Test (ubuntu-latest, Node 22.x) CI job passed. Diagnostic commands actually run and their results:

  • git diff origin/main...HEAD --stat, git log --first-parent — mapped the PR footprint and the dev-bot merge history; clean working tree confirmed before and after.
  • Dependency-edge inspection (package.json exports of core/acp-bridge, vite.config.ts aliases, grep over webui/sdk-typescript sources) — no edge from the PR's changed files into web-shell's runtime graph.
  • npm run build --workspace=packages/acp-bridge — passed (repairs the runner-local missing dist only; CI builds it via npm ci).
  • npm run dev --workspace=packages/web-shell (the exact Playwright webServer command) — ready in ~2 s, served HTTP 200.
  • Recursive module-graph crawl against the dev server — 894 modules transformed, 0 errors.
  • npx playwright install chromium + headless launch probe — install succeeded, launch failed on missing system libraries (libglib-2.0.so.0); browser reproduction unavailable on this runner (no sudo).
中文说明

Autofix 轮次总结 —— 无代码变更

本轮反馈不包含新发现:六个自动审查主体均为 COMMENTED,其中列出的每一条发现都明确标注为已在本 PR 上报告、不再重复(见此前各轮次的延后清单与行内评论)。收敛姿态下的延后清单明确标注"已记录,本轮不要求修改",因此本轮未处理这些条目。没有需要处理的行内评论,也没有议题级评论。唯一的可操作项是失败检查 web-shell E2E Smoke (ubuntu-latest, Node 22.x)。基于下述原因,本轮未做任何代码变更。

失败的 web-shell E2E Smoke 检查的诊断

1. 本 PR 与该失败套件之间不存在任何代码路径。 本 PR 的改动范围为 docs/design/packages/cli/src/commands/review/**packages/cli/src/config/settingsSchema.tspackages/core/src/skills/bundled/review/ 下的两个 markdown 文件,以及生成的 VS Code schema。web-shell 冒烟套件驱动的是一个 Vite 开发服务器,其模块图为 packages/web-shell/client/** + ../webui/src + ../sdk-typescript/src(见 vite.config.ts 的别名配置)。已核实:

  • packages/webui/srcpackages/sdk-typescript/src 中不存在对本 PR 所改动的 cli review 代码的任何导入。
  • 唯一的跨包边 sdk-typescript@qwen-code/qwen-code-core/transcriptRecords 解析到 packages/core/dist/src/utils/transcript-records.js,其源文件本 PR 未做改动。本 PR 编辑的 core 技能 markdown 会作为资产拷贝进 core 的 dist,但不属于 web-shell 的模块图。

2. 失败的 CI 运行所测试的合并提交刚刚从 main 拉入了一个 web-shell 功能。 失败作业(CI 运行 32685594239)测试的是合并提交 c98e3ad771(2026-08-24 03:11 UTC 创建),该提交将位于 747dbf00c2main 合并进分支 —— 即 feat(web-shell): compact agent activity summaries (#9657),该功能于 02:46 UTC 落地 main,距合并仅 25 分钟。#9657 改动的恰好是冒烟用例断言所依赖的渲染组件(MessageList.tsxToolGroup.tsxtranscriptToMessages.tsMessageItem.tsx),且未新增/更新任何 @smoke 用例。如果该失败是确定性的断言失败而非环境抖动,那么 #9657(main 侧代码)是合理的原因 —— 它在本 PR 的改动范围之外。

3. 在合并后的 HEAD 上取得的本地证据。

  • Playwright 配置所用的开发服务器命令(npm run dev -- --host 127.0.0.1 --port <port>)约 2 秒内启动并正常提供服务。
  • 对已服务模块图的递归抓取在 HEAD 上共转换了 894 个模块,零转换/解析错误。(最初有三个 @qwen-code/acp-bridge/* 子路径解析失败,排查发现是本 runner 的本地检出过旧、缺少 packages/acp-bridge/dist;本地构建 acp-bridge 后即解决。CI 执行的是干净的 npm ci,其 prepare 脚本会构建所有包,因此 CI 不受此问题影响。)
  • 本 runner 上无法进行浏览器级复现:Playwright 的 chromium 启动失败,报错 error while loading shared libraries: libglib-2.0.so.0: cannot open shared object file,且该 runner 无 sudo/root/apt,无法安装系统库。按工作流规则,当前 runner 上不可用的精确 CI 检查不视为失败的可运行检查。
  • 导致第 14 轮降级的 Test (ubuntu-latest, Node 22.x) 检查在同一次运行中已为 SUCCESS

4. 本环境中没有可用的 CI 日志(无 GitHub 凭据),因此无法点名具体失败的测试;以上结论基于依赖图分析与本地探测,而非 CI 日志。

处置

  • 本 PR 改动范围内的任何代码变更都无法影响 web-shell 冒烟套件,因此不存在可尝试的范围内修复。web-shell 的代码修复(如果确实需要)属于 web-shell 自己的改动范围,应另行处理 —— 且目前尚无任何问题被证实存在,因此不创建延后发现记录。
  • 仓库自身的机制已覆盖此类失败:dev-bot 在此前的 base 更新前已验证该检查在当前 main 上通过(2026-08-23 的评论);CI 抖动巡检(qwen-ci-flaky-rerun.yml,每 10 分钟一次)会对陈旧的 PR 检查失败进行分类并重跑;下一次自动 base 更新会在更新的 main 上重新触发 CI(origin/main 已推进到被合并版本之后)。
  • 此前各轮次的审查侧发现仍保留在 PR 线程的记录中;按照审查方明确的"已记录、不要求本轮处理"的延后声明,本轮未重新审议任何一条。

验证

本轮未改动任何代码(HEAD c98e3ad771 前后 git status 均为干净),因此门禁命令(npm run build / typecheck / lint / 聚焦 Vitest)没有新内容可验证;同一 HEAD 的 Test (ubuntu-latest, Node 22.x) CI 作业已通过。实际执行的诊断命令及结果:

  • git diff origin/main...HEAD --statgit log --first-parent —— 梳理了 PR 改动范围与 dev-bot 合并历史;前后均确认工作区干净。
  • 依赖边检查(core/acp-bridgepackage.json exports、vite.config.ts 别名、对 webui/sdk-typescript 源码的 grep)—— PR 所改文件与 web-shell 运行时模块图之间不存在任何边。
  • npm run build --workspace=packages/acp-bridge —— 通过(仅修复本 runner 缺失的 dist;CI 通过 npm ci 自行构建)。
  • npm run dev --workspace=packages/web-shell(与 Playwright webServer 完全一致的命令)—— 约 2 秒就绪,HTTP 200 正常。
  • 对开发服务器的递归模块图抓取 —— 转换 894 个模块,0 错误。
  • npx playwright install chromium + 无头启动探测 —— 安装成功,启动因缺少系统库(libglib-2.0.so.0)失败;本 runner 上无法进行浏览器复现(无 sudo)。

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


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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [web-shell E2E Smoke (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [web-shell E2E Smoke (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

  • D18-1 load-bearing measurement expression hand-inlined twice (baseline stamp and growth numerator) — already recorded in rounds 4/7/8/9/11/12/14/16/17 deferral lists (compose-review.ts; R17-1)
  • D18-2 COMMENT-event render path of a fired approach signal has no test — already recorded in rounds 2/3/7/8/9/10/12/13/14/16/17 deferral lists (compose-review.ts:4895; R17-2)
  • D18-3 neutrality invariant cannot catch a promoting verdict-wiring mutant — already recorded in the round-12 deferral list (compose-review.test.ts:10194, 'neutrality invariant … verdict-wiring mutants ship green')
  • D18-4 !scopedDelta supplier of fullSrcDiffLines has no test — already recorded in rounds 8/10/14/16/17 deferral lists (fetch-pr.ts:1515; R10-4/R17-13)
  • D18-5 nonConverged false direction and corroborating-clause absence unpinned — already recorded in rounds 7-14/16/17 deferral lists (compose-review.test.ts; R12-4/R17-7)
  • D18-6 advisory paragraph carries no trim rank — already recorded in rounds 10/11/13/14/16/17 deferral lists (compose-review.ts; R17-5)
  • D18-7 zh round-cap corroborating clause untested — already recorded in rounds 8-10/13/14/17 deferral lists (compose-review.test.ts; R10-7/R13-8)
  • D18-8 src0 recovered without the round gate its sibling facts carry — already recorded in rounds 11/12/14/16/17 deferral lists (compose-review.ts:1875; R12-9/R13-1/R17-12)
  • D18-9 cross-account recovery seam drops the src0 baseline when the foreign winner carries none — already recorded in rounds 4/12/14/16 deferral lists (lib/ledger.ts / pr-context.ts; R12-11)
  • D18-10 approachSignal block detaches the deferredCount comment from its code — already recorded in rounds 8/11/13/16/17 deferral lists (save-artifact.ts)

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

Test Plan (not a blocker): lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 23668, 21296, 496, 1688, 1654, 4226, 626 passed.

中文说明

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

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

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

Test Plan(非阻断):lib/run-ledger.tsno such file or directory; lib/ledger.test.tsno such file or directory; lib/review-settings.test.tsno such file or directory; 3266 passed — this review observed 23668, 21296, 496, 1688, 1654, 4226, 626 passed

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix review round — no action taken

Round outcome: No changes. This round's feedback contains no new actionable findings, no failed checks, no unresolved inline threads, and no base conflict to resolve.

Feedback triage

  • Review [rv:5006147075] (COMMENTED — partial review with disclosed gaps) posted 0 new findings (its ledger footer confirms posted: 0). It is informational, not CHANGES_REQUESTED.
  • The 10 Suggestion-level items enumerated in that review (D18-1 … D18-10) are explicitly confirmed as already reported on this PR and not repeated. They match findings already deferred with recorded reasons in earlier rounds (the rounds 4–17 deferral lists cited alongside each item). This PR is at review round 18 — well past the ~5-round point where AGENTS.md directs that only Critical fixes (correctness, security, data loss, regressions) should land, with remaining Suggestions deferred to a follow-up issue or PR and each deferral recorded in the PR thread. None of the 10 items is Critical, none carries a reproduced blocking defect, and none was newly requested this round, so they remain deferred to the follow-up queue; nothing is silently dropped.
  • No inline comments, no issue-level comments, no failed checks, and no still-red checks were present. --conflict false, so no base merge was required.

Informational notes (no code change implied)

  • The review disclosed one unreviewed area: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and the suite was not run locally by the reviewer. No check is red, so there is nothing to repair; the workflow's independent CI remains the final verification gate.
  • The review's non-blocking Test Plan note (lib/run-ledger.ts, lib/ledger.test.ts, lib/review-settings.test.ts reported as no such file or directory, plus a pass-count mismatch) is a path-resolution and description discrepancy, not a code defect: those files exist under packages/cli/src/commands/review/lib/, and the PR body's test-plan numbers predate several later review rounds and main merges. Updating the PR description is a GitHub write this workflow does not perform from inside the checkout.
中文说明

Autofix 审查轮次 — 未采取任何行动

**本轮结果:**无代码变更。本轮反馈中不包含新的可处理发现、没有失败的检查、没有未解决的行内讨论串,也没有需要解决的与 base 分支的冲突。

反馈分类

  • 审查 [rv:5006147075]COMMENTED —— 部分审查,已披露审查缺口)未发布任何新发现(其 ledger 页脚确认 posted: 0)。该审查为信息性审查,并非 CHANGES_REQUESTED
  • 该审查中列举的 10 条 Suggestion 级条目(D18-1 … D18-10)被明确确认为已在本 PR 上报告过且不再重复发布。它们与此前各轮(每条条目旁引用的第 4–17 轮延迟清单)中已记录理由并延迟处理的发现一致。本 PR 目前处于第 18 轮审查 —— 早已超过 AGENTS.md 规定的大约 5 轮节点;按照该规定,此后只应落地 Critical 级修复(正确性、安全性、数据丢失、回归),其余 Suggestion 应延迟到后续 issue 或 PR 中,并且每次延迟都需在 PR 讨论串中记录。这 10 条条目中没有一条是 Critical 级,没有任何一条附带已复现的阻断性缺陷,也没有任何一条是本轮新提出的请求,因此它们继续保留在延迟处理队列中;没有任何条目被静默丢弃。
  • 本轮不存在行内评论、问题级评论、失败的检查,也没有持续失败的检查。--conflict false,因此无需合并 base 分支。

信息性说明(不涉及代码变更)

  • 该审查披露了一个未审查区域:build-and-test — Integration Tests (CLI, No Sandbox) 在 CI 中被跳过,且审查者未在本地运行该测试套件。目前没有任何检查处于失败状态,因此没有需要修复的内容;工作流的独立 CI 仍然是最终验证关口。
  • 该审查的非阻断性 Test Plan 说明(lib/run-ledger.tslib/ledger.test.tslib/review-settings.test.ts 被报告为 no such file or directory,以及测试通过数量不一致)属于路径解析与描述层面的差异,并非代码缺陷:这些文件实际存在于 packages/cli/src/commands/review/lib/ 目录下,而 PR 正文中的测试计划数字早于此后的多轮审查与多次 main 分支合并。更新 PR 描述属于 GitHub 写操作,本工作流不会在检出目录内执行此类操作。

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


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

wenshao added a commit that referenced this pull request Aug 24, 2026
@wenshao

wenshao commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Local deep-verification verdict: merge-ready — 17/17 scripted assertions passed for PR head fd10856d7a5b554a5468e6ced3c488a346096f9f, exercised as merge ref 6a6e2fa2def3c9399809a85c4f6c2abb4c3d1fb3. The central advisory-only behavior is load-bearing against base 717ad101e262aa8b0ebbb0e1e18d1d598fb9e663; no blocking regression was found in the exercised compose/fetch/ledger/settings/artifact surface, and the affected core workspace built on both arms.

中文摘要

结论:可合并。中心 A/B 已证明:base 不产生 approach signal;PR merge ref 在轮次、源码规模和增长倍数同时达标时产生 signal,并且不改变 eventbaseEventcappedBy。边界矩阵、compose 对完整 diff 字段的消费、858 个相关测试以及禁用 signal 的 mutation 均符合预期。base 与 merge ref 的 core workspace 均构建成功;CLI 两臂的 TypeScript 编译也都通过,随后在当前 Docker Desktop bind-mount 环境中于同一 asset-copy 路径复现相同 EACCES,因此该剩余失败被 A/A 归为本地环境限制,而非 PR 回归。未覆盖真实 GitHub fetch-pr --since 网络触发和 GitHub-side rendering。

Central claim and A/B result

The same production composeReview entry was driven in separate base and merge worktrees with identical plan, ledger, comments count, Node 22 container, and lockfile. The archived arm binding recorded the exact commits, distinct compose-review.ts object hashes, and each arm's local core workspace link.

Scenario Base 717ad101 Merge 6a6e2fa2 (PR head fd10856d) Oracle
Round 6, baseline 228, full source diff 920 no signal signal {round: 6, src0: 228, srcDiffLines: 920, growth: 4.035…} body and verdict clause present only at head
Same review, no src0 baseline no signal no signal unknown growth fails open to silence
Original baseEvent=APPROVE, later capped to Comment no signal no signal convergence is never contradicted
Before round threshold no signal no signal threshold gate holds
Exactly 3x growth no signal signal inclusive growth boundary holds
Exactly 100 source lines no signal no signal strict non-trivial floor holds
Incremental diff 40, full diff 920 no signal signal reports 920 compose consumes the full-range field, not the incremental delta
Signal vs no-baseline review same event/baseEvent/caps same event/baseEvent/caps advisory-only invariant holds

PR 9340 verification summary

PR 9340 head boundary matrix

Tests and mutation proof

  • 6 targeted CLI test files passed: 858 passed, 1 skipped. Coverage included compose, fetch, ledger, operator settings, PR-context persistence, and artifact persistence.
  • Disabling the load-bearing signal assignment preserved the module interface and ran all 518 compose tests; 8 approach-signal tests failed on their intended field/body/threshold assertions. This pins the main disclosure, operator threshold, full-range incremental semantics, exact round and 3x boundaries, ledger-cap consistency, non-convergence wording, and Chinese rendering.
  • Base and merge-ref core builds both passed in stable non-concurrent containers.
  • After identical CLI prerequisite builds, base and merge-ref CLI TypeScript compilation both passed and both then failed in copy_files.js with the same bind-mount EACCES path, including as root. This A/A identifies the remaining asset-copy failure as local packaging-environment behavior rather than a PR regression.

Disabled-signal mutation killed by 8 targeted tests

Findings

No blocking or non-blocking product finding was reproduced in the exercised surface.

Not covered

  • The live GitHub fetch-pr --since network path was not executed inside the credential-free container. Compose consumption of fullSrcDiffLines was proven directly, and field generation was covered by the affected fetch tests, not by a live forge request.
  • The CLI asset-copy step did not pass on this Docker Desktop bind mount; the same post-TypeScript EACCES reproduced on base and merge-ref arms.
  • GitHub review submission and GitHub-side Markdown rendering were not exercised during verification.
  • No separate end-to-end settings-file hierarchy harness was added for workspace-scope rejection of review.approachRounds.

Methodology

Metadata was resolved on the host with gh pr view 9340 --repo QwenLM/qwen-code; untrusted PR code ran only in node:22-bookworm containers with no host home, SSH keys, GitHub token, or Docker socket mounted. Raw logs, harnesses, assertions, and the full report are retained locally under tmp/pr9340-verify-20260824-171533/.

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

No action taken on PR #9340 this round

This round's feedback contains no actionable items:

  • No reviews, no inline comments, and no failed or still-red CI checks — all 39 check runs on the head are green or legitimately skipped.
  • The only feedback is an issue-level comment (ic:5394295584) from @wenshao: a local deep-verification verdict that is explicitly merge-ready for PR head fd10856d7a (17/17 scripted assertions passed, 858 targeted tests passed, load-bearing signal behavior A/B-proven against base, mutation probe killed by 8 tests). Its Findings section states: "No blocking or non-blocking product finding was reproduced in the exercised surface."

The comment's "Not covered" notes were reviewed and none is a defect claim against this PR:

  1. Live GitHub fetch-pr --since network path — not executable in a credential-free container by design; compose consumption of fullSrcDiffLines and field generation are already covered by the PR's fetch/compose tests.
  2. CLI asset-copy EACCES on the verifier's Docker Desktop bind mount — the verifier A/A-tested base and merge-ref arms and reproduced the identical failure on both, attributing it to the local packaging environment rather than this PR.
  3. GitHub review submission and GitHub-side Markdown rendering — external to the repository; nothing in-repo to change.
  4. No separate end-to-end settings-hierarchy harness for workspace-scope rejection of review.approachRounds — a note about the verifier's own e2e scope, not a requested change; the existing unit test already pins the rejection path by asserting loadSettings is called with skipWorkspaceSettings: true (packages/cli/src/commands/review/lib/review-settings.test.ts). Adding a new e2e harness for an unrequested note would grow the diff without any finding behind it.

No claim that current behavior is wrong was made, so there was nothing to reproduce, fix, decline, defer, or escalate. No code changes, no commits.

中文说明

本轮对 PR #9340 不采取任何行动

本轮反馈中没有任何可执行的事项:

  • 没有 review、没有行内评论,也没有失败或持续失败的 CI 检查 —— 该 head 上全部 39 个 check run 均为通过或合理跳过。
  • 唯一的反馈是一条来自 @wenshao 的 issue 级评论(ic:5394295584:一次本地深度验证结论,明确表示 PR head fd10856d7a 可合并(17/17 脚本化断言通过、858 个目标测试通过、核心 signal 行为已对照 base 完成 A/B 证明、mutation 探针被 8 个测试杀死)。其 Findings 部分写道:“在所验证的范围内,没有复现任何阻塞性或非阻塞性的产品问题。”

该评论的 “Not covered”(未覆盖)部分已逐条审阅,没有一条是对本 PR 的缺陷指控:

  1. 真实 GitHub fetch-pr --since 网络路径 —— 在无凭据容器中按设计就无法执行;compose 对 fullSrcDiffLines 的消费以及字段生成已由本 PR 的 fetch/compose 测试覆盖。
  2. 验证者在 Docker Desktop bind mount 上遇到的 CLI asset-copy EACCES —— 验证者对 base 与 merge ref 两臂做了 A/A 测试,两边复现了完全相同的失败,从而将其归因于本地打包环境,而非本 PR 的回归。
  3. GitHub review 提交与 GitHub 侧的 Markdown 渲染 —— 属于仓库之外的环节,仓库内没有可改动之处。
  4. 没有为 review.approachRounds 的 workspace 作用域拒绝单独添加端到端 settings 层级测试 —— 这只是验证者对自身 e2e 覆盖范围的说明,并非改动要求;现有单元测试已通过断言 loadSettingsskipWorkspaceSettings: true 被调用,固定了该拒绝路径(packages/cli/src/commands/review/lib/review-settings.test.ts)。为一条未被提出的建议新增 e2e 测试,只会让 diff 增长而背后没有任何 finding 支撑。

没有任何“当前行为有错”的指控,因此没有需要复现、修复、拒绝、延期或升级的事项。未改动任何代码,未产生任何提交。

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


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

@wenshao
wenshao enabled auto-merge August 24, 2026 11:52
@wenshao

wenshao commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

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

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

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

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

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

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

Verification report

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

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

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

Scripted assertions: 148 passed · 0 failed · 148 total

中文 — 判定:✅ 通过 · 可合入(agent 判定,follow-up 轮)

本轮为 follow-up:上一轮在 head 3c51d0ffmerge-ready(144/144);此后 PR 仅新增 8 个 main 合并提交(无新功能提交),base 推进到 717ad101。按规则全部测量在新 head 重跑(不沿用旧数字):

  • 结论merge-ready,148 条脚本化断言全过(H1 点火矩阵 + 披露-only 契约 head 50 / base 25;H2 ledger src0 生命周期 head 26 / base 11;H3 设置与产物校验器 head 20 / base 3;变异矩阵 7/7 杀死 + 未变异对照绿 + 门禁 5 项)。
  • A/B 结论:与上一轮一致——第 6 轮、src0: 228、920 行的事件夹具在 head 同时产出正文段落与判定行子句,base 完全沉默;event/baseEvent/cappedBy 逐字节一致,正文除「段落 + marker 的 src0 键 + 空-body 边缘情形的署名页脚」外逐字节一致。三个阈值边界、cap 钳制(10000,无 10001)、增量轮全程量比较、APPROVE/降级 APPROVE 永不触发,均按设计钉死。
  • 旧 findings 状态:两条信息性观察均 stands(在新 head 复测):① 点火孪生在空-body 边缘情形仍多带署名页脚(footerInFiring=true, footerInTwin=false,剥离三个已知差量后逐字节一致);② plan 数值字段仍经 Number() 宽松转型(字符串 "920" 仍点火)。均不影响判定。
  • 未覆盖:逐 commit 归因(depth-2 仅 3 个 commit 可达,元数据 20 个);fetch-pr 联网路径(容器无 token,其单元在套件内覆盖)。
Verification report

PR 9340 deep verification — follow-up round at head fd10856d

Verdict: merge-ready — 148/148 scripted assertions passed (0 failed). Verified head: fd10856d7a5b554a5468e6ced3c488a346096f9f (merge ref 6a6e2fa2, base tip 717ad101). Follow-up round: a previous-report.md from the first round (verdict merge-ready, 144/144, head 3c51d0ff) was present; all measurements below were re-run at the new head, none carried over by diff.

Previous-finding status (follow-up)

# finding (first round) severity status at fd10856d
1 Firing body's deltas beyond the paragraph: the marker's src0 key and, on an otherwise-empty body, the attribution footer informational stands — re-measured: after stripping paragraph + src0 key (any value) + footer, the firing/twin bodies are byte-identical (H1 F1 twin bodies identical mod paragraph+src0+footer PASS); the footer nuance reproduces (footerInFiring: true, footerInTwin: false). Same interaction every existing disclosure clause has; cannot move a verdict. Agree with the first round's assessment: no code change asked.
2 Plan numeric fields pass through Number() leniently (a string "920" coerces and fires) nit stands — re-measured: H1 F2 probe fires on "srcDiffLines": "920". The untrusted side (src0 from posted markers) remains strictly positive-integer-validated at every read (H2 T4: 8 hostile shapes → silence).

No corrections needed: nothing in the material I can see described the code inaccurately.

Delta since the previous round

The 8 commits added after the previously verified head 3c51d0ff are all Merge branch 'main' into feat/review-approach-signal — no feature commits. The base advanced 3b3818db → 717ad101 (main, incl. #9802 web-shell async submit). The effective HEAD^1..HEAD diff is the same 16-file feature (1221 insertions); every code fact the previous round asserted (R9-1 clamp, all guards, validator) was re-measured present at the new head. The review suite grew 3858 → 4953 tests with main's advance — numbers below are measured at the new head.

Central claim and A/B

Central claim (unchanged from round 1): when a non-Approve round reaches the round threshold (default 5, review.approachRounds) AND a recorded baseline exists AND the source diff is past the 100-line floor AND has grown ≥ 3x since the baseline, the composed review carries one advisory paragraph (EN, plus ZH for Han descriptions) and one verdict-line clause — disclosure-only: it never moves event, baseEvent, or caps, and never fires on an Approve.

Both arms drive the real composeReview (no module mocks) through its file seams, via harnesses parameterized by QWEN_TREE: plan JSON, the qwen-review-pr-8255-prev-ledger.json side file, the Step-4/5 + chunk + matrix transcripts, the production writeRoundCapStop writer, and QWEN_HOME-pointed settings. Witnesses: 01-ab-firing-head-arm.png, 02-ab-control-base-arm.png.

cell (head arm unless noted) oracle head base
incident fixture: round 6, src0 228 → 920, REQUEST_CHANGES paragraph + verdict clause + marker fires; exact text ⚠️ Round 6 … 4.0x … 228 → 920 source diff lines … Advisory only; clause reconsider the approach, not only the findings; marker "src0":228 silent; no src0 anywhere; same event
twin without baseline (same run) event/baseEvent/cappedBy + body delta signal null; event/baseEvent/cappedBy identical to firing cell; body identical mod paragraph+src0+footer silent
APPROVE (same state) paragraph absent silent silent
APPROVE downgraded to COMMENT gate reads baseEvent silent silent
silence matrix: early round (3, 20x), late no-growth (10, 1.1x), below floor (60, 12x), exactly at floor (100, 20x), 2.99x growth, round 4 signal null 6/6 silent 6/6 silent
boundaries fire: 101 lines; growth exactly 3.0x; round exactly 5 signal non-null 3/3 fire n/a
prevRound at the ledger cap (10 000) round named consistently signal/paragraph/marker all name 10000; 10001 appears nowhere silent
round-cap stop this round corroborating clause nonConverged: true; EN clause renders n/a
Han-character description zh half ⚠️ 第 6 轮 + zh corroborating clause render n/a
incremental: delta 138/full 920; legacy w/o full size; delta 350/full 120; baseline from full 950 full-range comparison 4/4 as designed n/a
baseline lifecycle: measure when absent (340); carry unchanged on shrink (228) marker "src0" 2/2 marker never carries src0
side-file type edges: src0 = −5, 2.5, "228", true dropped → silence + re-measure 4/4 silent, re-measured 920 n/a
operator approachRounds via real loadSettings: 8→suppress, 4→fires at round 5, 0→built-in, workspace 999→ignored threshold honoured 4/4 4/4 silent

Totals: H1 head 50/50, H1 base 25/25. Control purity asserted before trusting the base arm: node_modules/@qwen-code/qwen-code-core realpath resolves into the head tree, whose base→head diff is docs-only (SKILL.md, DESIGN.md); package.json/package-lock.json untouched; the base worktree has no node_modules of its own and resolves to the same root as head.

Secondary claims

S1 — src0 baseline lifecycle (H2, head 26/26, base 11/11). Round-trip keeps src0; a 400-finding ledger truncates under the 8192-byte cap, drops sha and keeps src0 — the truncation ruling; the -- comment escape coexists with src0 (no literal -- inside the comment, title round-trips verbatim); parse drops every non-positive-integer src0 (0, −3, 2.5, "228", true, null, [228], {n:228}) to silence, keeps a large positive integer, and a garbled marker parses null without throwing; serializer never writes a non-positive-integer baseline; cross-version cells: base ignores a head-shaped marker, head reads base-shaped markers as absence.

S2 — review.approachRounds + artifact validator (H3, head 20/20, base 3/3). Through the real loadSettings(): 8→8, 1→1, 0/−2/2.5/"6"/true/null→absent. saveReviewArtifact over real files: a valid signal round-trips exactly; absent/null load as null; nine wrong shapes each refused with a message naming the offending field. Base arm: a composed file carrying approachSignal still saves, minus the unknown field (forward-compatible). Consumer census: approachSignal is read only by compose-review.ts (producer) and save-artifact.ts (validator) — no web-shell/acp-bridge/core consumer exists to trip on the field.

Mutation matrix — 7/7 killed, zero survivors (witness: 03-mutation-matrix-all-killed.png) — re-run at the new head in a scratch worktree at HEAD:

mutant red tests killed at intended assertion
M0 control: default 5→6 1 fires at exactly the round threshold (positive control)
M1: revert R9-1 clamp 1 names the round AT the ledger capexpected { round: 10001, … } to match { round: 10000, … }
M2: drop prevSrc0 > 0 4 stays silent when the previous round recorded no baseline + the disclosure twin + two pre-existing body-trim tests
M3: drop baseEvent !== 'APPROVE' 2 both APPROVE cells
M4: >= 3x> 3x 1 fires at exactly the growth factor
M5: serializer stops writing src0 8 all baseline tests incl. survives truncation where the anchor does not
M6: floor >>= 1 stays silent at exactly the source-diff floor
unmutated control 0 green: 858 passed, 1 skipped (6 changed test files)

Every revert failed the intended behavioral assertion with expected-vs-actual values; the scratch tree ended git status CLEAN.

Targeted gates (witness: 04-targeted-gates.png)

  • Head, full review suite (packages/cli, vitest run src/commands/review/): 101 files, 4953 passed, 4 skipped, 0 failed.
  • run-ledger A/A: lib/run-ledger.test.ts 72/72 on head and 72/72 on base at uid 1000; the PR touches neither run-ledger.ts nor its test (0 diff lines). The failure the PR description names is the root-environment case (chmod does not bind as root); this container runs as uid 1000, so attribution stays clean and environmental.
  • Generated artifact: re-running npm run generate:settings-schema reproduces the committed settings.schema.json byte-identical (sha256 0d8cc6cf… on both sides) — the approachRounds entry was generated, not hand-edited.

Findings

Both carried findings stand as informational (status table above); neither blocks and neither asks for a code change. No new findings this round.

Not covered

  • Per-commit attribution. The depth-2 merge-ref checkout reaches only 3 of the 20 commits named in the metadata (git rev-list --count HEAD^1..HEAD^2 = 1 at the shallow boundary). The aggregate HEAD^1..HEAD diff is what was verified; the delta since the previous round is merge-only (8 Merge branch 'main' commits, no feature commits after 3c51d0ff), and every commit's behavioral claim maps to a re-run matrix cell.
  • fetch-pr's live network path (gh pr view, real diff fetch): no token in this container. The fullSrcDiffLines computation is covered by the PR's own fetch-pr.test.ts additions (passing within the 4953-test suite) and consumed end-to-end by the H1 incremental cells; the wire itself was not driven.
  • Full base-arm suite. The base side is the control and its assertions ran (H1/H2/H3 base cells + run-ledger A/A); a full base suite was not re-run, matching the first round's scoping.
  • The flakiness gate (5 identical rounds over changed test files) is run by the workflow, not by this round.

Methodology

CI verify container (node:22-bookworm), Node v22.23.2, uid 1000, no GH_TOKEN; working tree = refs/pull/9340/merge at depth 2, npm ci + npm run build pre-run. Harnesses (harness/h1-firing-matrix.ts, h2-ledger.ts, h3-settings-artifact.ts, run via tsx) import the real production modules from the tree named by QWEN_TREE (head tree vs tmp/base-tree worktree at HEAD^1) with no module interception; they drive the code through file/config seams: plan JSON, prev-ledger side files, agent transcripts, stop files written by the production writer, QWEN_HOME-pointed settings, and real artifact files for saveReviewArtifact. Mutation matrix (harness/mutate.py) applied one single-point mutant at a time in a scratch worktree at HEAD (tmp/mut-tree, wired with symlinked dist/ and nested node_modules), ran the six changed test files, recorded red tests verbatim, and restored (final status CLEAN). Raw logs in logs/, evidence images in evidence/.

Evidence images

01-ab-firing-head-arm

02-ab-control-base-arm

03-mutation-matrix-all-killed

04-targeted-gates

Qwen Code · sandboxed verification

Flakiness gate log

rounds=5 files=6 skipped=0
file packages/cli/src/commands/review/compose-review.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/compose-review.test.ts
file packages/cli/src/commands/review/fetch-pr.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/fetch-pr.test.ts
file packages/cli/src/commands/review/lib/ledger.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/lib/ledger.test.ts
file packages/cli/src/commands/review/lib/review-settings.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/lib/review-settings.test.ts
file packages/cli/src/commands/review/pr-context-persist.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/pr-context-persist.test.ts
file packages/cli/src/commands/review/save-artifact.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/save-artifact.test.ts


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  packages/cli/src/commands/review/compose-review.test.ts: PPPPP
  packages/cli/src/commands/review/fetch-pr.test.ts: PPPPP
  packages/cli/src/commands/review/lib/ledger.test.ts: PPPPP
  packages/cli/src/commands/review/lib/review-settings.test.ts: PPPPP
  packages/cli/src/commands/review/pr-context-persist.test.ts: PPPPP
  packages/cli/src/commands/review/save-artifact.test.ts: PPPPP

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

--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/cli/src/commands/review/compose-review.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/fetch-pr.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/lib/ledger.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/lib/review-settings.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/pr-context-persist.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/save-artifact.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/compose-review.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/fetch-pr.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/lib/ledger.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/lib/review-settings.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/pr-context-persist.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/save-artifact.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/compose-review.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/fetch-pr.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/lib/ledger.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/lib/review-settings.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/pr-context-persist.test.ts: P (exit 0)
round 3 · packages/cli/src/commands/review/save-artifact.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/compose-review.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/fetch-pr.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/lib/ledger.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/lib/review-settings.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/pr-context-persist.test.ts: P (exit 0)
round 4 · packages/cli/src/commands/review/save-artifact.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/compose-review.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/fetch-pr.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/lib/ledger.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/lib/review-settings.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/pr-context-persist.test.ts: P (exit 0)
round 5 · packages/cli/src/commands/review/save-artifact.test.ts: P (exit 0)

Evidence images

01-ab-firing-head-arm

02-ab-control-base-arm

03-mutation-matrix-all-killed

04-targeted-gates

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

@wenshao
wenshao added this pull request to the merge queue Aug 24, 2026
Merged via the queue into QwenLM:main with commit 24db7f6 Aug 24, 2026
68 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.22.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants