Repository navigation
feat(review): swap re-review rounds to a fix-audit shape under the critical posture - #10136
Conversation
…itical posture Once a multi-round review settles into the critical-only posting posture (floor: c), the re-review round still ran round 1's full shape: the whole territory fan-out plus up to five full-width reverse-audit waves, while everything found below Critical was deferred anyway. Measured on PR #9729 round 15, the 3h13m / 131M-token round's entire finder fan-out contributed nothing postable, all three posted Criticals first surfaced in the reverse-audit waves, and the one-hop import widening re-entered 89% of the diff. When the posture is knowable at capture time and a usable anchor exists, the round now changes shape: - fetch-pr predicts the compose-time floor resolution from the side file pr-context persisted (round schedule and latched flatRounds — monotone arms only) or the CLI-recorded explicit floor, and records incremental.posture: "critical" with its postureCause in the plan. - The topology gate reads the posture: a fix-audit round is a territory fan-out whatever its narrowed sizes say, so the roster (chunk agents, no Agent 0), the round-cap tier and the #9242 note all follow from the one shared predicate. Chunk briefs carry a fix-audit frame; severities are unchanged (the floor governs posting, never finding). - Interaction files re-enter seam-bounded: only the hunks displaying a line that imports or uses what changed republish, with a seam: {kept, total} census per entry; a file with no seam hunk publishes header-only and still lands in a chunk so its brief asks the seam question from the worktree. Every doubt state republishes in full. - Reverse-audit waves narrow instead of the cap lowering: from round 3 a chunk holding no delta file leaves the schedule after one substantive dry audit and takes no cold checks, while delta territories keep the full retirement rules — recall stays where the measured fix-induced Criticals surfaced (waves up to the cap), over a shrinking front. - Every reduction is disclosed: the plan record, the briefs' seam census, a posture-narrowing note beside the retirement note, and a round-shape paragraph compose-review renders into the posted body. Closes #10104
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
|
@qwen-code /triage |
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
… off enforcement (#10136)
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Review-feedback round — PR #10136Commit Resolved (8 inline findings)
Declined (1 review-level item)
Escalated (1 review-level item)
Deferred to the next round (15 inline findings, batch bound ~8)R1-4 (retirement one-receipt discriminating test), R1-5 (fetch-pr recorded-floor wiring tests), R1-6 (APPROVE-branch disclosure test), R1-7 + documentation twin (seam census Notes
VerificationCommands actually run, in order:
中文说明评审反馈轮 — PR #10136提交 已解决(8 条内联发现)
拒绝(1 条评审级条目)
升级(1 条评审级条目)
延后到下一轮(15 条内联发现,批次上限约 8)R1-4(retirement 单次收据区分性测试)、R1-5(fetch-pr 已记录下限接线测试)、R1-6(APPROVE 分支披露测试)、R1-7 + 文档孪生(接缝计数 备注
验证实际执行的命令,按顺序:
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🔀 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 将重新运行。 |
|
🔀 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 将重新运行。 |
|
🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下: Review round summary — PR #10136All 8 distinct findings (15 inline threads) are resolved in code, each with a failing-on-pre-fix witness and a mutation probe where the change adds a guard. Nothing declined, deferred, or escalated this round. The Findings and dispositionsR2-1 — Critical (rc:3866310288, rc:3867715494, rc:3868766828, rc:3869699872): licence block contradicts the fix-audit plan arm. Reproduced first: the flagship test shape (posture'd plan, R4-1 — Critical (rc:3868766831, rc:3869699884): Agent-0 exclusion rationale disproved. Reproduced through the roster: a fix-audit plan whose published scope shows no deletion rosters exactly R5-1 — Critical (rc:3869699893): R2-2 — Suggestion (rc:3866310301): open-floor cause ternary. Reproduced: a drifted floor ( R2-3 — Suggestion (rc:3866310332): delta-list bar parity. Reproduced: R2-4 — Suggestion (rc:3866310340, rc:3866310349, rc:3866310356, rc:3866310362): "never posted" universal at 4 sites. Fixed at all four: compose engaged-floor sentence (en + zh), fix-audit brief banner, SKILL.md:709 parenthetical, and SKILL.md:299 topology bullet now carve out pre-confirmed R2-5 — Suggestion (rc:3866310373): tier clause. Fixed (prose): SKILL.md:299 now names the huge tier's 3 where a deadline-bound run's delta is itself huge — the huge gate is checked first. Witness N/A (prose). R2-6 — Suggestion (rc:3866310381): seam-census universal. Fixed (prose): SKILL.md:166 now says each entry that the seam scan could run carries the census, and names the doubt states (unreadable worktree source, hunk-less section) that re-enter full-range with no census. Witness N/A (prose). Notes
VerificationCommands actually run this round (all from the repository root unless noted):
中文说明评审轮次总结 — PR #10136全部 8 个独立发现(15 条行内评论线程)均已在代码中解决,每个改动都配有"在修复前代码上失败"的见证测试,凡新增守卫处均做了变异探针验证。本轮没有拒绝、延后或升级任何条目。评审正文中 发现与处理R2-1 — Critical(rc:3866310288、rc:3867715494、rc:3868766828、rc:3869699872):许可块与 fix-audit 计划分支自相矛盾。 先复现:旗舰测试形态(带姿态的计划、 R4-1 — Critical(rc:3868766831、rc:3869699884):Agent-0 排除理由被证伪。 通过 roster 复现:发布范围内没有删除行的 fix-audit 计划,其 roster 恰为 R5-1 — Critical(rc:3869699893): R2-2 — Suggestion(rc:3866310301):开放下限原因三元式。 复现:漂移的下限值( R2-3 — Suggestion(rc:3866310332):delta 列表门限一致性。 复现: R2-4 — Suggestion(rc:3866310340、rc:3866310349、rc:3866310356、rc:3866310362):四处"从不发布"的全称断言。 四处均已修复:compose 的"下限已启用"句(中英文)、fix-audit 简报横幅、SKILL.md:709 括注、SKILL.md:299 拓扑条目,均加入"预确认的 R2-5 — Suggestion(rc:3866310373):层级子句。 已修复(文字):SKILL.md:299 现在写明——当有截止时限的运行其 delta 本身达到"巨大"时取巨大层的 3,且巨大门先被检查。见证不适用(纯文字)。 R2-6 — Suggestion(rc:3866310381):接缝普查全称断言。 已修复(文字):SKILL.md:166 现在写明只有接缝扫描得以运行的条目才携带普查数,并点名两种疑态(worktree 源文件读不了、小节无 hunk)会以全量 hunk、无普查数的形式重新进入。见证不适用(纯文字)。 说明
验证本轮实际执行的命令(除注明外均在仓库根目录):
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
Conflicts resolved by keeping both sides: - compose-review.ts: union of the md-field and posture imports - SKILL.md: the branch's fix-audit 'effective' bullet with main's stop-sidecar 'upToDate' bullet - DESIGN.md: main's local-flow anchoring paragraph closing the import-hop section, then the branch's #10104 posture section Co-authored-by: Qwen-Coder <[email protected]>
…0136) The fix-audit roster keeps Agent 0 unconditionally for PR plans — the probe the roster comment records disproved dropping it — but three prose claims survived saying otherwise. Rewrite the Step 3B role-0 parenthetical and the user-docs fix-audit sentence to say the round keeps issue fidelity, and qualify the Step 6 floor paragraph and the floorEnforcedReroute docblock fail-open enumeration with the plan record arm they contradict. Also normalises pre-existing prettier markers on the edited docs line. Prose only; no behaviour change. Co-authored-by: Qwen-Coder <[email protected]>
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Address-review summary — PR #10136 (round 6)Dispositions[rc:3872893476] Critical R6-1 — SKILL.md says the fix-audit roster "deliberately omits" Agent 0 → FixedReproduced by inspection at the reviewed commit: Witness probe before editing: negating the Changes (commit
The prose itself is pinned by no test (as the finding noted); the two named [rv:5042213052] CHANGES_REQUESTED review body
Conflict resolution (
|
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x), Post Coverage Comment (ubuntu-latest, 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x), Post Coverage Comment (ubuntu-latest, 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
DESIGN.md's disclosure list still promised a plan census and a per-file bound in the brief, and its "what deliberately did not change" paragraph said nothing about the lever that came out. Both follow the code now, and the section records why the seam-bounded republication is absent: its premise is per-file while the only gate that can vouch it is per-base, and neither repair is proportionate — a per-file vouch needs publication state the byte-capped marker cannot carry, and a gate keyed to the last full-range round goes dormant at every merge-base advance. The user doc's disclosure list follows, and both narrowing rules this round added are named there too. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01TgejmvesNzoRG1KrdYJ8iX
|
@qwen-code /triage |
qqqys
left a comment
There was a problem hiding this comment.
Critical-only review pass at head 61ed6436ca7b49ea5460ca53596a8b03e24bf4b8. No merge-blocking defect found, and every Critical from the latest round is verified fixed or rendered moot by reading the current head code.
Round 22 Criticals, re-verified on this head
- R20-3 - entry identity for the whole staleness ruling - fixed. In
packages/cli/src/commands/review/lib/retirement.ts,fileLineTokenno longer accepts any first whitespace-delimited token: the code comment cites this finding and explains that the entry anchor cannot distinguish a genuine entry from a finding's own**Anchor:**block quoting the format template (- **File:** <file>:<line>), so a token that does not have the mandatedfile:lineshape (<file,[list) is rejected.entryTokensOfthen returnsnullfor a non-empty list that yields no token, and its documented fail direction is stale rather than a fresh reading of a list nobody could parse, so an unparseable list can never certify a chunk dry. - R22-2 - both list-based staleness arms required a file-backed list, so the inlined fallback could not satisfy them - fixed. Records now carry
findingsFromFile("the list was read back from its.findings.mdfile, not the prompt fallback"), and the per-round accumulator only collects a list intofileListsunderif (rec.findingsFromFile). The two sources are therefore tracked separately instead of one silently standing in for the other. - R22-3 - the staleness scan skipped rounds on the round-level dry fold - fixed. The per-round entry now also keeps
memberOutcomes: AuditOutcome[], one outcome per audit member, with the comment that the round-level fold hides them becausemergeOutcomesfolds['unknown','dry']to'dry'. A record no transcript certified pushes'unknown'as a member, so a round holding one dry receipt beside an uncertified sibling is no longer read as a round that certified the territory. - R18-1 (
lib/import-graph.ts), R20-5 and R22-1 (lib/incremental-scope.ts) - no longer this PR's surface. The seam-bounded widening was dropped inf948f869 feat(review)!: drop the seam-bounded widening from the fix-audit shape, and neither file appears in this PR's diff against main at this head (19 files, all undercommands/reviewplus the two bundled review docs), so the code those findings cited is not part of the change under review. The Risk & Scope section states the removal and why the lever's per-file premise could not be vouched by a per-base gate.
All 108 review threads are resolved, across both pages.
Current scan
- The posture prediction cannot outrun the resolution and fails toward the full round.
lib/posture.tsreturnsnullfor an explicitsuggestionfloor, for a side ledger that is not a non-null object, and for a round that is absent, non-integer or non-positive; it predicts'round'only fromround + 1 >= CRITICAL_FLOOR_ROUND, the same schedule constant compose reads. The flat-trend arm clamps the recorded streak toMath.max(round - 2, 0), so a planted or corrupt side file claiming a long streak cannot engage the posture off rounds the signal never measured. - Shape and posting floor stay aligned in the safe direction.
floorResolvesCriticalincompose-review.tsgates the new fix-audit arm onfloor === 'auto'(if (floor === 'auto' && fixAuditPlan === true) return 'auto-resolved';), so an explicit--severity-floor suggestionstill wins over a stale plan record, and every unrecognized state returnsundefined- the floor fails open, deferring fewer findings rather than more. The arm exists so a round that already spent its shape on the posture cannot then post sub-Critical findings in full beside a disclosure describing a posture it did not run. - Record identity is anchored.
RECORD_KEY_REis^reverse-audit--chunk-(\d+)--round-(\d+)--([0-9a-f]+])$, and the transcript-certification relation is inverted deliberately so one launch prompt that verbatim-contains several recorded prompts cannot credit every chunk with the same dry receipt. - The narrowing is disclosed rather than silent. The
narrowedlist of{ chunkId, dryRound }is the note itself, and the reduction also lands in the plan record, the chunk briefs and the posted body's round-shape paragraph.
CI
At review time every concluded check on this head was success, including Serve A/B, Integration Tests (no-AK), both Desktop Shell lanes, the TUI parity and OpenTUI gates and the real-daemon E2E lanes. Test (ubuntu-latest, Node 22.x), Lint & Static (ubuntu-latest, Node 22.x) and review-pr were still running; no failure is attributable to this change, and a pending check is not treated as a gate here. If either unit lane concludes red, that conclusion should be read before merging.
yiliang114
left a comment
There was a problem hiding this comment.
Reviewed at head 61ed6436ca7b49ea5460ca53596a8b03e24bf4b8 against base d0e393570, dimensions: correctness and security. Three findings inline — two Suggestions and one Nice-to-have. Not approving, and the reason is the adjudication below rather than my own findings.
The unresolved Critical on this head still stands — I read it independently rather than taking the bot's word. In lib/retirement.ts, mergeOutcomes (848-852) folds ['unknown', 'dry'] to 'dry'; the staleness scan skips any round whose fold is dry at 1168, so arm 3 (1200-1209) — the only consumer of a.filedFiles, collected fold-independently at 1101 — never consults a dry-folded round's filings. The member-level half exists at 1230 but reads latest.memberOutcomes alone, and narrowing first rules at round 3, so latest is round 2 and the round that can hide a filed sibling is round 1. listUnreadable (1244-1246) keys on the same fold axis. The module's own bar at 1140-1142 — "falls through to the ordinary rules and stays hot, the same fail-toward-auditing floor as every other refusal in this file" — is the direction this path breaks, and the R20-2 comment at 1211-1229 states the mechanism correctly while closing only its latest instance.
Verified clean, so it does not get re-litigated next round: every doubt state in resolveCriticalPosture returns null (posture.ts:88-106), the planted-streak clamp included; isTerritoryFanOut receives incremental at every call site I could find (budget.ts:420-424, 462 through cappedRoundTier, 634 failing toward the full tier on a bare object, roster.ts:322/433/465, agent-prompt.ts:3213 through PlanReport), so no size branch bypasses the shared predicate; Agent 0 is kept on a fix-audit round (roster.ts:303-320) — the PR body's four "no Agent 0" claims are the stale side of that pair; nothing narrows severities, and postureNarrowing fails every malformed shape to the ordinary schedule; the terminal narrowingNote (agent-prompt.ts:3397-3413) and the posted "Round shape" paragraph (compose-review.ts:7824-7840) enumerate the same stay-in causes from opposite perspectives, so the 3-versus-5 count is phrasing, not divergence. No new sink in the diff: no child_process, eval, dynamic import or new RegExp, every added pattern is a static anchored literal, the side-file path is keyed only by a twice-validated PR number, and no secret or internal host reaches the disclosure strings.
中文说明
不 approve 的原因不是我这轮的发现,而是当前 head 上那条未解决的 Critical 我自己读下来仍然成立:retirement.ts 的折叠级 dry 判定(1168)让 arm 3 的 filedFiles 对非 latest 轮永不被查阅,而 member 级判定(1230)只覆盖 latest,收窄最早在 round 3 裁决,能藏住已归档兄弟的恰好是 round 1。我另外提了 2 条 Suggestion + 1 条 Nice-to-have,都在 inline;同时把已核实为干净的点列在上面,避免下一轮重复审查。
|
@qwen-code /triage |
…floor Two from the bot, three from review. R20-3 (mine) — `FILE_LINE_TOKEN_RE` was one spelling short of the format this CLI itself orders. `FINDING_FORMAT` says `<file path>:<line number or RANGE>` and is interpolated into every findings-producing role; the reader accepted only `:12`, so a range-form filing yielded no token, arm 3 had nothing to look for, and the chunk was priced out of the wave over a finding no receipt ever saw. Measured on the lists one real round produced: 36 of 92 entries used the range form. Both halves are in — the regex reads the format now, and a file line NON-EMPTY after the tag strip that still yields no token is carried as doubt (`filedTokenlessly`) rather than as absence, so every spelling the allow-list does not know fails toward auditing. A line that IS empty after the strip still names nothing. R23-1 — the open arm asserted that "the posting floor itself" resolved OPEN while the same body's marker stamped `floor: c` and the mechanism-health note said it resolved to critical. The two readings diverge on exactly one state, an absent floor record beside a postured plan, and only the parenthetical attributed the openness to the enforcement reading. The clause says which reading it describes now. Review (yiliang114): - The round counter is a shared id space, and until now neither counter arm read the provenance the side file already records. Before this PR a counter another account wrote moved the posting floor; with it, the same counter buys less review WORK. Both counter arms refuse a record stamped `foreign` or `anonymousAdoption`; the `explicit` arm is unconditioned because it reads the operator's own CLI record. - The host-chain comment claimed parity with submit's four-term chain. The missing term is submit's RECORDED binding, and it cannot be a term here: the record IS what is being read, so binding the axis to its own host would make `recordedSeverityFloor` compare a value against itself. The comment says what it implements, and where the two-names shape is actually absorbed (`hostsEquivalent`). - The plan file licensing its own consequence is now stated rather than implied: DESIGN.md records the boundary (trusted for facts about the round's own shape, never for identity, which is the line `authorization.ts` already draws), and the posted body names the capture's plan record as the licence on exactly the states where the compose-side arms could not re-derive the posture. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01TgejmvesNzoRG1KrdYJ8iX
…listed The doubt arm this round added — a filing whose file line cannot be read as a comparable token — was missing from both surfaces that enumerate what returns a chunk to the ordinary retirement rules: the round's `posture narrowing:` note and the posted round-shape sentence. Same class the round filed twice against this PR's own prose, and the same answer: a disclosure that lists the rules lists the rules. The arms' own docblock said "three arms" and now says four, with the ordering that matters spelled out — each is reached only for a round the record does not certify dry, so a dry round's own text never rules. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01TgejmvesNzoRG1KrdYJ8iX
DESIGN.md described the posture prediction's counter arms without the provenance refusal they now make, and still said the open sentence states that "the floor" resolved open — the exact unqualified claim R23-1 filed. Both follow the code, with the reason each is keyed the way it is. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01TgejmvesNzoRG1KrdYJ8iX
…round-shape Main's explicit-deadline change (#11686) factored `sizeTier` out of the round-cap tier and shares it with the plan's new default wall. This branch flips the cap to the territory tier on a critical-posture fix-audit round. The posture is now read in the cap's small case, not inside `sizeTier`: a posture is not a size, and the wall keyed on that size must read the same tier whether or not the plan's incremental block rides along. A fix-audit round therefore keeps the 3B cap of 5 and the default wall its delta's size earns, and tests pin both halves. fetch-pr records `hasDeadline: wall.explicit` beside the incremental ruling. The SKILL, DESIGN and user-doc wording that said the huge reduction applies "where there is a wall" now says an explicit deadline.
The anonymous counter-advance in `persistRecoveredLedger` keeps this account's own findings and adopts only the winning marker's round, and it recorded nothing about where that number came from. The plan-time posture reader refused `foreign` and `anonymousAdoption`, neither of which this write sets, so an unattributable counter could engage the fix-audit shape and buy less review work. The write now stamps `roundAdoptedAnonymously: true`, a flag of its own. `anonymousAdoption` describes the list, and the closure mint reads it to decide whether absence can mean "ruled fixed" over a list this branch keeps as the account's own. The identity-known whole write rebuilds the file from the recovered ledger and so clears the stamp, and `resolveCriticalPosture` refuses it beside the other two.
…dering The posture narrowing decided whether a dry receipt had seen an earlier round's findings by reading the model-edited findings list: its entry set, the file lines a return filed, the entries a return quoted. Every review round found a rendering where a misreading granted the narrowing (a re-wrapped list, a range, an aggregate, a `./` prefix, a second finding at one location), and a stricter reader only moved the gap. The loop already fixes the order that question is about. Findings merge before every round build, and only the convergence pair is built together. So the ruling now counts launches: rounds 1 and 2 are one launch, and a non-delta chunk narrows only when every member of its latest launch is dry. Every earlier launch's findings are then already on the list the dry launch was built on. Two withhold-only checks guard where that order can break. If an earlier round with a member not certified dry shares a findings digest with the dry launch, the merge never ran. And a resumed run receives an interrupted attempt's returns only through `recover-findings`, whose bar is stricter than this module's scan. So every return that was not dry must share one stamped session with every dry receipt of the latest launch, or the narrowing is held. The list-reading arms, the location reader and the anchored quotation guard are removed, and `classifyReturn` matches main again. Also clears wording left from the removed seam-bounded widening in test titles and comments, and names the lone posture-narrowing note when a round retires nothing.
|
Takeover round 24: the merge conflict is resolved, both round-24 Criticals are answered, and the reverse-audit narrowing no longer reads the findings list. Head is
Verification. Before commit, six rounds of undirected audit and six of reverse audit ran against the change. Every surviving finding was fixed, and the last round found nothing new. Every new guard has a mutation that turns a test red. 中文说明接管第 24 轮:合并冲突已解决,第 24 轮的两条 Critical 已处理,反审收窄不再读取发现清单。 当前 head 为
验证。 提交前对改动做了六轮开放式审计和六轮反向审计。所有成立的发现都已修复,最后一轮没有新发现。每个新增守卫都有能让测试变红的变异。 |
…round-shape Main's #11779 documents the deadline's pause ceilings in a new "Review Deadline" section and points the default-wall paragraph at it. This branch had added a sentence to that same paragraph saying a fix-audit round keeps the wall its delta's size earns. Both edits are kept: the branch's sentence, and main's pointer at the end of the paragraph's parenthetical.
The reverse-audit schedule treated a chunk's prompt records as the complete list of rounds that ran. A record's write and its read are both best-effort, and a fail-open build prints every chunk whatever the schedule said. So a round can run, file a yield, and leave no record to pair. That round then vanished from the chunk's history: a stale dry launch before it read as the latest one and narrowed the chunk out for good, and a lost cold check let a retired chunk skip odd rounds and converge over the yield. Every launch block points its agent at the brief filed under its record key in the plan's record dir, so a transcript still names every key it was launched for. The pairing walk now records which keys each transcript paired with. Every other key it names, for a round before the one being built, joins that chunk's round, carrying the launch's findings digest and session: as the yield it filed, or as a member that certified nothing. That covers a lost record, an altered delivery beside a record that survived, and a multi-block launch. A transcript that matched several records, or names any key but the one it paired with, certifies nothing, but its yield still joins every record it paired with, so a dry relaunch cannot decide the round over it. A lost launch's quotation guard reads the findings list the CLI filed for that round and digest, never a pointer from the delivery. Keys are read from the flattened launch, the same text the pairing reads, and matched on the tail every absolute spelling of the record dir shares, in any letter case, so a plan spelled through a symlink, a relative path or another case names the same keys. A key whose record is on disk but older than the plan belongs to a dead attempt, so it is skipped. A transcript also names a block by delivering every line of its record but the brief line, read against every record outside the (chunk, round) it paired with, and a transcript that paired with no record names the brief its agent opened, as the harness recorded the call. So an altered brief line, alone or inside a multi-block launch, still counts, while an honest launch still certifies its own record. Transcripts are pre- filtered on the role id in the delivery or in a call's arguments, so an agent that opened its brief is read even when the delivery lost every mention of the role. A transcript that did pair names an opened brief only when no launch paired with that block's record (lost or partly written), and then certifies nothing. The posture narrowing and the ordinary retirement rule both read this history. An earlier launch that lost its record does not hold a later dry launch, because that launch was built after it merged. The narrowing also reads the clock. A return that was not dry and was written after the latest launch's first record was built cannot be on the list that launch was built against, so it holds the narrowing the way the list-bytes check does. That covers a block of an earlier round launched again inside a later wave, which pairs with its own old record on a digest the dry launch does not share. A record's build time is its birth time, which a reprint of the same block under the same key (the single-auditor repair) does not move; a record born before the plan, which a retry rewrote, and a filesystem that keeps no birth time, date from the last write. The CLI's posture narrowing note, the posted round-shape sentence, SKILL.md and the user guide now name the clock among the rules that return a chunk to the ordinary retirement rules.
|
Takeover round 25: main is merged again and R25-1 is fixed. Head is
Verification. Before commit: 中文说明接管第 25 轮:再次合并 main,并修复 R25-1。 当前 head 为
验证。 提交前做了 |
…re cannot see (#10136 R26-1) The unmerged guard compares list BYTES, which only ever detects a total merge skip: a partial merge — the orchestrator merging some of a round's returns and dropping one — changes the bytes, so the tripwire stayed false and a non-delta chunk was narrowed out over a filed Critical that never reached any list, permanently (no cold check, and CONVERGED could fire over it). The merge is a prose instruction to the model, not a mechanism, so prove it on the one artifact the merge wrote: the findings file the dry launch was built from, read by the round-and-digest key the lost-launch path already uses. For each earlier round with a yielded member, that list must carry the yield's filed **File:** line (the membership test classifyReturn already applies); absent, the narrowing is refused. The witness is refusal-only — a misreading keeps the chunk hot, and a file absent from disk is no evidence either way, so the records-only fixture shapes (:276-310, :340-374) grant exactly as before. Mutation: dropping `!partialMerge` from the gate reds the new partial- merge case while the control (both filings carried) still narrows.
|
@qwen-code /triage |
With #10136 on the branch two things answer to "fix audit": the PR re-review's narrowed round, read off the plan's `incremental.posture`, and Step 6B's one agent over the hunks a local `--fix` applied. Step 6B now says which one it is and why the two never meet in one run (the round needs a pull-request target, where `fix.effective` is false), so a reader cannot carry the round's convergence carve-out into a local `--fix` review. DESIGN.md records the same; SKILL.test.ts pins the sentence.
…wenLM#10169) * feat(review): audit the applied --fix for unpinned new assumptions Step 6B applied findings to the working tree and forbade re-running the review over the result, so `--fix` output shipped with no independent check at all — while the fix round is the loop's largest single source of its own next round (a third of post-first-round findings, #9578), and PR #9793's two fix-introduced Criticals survived a mutation probe of the intended fix sites. Add a scoped fix audit, not a re-review: `review fix-delta` records the working tree before the first edit (a tree object through a throwaway index — the user's index and the stash stack are never touched) and diffs the tree against it afterwards, with the review's own side files excluded; `agent-prompt --role fix-audit` renders the `fixed` findings above those hunks into one digest-keyed list file and prints the launch block for a single agent whose brief asks one question per hunk — what does this edit newly assume, and does anything in the tree pin it — and reports only the unpinned. The role reads no diff, carries no finder machinery and no project rules, produces no verdict, and its output is a disclosure (the affected finding's outcomeNote plus a terminal block), never a finding. The command refuses an artifact without outcomes, one with no `fixed` finding, and empty hunks beside a ledger that claims a fix. Closes #10154 * fix(review): close round-1 fix-audit holes (#10169) Co-authored-by: Qwen-Coder <[email protected]> * fix(review): close round-2 fix-delta holes (#10169) * fix(review): close round-3 fix-delta holes (#10169) * fix(review): close round-4 fix-delta holes (#10169) * fix(review): close round-5 fix-delta holes (#10169) * fix(review): fail closed on fix-delta blind spots and capture notes (#10169) Close the round-6 Criticals structurally instead of per entrance: - The blind-spot probe now answers in two states — confirmed dirt and unresolved paths — instead of one folded flag. Unopenable directories, failed inner probes and over-budget walks are disclosed at every comparison and never enter the snapshot baseline, the exclusion families are applied to what the ignored-directory walk discovers, and the empty-diff all-clear is hedged to what `git add -A` captures. - The `add -A` capture is ruled on the child's own stderr line-by-line (spawnSync keeps stderr on exit-0 paths too): only the zero-commit nested-repository error, the embedded-repository notice with its hints, and the autocrlf normalisation warnings are tolerated — any other note refuses the snapshot and names the gap. - The hunks artifact is written as git's raw patch bytes; `-z` names are decoded one by one, so non-UTF-8 fix content and filenames survive. * fix(review): close round-7 and round-8 fix-delta holes (#10169) - Refuse a `git add` child that did not exit normally (killed by timeout or buffer overflow): its notes are a partial capture, and ruling on them recorded HEAD's tree as the snapshot baseline (R5-2). - Raise the `add -A` spawnSync maxBuffer to the 512 MiB ceiling every other git spawn in this tree uses, so a large autocrlf tree's warnings cannot kill the child mid-capture (R7-7). - Pin LANG/LC_ALL to C for the review git children: the tolerated-note patterns match git's English rendering, and translated catalogs turned every tolerated shape into a hard refusal (R7-5). - Render git-facing paths in git's `/` separator: the in-tree git-dir exclusion pathspec (R7-8), the exclusion matching and walk-discovered disclosure names, which compare against git-originated `/`-separated names in reporting and baseline identity (R7-6, R8-1). - Fix the suite's absence assertions to name the all-clear the command actually prints; the old phrase appears in no output, leaving the never-all-clear-beside-a-blind-spot contract unpinned (R7-9). - Skip the two POSIX-only fixtures on the Windows lane (R7-1, R7-2) and normalize the recorded snapshot root before comparing it (R7-3). - Carve the empty-hunks refusal out of the fix-audit blanket refusal rule in the skill: it is a ledger/tree mismatch whose message directs a correction, not an audit failure to move past (R7-10). * fix(review): close round-9 and round-10 fix-delta holes (#10169) Resolve the outstanding Criticals on `fix-delta` and the fix-audit refusal, and merge origin/main (the one conflict — the `review` demandCommand message — is resolved by keeping BOTH new subcommands, main's `emit-workflow` and this PR's `fix-delta`). R9-1: pin `-c core.quotePath=false` on the hunks `diff-tree` so a non-ASCII file name arrives raw in the hunks headers exactly as it does in the summary; the default quoted form gave the fix audit two spellings of one file. R9-2: exclude the command's own `--out`/`--since` paths from capture, comparison and probe whenever they resolve inside the repository — the name families only cover what the REVIEW flow writes, and a non-canonical in-repo side file was entering the hunks as bookkeeping. R5-1 (structural): discover nested repositories from the index's own mode-160000 gitlinks, not status entries alone — assume-unchanged / skip-worktree bits hide dirt from BOTH status runs; confirm an empty inner status against the index's tags instead of reading it as clean; treat unknown-type (DT_UNKNOWN) dirents in the ignored-directory walk as unreadable instead of silently skipping them. Uninitialized gitlinks still stay quiet. R8-2: resolve a symlink's TARGET against the family / in-tree-git-dir exclusion at both probe sites, so a link planted at a non-family name cannot reach the review's own worktrees past the exclusion. R8-3: reassemble multi-line zero-commit `git add` notes at line boundaries before matching; a nested repo whose name contains a newline is tolerated as promised, not refused. R7-10: replace the empty-hunks refusal's impossible "re-take the snapshot before the first edit" remedy with the no-recovery disclosure — at refusal time the edits are already in the tree — in both the skill and the fix-audit prompt, and re-pin the tests to the new wording. Also harden the suite teardown against the detached auto-gc race the 11k-file tree triggers (rmSync retry). Every fix lands with its own witness; mutation probes confirm each guard turns its test red when removed. * fix(review): refuse side-path symlink writes, close blind-spot entrances (#10169) * fix(review): close round-11 fix-delta holes (#10169) R11-1: pass --binary to the hunks diff-tree. Without it a binary-content edit entered the hunks — the command's sole output — as a bare "Binary files ... differ" stub: no patch data, not git apply-replayable, while the summary still reported the file. Witness: a NUL-byte fixture edit must arrive as a GIT binary patch. R5-1: key the blind-spot identity on the raw path bytes (the latin1 byte<->char bijection) instead of the lossy display decode — decodePath is not injective, and a clean nested repo named bytes C3 A9 swallowed its dirty sibling named byte E9 (both render 'é') through the `seen` mark, printing the bare all-clear beside the landed edit. The persisted baseline compares the same keys. Walk-discovered rels now join with git's '/' on every platform so both discovery routes key identically. R5-1 (round-7 entrance): refuse the spawn UTF-8 coercion in the inner probe — a name the bytes cannot represent would reach the child mangled to U+FFFD and probe a planted decoy in place of the real repository. The probe fails for it instead; the failure direction over-warns. R11-2: failure-couple the Step 6B fix-audit block in the skill — `&&` between the hunks producer and the auditor prompt, plus the branch for the producer's failure: fix-delta writes its hunks only as its final act, so a failed producer leaves whatever an interrupted earlier run wrote at the same deterministic path, and the auditor must not run over it. Re-pinned in the skill test beside the producer/consumer pins. Every fix lands with its own witness; mutation probes confirm each guard turns its test red when removed. * fix(review): close the fix-audit's blind spots and its claim-vs-edit gaps Round-15 review findings on the Step 6B fix audit, addressed at the mechanism rather than entrance by entrance. fix-delta — the probe no longer trusts the tree it measures: - Every probe child carries `-c core.fsmonitor=` and `--work-tree <path>`. A repository discovered in the working tree owns a `.git/config` that is writable as this user, and `core.fsmonitor` runs a command on `status` AND on `ls-files -v` — the measurement became the execution. A repo-local `core.worktree` redirected the same status at a pristine decoy, so the probe answered clean over an edit on disk; the work-tree pin outranks it, and a probe that cannot honour the pins answers `failed`, never `clean`. - The baseline records a DIGEST of the state inside each dirty path, not a boolean. "Already dirty at snapshot time" is now a checkable claim: a level-2 gitlink that merely moved used to stamp its parent, after which a fix's real edit inside that parent was filed as dirt that was already there and the blind-spot warning went silent. - The probe's status runs with the review's name families left IN, and prunes what it discovers by name instead. A nested repository planted at `sub/.qwen/tmp/qwen-review-*` fell out of the capture pathspec and out of the probe's at once; the families hold review FILES, never a repository, so only the worktree family (`review-pr-*`) prunes one. - A symlink whose target merely CONTAINS a repository is walked, not returned from silently; the git-dir exclusion compares bytes rather than the non-injective display decode. - Repo-local config that shapes the capture and the rendering cannot be overridden, so it is disclosed: `filter.<name>.clean`, `info/attributes`, `core.attributesFile` and an active `info/exclude` at both moments, and any changed path whose `diff` attribute resolves to `-diff` or a driver — the surface that turns a text fix's hunks into an unreadable binary patch. agent-prompt — the ledger and the tree are cross-checked: - Zero `fixed` outcomes no longer asserts "nothing was applied" without reading `--hunks`. Beside hunks that landed it is a ledger/tree mismatch, the mirror of the empty-hunks refusal, and the audit is not what to skip. - Each `fixed` finding is reconciled against the hunk headers: one that no hunk corroborates is marked in the rendered entry (a fix may legitimately land elsewhere), and an input where none of them is corroborated is refused. The brief says what the marker means. SKILL.md — the two claims that were false as written: the skip clause now requires an empty hunks file as well as an empty `fixed` set, and the interactive path rules per target instead of asserting a sweep that never reaches a file review's plan — `file-review-…` is outside every cleanup prefix by contract, so when that plan survives the audit runs there. * fix(review): ask the attribute probe about the raw name bytes `renderSteeringPaths` was fed `filesBetweenTrees`' DISPLAY names. A name whose bytes are not UTF-8 renders through latin1, and re-encoding that string as UTF-8 asks `check-attr` about a different path — one no rule matches — so it resolved `unspecified` and the run answered "no steering" for exactly the path it could not name. Fail-open, in the one direction the disclosure exists to close. `filesBetweenTrees` now hands back the raw name buffers and the display decode happens at the reporting edge, where it belonged: the probe asks about the bytes, the summary prints the rendering. Pinned by a `bad<0xE9>.txt` fixture whose `-diff` rule is planted with the same bytes — feeding the decode back in reds it. * chore(review): smooth the claim-vs-edit refusal wording * fix(review): derive the fix-delta prune from git's registry and pin the outer spawns Round-16 findings on the fix audit, closed at the model rather than entrance by entrance: - R5-1: nothing prunes by name any more. The probe excludes the in-tree git dir and the review's own worktrees — repositories under the worktree family whose gitfile points into the audited repository's `worktrees/` registry — and walks or probes everything else, so a repository planted under a worktree family name, or one level under a side-file family directory, is discovered. The digest carries the repository's identity (`status --branch --show-stash`) and is kept for every probed path, so content committed or stashed inside a nested repository the trees record nothing of is disclosed. - R16-1: every outer spawn against the audited repository carries the probe's pins (`--work-tree <root> -c core.fsmonitor=`) — `diff-tree` with a pathspec included, which loads the index — and root derivation is gated: the tree git names must contain the cwd, or a repo-local `core.worktree` redirect is refused. - R16-2 / R16-3: `captureSteeringSurfaces` names `core.excludesFile` (when its file carries an active rule) and `filter.<name>.process`. - R16-5: the tracked half of a family match is recorded after the family exclusion (`add -u` over the paths the throwaway index tracks, byte-exact through `--pathspec-from-file`), and the tree comparison runs under the literal excludes only. - R13-2: `hunkHeaderPaths` admits git's C-quoted header productions, contributing nothing on a decode it cannot complete. - R16-4 / R16-6: Step 6B enumerates the three ledger/tree-mismatch carve-outs and relays fix-delta's stderr qualifications under the Fix audit heading; DESIGN.md follows. Also tolerates the "unable to index file" note newer gits (2.55) pair with the zero-commit note, for the same path only. * fix(review): anchor the fix-delta baseline and close the round-17 blind-spot entrances Round-17 findings on the fix audit: - R5-1: the registry check binds back — a review worktree is one whose `<common>/worktrees/<entry>/gitdir` names the probed path, so a gitfile merely pointing into a genuine entry is probed. A review worktree is walked rather than pruned on every discovery route, so a repository planted inside it is found. Two new transitions, `appeared` (answered now, no baseline) and `vanished` (baseline, no answer now), join the all-clear gate; the six transitions now cover every key of either digest map. The interior of a git directory is ruled out of scope and the all-clear line says so. - R17-1: both digest maps are null-prototyped, so a repository named `__proto__` is an ordinary key on the probe side and the loaded record. - R17-2: `hunkHeaderPaths` is hunk-aware — after `@@ -l,s +l,s @@` the counted body lines are consumed before header scanning resumes, so a deleted `-- a/<path>` content line can no longer corroborate a finding. - R17-3: with a clean/process filter configured, `add`'s notes are ruled on their own lines (no multi-line reassembly a forged opener could absorb); the single-line zero-commit pairing stays, which keeps the audit for Git-LFS users on git >= 2.55. - R17-4: `--snapshot` prints the full tree sha and a SHA-256 fingerprint of the record; `--since` requires `--fingerprint` and refuses a file whose bytes no longer match. Step 6B carries the printed hex into the `--since` call and never recomputes it from the file. Two rounds of independent reverse audit before this commit: round 1 caught the LFS regression of the first cut (pairing disabled under a filter) and the fingerprint-recompute loophole; round 2 found no silence-direction defect and a DESIGN.md sentence that contradicted the code, fixed here, plus the two route pins for the review-worktree walk. * fix(review): classify review worktrees by the orchestrator's naming and close the round-18 entrances Round-18 findings on the fix audit: - R5-1: nothing in the tree classifies a review worktree any more — the registry lookup, its back-link and the family-name match are gone. `fix-delta --review-worktree <path>` (repeatable) names the worktrees this flow created; those are walked for planted repositories and never probed, everything else is content and its dirt disclosed. A `--fix` run names none. The digest's status is pinned to `core.quotePath=true` so a non-UTF-8 name cannot collapse two interior states into one hash. - R18-1: the mode guard counts presence; an empty `--since` is refused. - R18-2: a link resolving to the audited root, or a walk re-entering it, is recognised by filesystem identity (dev/ino) and never probed as its own nested repository. - R18-3: the side-path redirect check runs again before each write, covers the ancestor chain of an in-repository side path (bounded by the checkout, from the nearest existing component), and the writes open with O_NOFOLLOW where the platform has it. - R18-4: `check-attr diff` is asked about a rename's source names too. - R18-5: root derivation also requires the git dir discovered from the cwd to be the git dir the derived root belongs to. - R18-6: the capture pins `core.autocrlf=false` (and `core.safecrlf=false` beside it); the nested-repository probes keep git's own view of "modified", for the reason stated in DESIGN.md. Found by the reverse audits before this commit rather than by the review: in a repository that ignores `.qwen/` — this one does — the skill's own in-tree `--out` made `add -A` exit 1 on the negative pathspec item, so every fix audit refused there. Ignored side paths now stay out of the capture's pathspec (`check-ignore`), pinned by a test in the exact skill shape. Two rounds of independent reverse audit: round 1 found the ignored side path refusal, a resolved-spelling root check that a differently-cased link defeats on APFS, and a `safecrlf=true` refusal under the autocrlf pin — all fixed here; round 2 found no silence-direction defect and a DESIGN.md sentence describing the rejected root check, corrected. * fix(review): close the round-20 fix-delta and fix-audit entrances - fix-delta: key the blind-spot walk on filesystem identity — one physical directory walked, one physical repository probed, one entry budget shared by every walk of the run — so a link cycle inside an ignored directory cannot mint tens of thousands of synthetic probes (R20-14). - fix-delta: close the blind-spot state matrix. `--ignored=matching`'s `!` entries ride the digest but never count as dirt; discovery stat failures are ruled by errno so only ENOENT skips silently (ELOOP and its kin disclose); and the baseline persists the unresolved paths, so an unresolved-then-gone path is its own transition that gates the bare all-clear (R5-1). - fix-delta: derive the honest toplevel config-blind — walk the cwd up to the first `.git` entry (directory or gitfile) and refuse when it is not the root git named — plus refuse the git-dir re-entry signature, closing the subtree and planted-gitfile-ancestor `core.worktree` redirects the containment+identity gate passed (R18-5). The nested probe's status is likewise refused when its toplevel is not the path it was asked about. - fix-delta: disclose a tracked family-named path the fix renamed away — the hunks can only certify the bare deletion (R19-1) — and take the house 512 MiB maxBuffer at the shared git opts so the steering enumeration cannot overflow into silence (R19-2). - agent-prompt: decode C-quoted path tokens by code point, so an astral character in a quoted name no longer decodes to U+FFFD pairs (R19-3). - agent-prompt: drop the wholesale "no hunk corroborates any fixed finding" refusal — a fix can legitimately land in files no finding names, and the two states are indistinguishable to the command, so the all-unmatched case is annotated and built; SKILL.md, the brief, and the user docs rule it the same (R20-26). - skill/docs: state the fix audit's reach precisely — the local/file `--fix` path where `fix.effective` runs; the #9793 incident happened on the posted-comment path #10153 covers (R20-1). Each new guard carries a pin that reds when the guard is removed (proven per guard); the full review suite passes apart from the eight pre-existing macOS-only failures that fail identically on the base. * fix(review): close the round-19/20 fix-audit findings with a state matrix and config-blind gates fix-delta: - The comparison is a closed 4×4 matrix (dirty/clean/unresolved/absent at both moments); unresolved paths are persisted for disclosure so U→A is reachable; a status-discovered path whose interior probe fails is unresolved, never "dirty without a digest". - Ignored (`! `) entries of a nested repository ride its digest but are not dirt; its collapsed entries take the top level's routing entry for entry (a directory that IS a repository is probed, one that holds repositories is walked, a slashless entry may be a link); an ignored entry appearing or vanishing is disclosed by the interior-moved note. - Nested probe: `.git` must resolve (through a link) to the git dir git discovers from inside; a clean/process filter in any non-user scope of the merged config (own config, include.path/includeIf, per-worktree; read from `-z` fields so a newline in a value cannot forge a scope, and an answer that does not pair off is unresolved, not "no filter") makes the repository unresolved; errno-keyed catches (only ENOENT silent); FIFO/socket/device dirents classified. - Walks are keyed on filesystem identity (dev:ino; no identity on ino 0), with a per-walk budget and a per-run cap. - Root gate is config-blind: the derived root must be the directory of the first `.git` git's own discovery accepts at or above the cwd (a gitfile, or a directory whose HEAD validates). - Tracked family-named paths deleted between the trees are disclosed. - Every git wrapper carries the 512 MiB maxBuffer; an unreadable filter enumeration is disclosed as such. agent-prompt: C-quoted header names decode by code point; the wholesale "no fixed finding corroborated" refusal is replaced by per-entry annotation; only a hunks file with no header line is refused. SKILL.md/DESIGN.md/docs: reach stated exactly (local/file --fix path; the posted-comment path is #10153's); carve-outs are the two ledger/tree refusals plus the annotation rule; core.ignoreCase named as a residual. Verified: fix-delta 122/129 locally (7 non-UTF-8-name fixtures are EILSEQ on APFS) and all 129 in node:22 (git 2.39.5); agent-prompt 324; SKILL 63; review-directory suite 5898 passed; 24 mutants each killed by one pin; two rounds of independent reverse audit before commit, the second closing a nested collapsed entry that IS a repository, the global-filter scope ruling and the HEAD validation. * fix(review): close the round-21/22 fix-audit findings — hooks, discovery bounds and invented deletions fix-delta: - Hooks are steering the tree carries: every spawn now pins `core.hooksPath` at a path that cannot exist, and the capture's `check-ignore` — the one working-tree-measuring spawn without them — takes the probe pins, so neither `core.fsmonitor` nor a `post-index-change` hook runs inside a measurement (R21-1, R21-2). - A nested repository's collapsed entries take the top level's routing entry for entry, so a repository under an ignored path OF a nested repository is discovered rather than all-cleared over; identity dedupe goes through the house `hasVerifiableInode` predicate, so a filesystem reporting no inode walks and probes rather than collapsing every directory into one; and the walk is bounded to the audited repository, so a directory link out of the tree is named as scope and never baselined — by bytes and identity, since Node's portable realpathSync cannot read a path byte no UTF-8 decode accepts (R5-1, three entrances). - A `.git` entry planted inside the working tree narrowed every reading to a subtree: the run is refused when an enclosing checkout answers for the same git dir, which no submodule or linked worktree produces (R18-5). - The walk budget is charged per directory OPENED, not per dirent: this checkout's own node_modules (8,174 directories, 84,566 entries) spent the ceiling on every run, so `node_modules` was disclosed as unresolvable every time and the all-clear could never print (R22-1). - A family-named path the user staged without committing is captured from the user's index too — the throwaway index seeded from HEAD cannot see it, so a fix's edit to it produced an empty delta (R22-2). - A deletion the capture invented — a path git now reports ignored whose file is still on disk — is dropped from the hunks and disclosed, so the auditor's sole input never asserts an edit the fix did not make; the ignore test is what keeps a file the fix replaced with a directory of the same name in the hunks (R22-3). SKILL.md: the no-`fixed`-beside-landed-hunks refusal names its out-of-band cause and gets an exit that is not inventing a `fixed` outcome (R21-3). Verified: fix-delta 131/138 locally (7 non-UTF-8-name fixtures are EILSEQ on APFS) and all of them in node:22 (git 2.39.5); agent-prompt 331; SKILL 64; 38 mutants each killed by one pin; measured in this checkout that the walk no longer discloses node_modules on every run; reverse-audited before commit, which closed the invented-deletion classification, the byte-safe root bound and the out-of-root scope bucket. * fix(review): close the round-23 fix-audit findings - fix-delta: the staged-family re-inclusion and the ghost classifier test the ENTRY with `lstatSync` (drop only on ENOENT), so a staged or untracked DANGLING symlink is no longer dropped from both trees / skipped by the classifier — git records a link (mode 120000) whether or not its target exists (R22-2, R23-3). - fix-delta: `ghostDeletions` asks `check-ignore` with `--no-index`: the question is whether the RULES hide the path, and without it the user's index outranked them, so a staged-but-uncommitted file was never recognised as a capture-invented deletion (R22-3). - fix-delta: `assertCompleteCapture` splits the shared stderr on the note boundary too (`/\n|(?=(?:error|fatal|warning|hint): )/`): a filter child that omits its trailing newline merged its bytes with git's next note, and the prefix-anchored tolerance absorbed the note that proves a path was skipped (R23-2). - fix-delta: `isGitEntry` asks git (`rev-parse --git-dir`) instead of approximating `validate_headref` — `ref:` with zero whitespace and uppercase SHAs are git's to rule, not a regex's — and gate 3 walks every strict ancestor holding a `.git` path UNVALIDATED, comparing git dirs, because a validating predicate's miss stood the gate down (R18-5). - fix-delta: one classifier at every walk/probe entrance (R5-1): `escapesRoot` is tri-state (inside / outside / unresolvable — an unresolvable link rides `unresolved`, never `outOfRoot`), consulted by `probeLinkedRepo`, `probeNestedInterior` and `walkAndProbe` alike; a link to an out-of-root REPOSITORY is still probed (its uncommitted dirt gates) but rides the outOfRoot bucket, so a commit inside a foreign store reads as scope rather than a move inside the audited tree; the status-entry route registers the physical repository in `state.probed`; the nested interior sweeps its own index for tracked symlinks; the in-tree git-dir comparison is byte-exact; and where the inode cannot answer, identity falls back to the resolved spelling. - skill/cli: the out-of-band exception is keyed on OWNERSHIP, not path/finding overlap — a foreign write can land in a path a finding names, and the overlap-keyed exception instructed falsifying the ledger there — and the empty-hunks refusal names the third cause (the edit landed where the capture cannot see it) in both the ruling and the message (R21-3). Each new guard carries a pin that reds when the guard is removed (proven per guard; the non-UTF-8 byte-shadow case runs on Linux, like its siblings). Suites: fix-delta 142 passed on macOS (the 9 failures are the pre-existing EILSEQ/teardown class, identical on the base), the rest of the review directory 5706 passed, agent-prompt 331, SKILL 64, tsc/eslint/prettier clean. * fix(review): carry the capture's pathspec as bytes through stdin The capture's exclusion reached `add -A` as argv: spawn args coerce a name that is not valid UTF-8 to U+FFFD, so an in-tree git dir named with such bytes (a `--separate-git-dir` the user chose) fell out of the exclusion, and the capture recorded the audited repository's own objects as edits — caught on Linux CI by the round-23 byte-shadow pin (`probes a repository whose name decodes to the in-tree git dir name`, whose planted repo sits exactly at the mangled name). The `add -A` call now reads the pathspec with `--pathspec-from-file=- --pathspec-file-nul`, the same channel the tracked re-inclusion already uses, with the git-dir entry carried in `inTreeGitDirBytes`' raw form. * fix(review): stop excluding the in-tree git dir by its decoded name in the probe pathspec The probe's status/diff pathspec carried the in-tree git dir through argv, which coerces a non-UTF-8 name to U+FFFD: beside a `--separate-git-dir` named `gd-<0xE9>`, the exclusion matched a lookalike directory (`gd-<EF BF BD>`, valid UTF-8) and hid exactly the content the probe exists to see — the Linux run of the round-23 byte-shadow pin caught it. The probe pathspec now drops the git dir entirely: `probeExcluded` prunes it byte-exactly on every discovery route, and the trees never contain it (the capture's exclusion is the raw-bytes stdin pathspec). The byte form becomes the one exported pathspec (`capturePathspecBytes`), and the test pins move with it. * fix(review): close the round-24/25 blind-spot and capture-invention entrances The fix audit's measurement mis-answered a shape per route, each route assuming another had ruled it: - probeNestedRepo — the funnel every discovery route git-spawns, digests and baselines through — carried no root-containment ruling of its own, so an index gitlink whose worktree is a link out of the tree (and a repository one level inside an out-of-root link target) was baselined as this tree's content. The funnel rules the reach first now: the audited root returns, an unresolvable path rides unresolved, an outside one is recorded as scope — and probed all the same, keeping the link route's deliberate probe of out-of-root dirt. - A discovered link whose target is not a directory was dropped unclassified; the reach ruling now precedes the directory test on the link route and inside the ignored-directory walk alike. - A tracked file whose worktree copy is a symlink carries an N... sub token and an unchanged index mode, so neither the S-token gate nor the index sweep routed it anywhere; the record's worktree mode (120000) routes it through the link classifier now. - The nested status spawn pins core.trustctime (a nested repository's own config answered clean over a same-size, mtime-restored edit), and is ruled on stderr as well as exit code: an exit-0 "could not open directory" warning certified clean over a subtree nobody read. The index-bit confirmation recurses to the depth the status was taken at, bounded by the probe run's visited/budget state. - isAuditedRoot's lexical fallback compared a latin1 decode of the path bytes and failed open on a non-ASCII root; it compares bytes. - inTreeGitDirBytes accepted '\' as the root/git-dir boundary byte on every platform; on POSIX a sibling '<root>\packages' git dir then read as the in-tree 'packages/', dropping that subtree from capture and probe. The byte is win32's separator spelling only. - The T2-side transitions (appeared/movedInside/vanished) keyed the scope exemption on the union of both moments — a path that was a link at snapshot time and holds an in-tree repository now is content of this tree and is no longer exempted on the baseline's say-so. The scope note keys on freshDirt, so pre-existing dirt on an out-of-root path falls back to the note instead of cancelling into a bare all-clear. - The capture-invention classifier was deletion-only: an ignore rule REMOVED between the moments admitted pre-existing untracked content as full-file additions. The snapshot records what the rules hid (ls-files --others --ignored), and the addition side is ruled against that record — the first moment's rule set is gone by --since. The deletion side now refuses a ghost when the path holds a directory: a real replacement the fix made, not the capture's invention. - The file-target plan family (file-review-*) joins the excluded side families, so a Step 6B re-run on a file target stops capturing the audit's own brief/input/launch records as the hunks it audits. - recover-findings filters the fix-audit key from the sections channel too: its disclosures are not findings, and a resumed run re-audits from a fresh snapshot, so nothing is lost. Every entrance carries a red-proven test (guard removed, witness red, guard restored); the pins the findings named stay green — the all-clear gate's freshOutOfRoot keying, the literal pathspec's glob-name case, the three neighbouring ghost cases, and the byte-shadow pathspec pin. * test(review): skip the unreadable-interior witness where permissions cannot bite win32 has no POSIX permission bits to make the directory unreadable and root bypasses them everywhere, so the fixture's precondition cannot be constructed there — the same guard the suite's other unreadable-directory witnesses carry. * docs(review): name the channel the fix-delta snapshot fingerprint prints on The Step 6B contract said "prints one line" without saying where; the line goes to stderr (writeStderrLine), and an orchestrator capturing only stdout loses the fingerprint and walks into the "Never recompute it from the file" prohibition with no recovery path. Pinned in SKILL.test.ts. * fix(review): close round 26's capture, probe and protocol entrances The R26 review of the fix audit's measurement found one leak per layer: - The family re-inclusion trusted "the index holds it, so it is user content" — and Step 6B's own artifacts (the outcomes/findings rebuild between the two moments) arrive exactly that way when the user staged `.qwen` in a checkout that does not ignore it. The re-inclusion now drops the flow's own bookkeeping by name, and every path it still admits is disclosed on its own line. - inRepoSidePaths decided containment lexically against the canonical root, so a side path spelled through a symlinked prefix (every macOS /tmp path) read as outside and the side file was never excluded — and the redirect guard's ancestor walk stood down over the same spelling. Containment now goes through repoRelativeOf (canonical, with the not-yet-existing leaf resolved through its nearest ancestor), while the redirect guard judges each symlink component of the typed spelling by where its parent canonically sits: a link inside the checkout must resolve inside it, a link whose parent is outside is the user's own layout. - The capture's note ruling gated its strict per-line form on a filter being configured, but the tolerated notes embed raw path bytes — a nested repository named `A'error: 'B` forges the unterminated opener with no filter involved, and the reassembly absorbed a real failure behind it. The ruling is unconditional now, and the filtersConfigured plumbing is gone. (A zero-commit note split by a newline in the repo's own name now refuses — the failure direction over-warns.) - The capture claimed content filters had no equivalent override; filterBlankEnv is exactly that. Repo-local filter commands are screened and blanked through GIT_CONFIG_* pairs on the capture's spawns and the discovery status (a planted `filter.*.clean` no longer executes as the reviewing user), while global filters (the user's own, e.g. git-lfs) stay. - The blind-spot probe's discovery status ran through a wrapper that discarded stderr, so an exit-0 `could not open directory` over a subtree certified a partial enumeration as clean. The spawn now rides gitRawReport (raw stdout kept, stderr captured, status kept): a warning lands the named path (or the root, when the note's shape is unknown) in unresolved, never clean. - probePins gains core.checkStat=default beside core.trustctime — a nested repository's own core.checkStat=minimal reopens the same stat-cache cell one knob over. - The index-bit recursion treated an UNREADABLE level-2 checkout like an absent one; only ENOENT means "nothing there" now, and anything else is unconfirmable — never clean. - The snapshot's hid-set unions ls-files --cached --ignored with --others: a staged-but-uncommitted ignored file was in the user's index and invisible to both, so a removed rule admitted it as a fabricated addition the classifier could not see. - Tree-controlled strings (path names, config values) interpolate into the single-line protocol notes escaped — a config value or directory name carrying `\nfix-delta: …` forged a standalone protocol line, including a fingerprint receipt the orchestrator would keep over the real one. Escaping happens at the render boundary only; identity keys stay bytes. - The subtree-narrowing gate keyed on an enclosing checkout answering for the SAME git dir; a planted `.git` gitfile naming ANY OTHER git dir narrowed every reading identically. A gitfile narrowing now requires reciprocal registration — a gitlink in the enclosing index, or a worktrees/<name>/gitdir entry naming this checkout's `.git` — and a planted gitfile is registered from nowhere. The same-git-dir refusal and the copied-worktree carve-out stand. Each fix carries a red-proven test (guard removed, witness red, restored green), including the two-directional ones: the newline-named zero-commit repository test inverts from tolerated to refused, and the two redirect re-check fixtures move their swap onto a GLOBAL filter — the one channel the blanking deliberately leaves executing. * fix(review): narrow the file-target family's own records out of the re-inclusion too R26-1's narrowing keyed on the qwen-review-{target}-* suffixes the finding enumerated; the file-target plan family carries the same flow bookkeeping — the plan report and the role-keyed prompt records under its -prompts directory — and a staged copy of either rode the capture identically. Same contract: the names the flow writes are dropped by name, user content under the family still re-includes, and the disclosure line still names everything admitted. Test extended with both file-review shapes; removing the narrowing reddens it. * test(review): pin the fix-audit wave emit-workflow builds for Step 6B Step 6B now dispatches the auditor as a one-agent batch manifest through emit-workflow, so pin that the batch path builds it verbatim and without a worktree pin — a local or file plan carries no worktreePath, and the audit must read the working tree the fix was applied in, not a review tree. * fix(review): close the round-10 capture entrances and anchor the hunks hand-off R10-1: the redirect check lstat'd only the prefixes of the excluded directories, so a symlink planted at a family-matching NAME under .qwen/tmp redirected every side file the flow writes under it; lstat the family entries themselves and refuse one that resolves to a directory. R5-1: two new entrances to the false bare all-clear. A TRACKED symlink reaching a git repository emits no status entry, so the walk never found the repo behind it — probe an out-of-root link target that holds one instead of only naming the scope. A nested repository's own mode-160000 gitlinks take the root's index-sweep ruling, so a dead interior gitlink is unresolved rather than silently skipped. Also from the deferred list: fix-delta --since now fingerprints the hunks file it writes, and agent-prompt --role fix-audit reads the hunks only against --hunks-fingerprint, closing the tree-writable rewrite window between the two processes. Flow bookkeeping is derived from the Step 1 plan path (the plan file and its prompt-record directory) instead of a hand-listed suffix set, and the staged half of the family re-inclusion is gated fail-closed on the snapshot's own record of what it re-included. A filter screen that cannot be read to the bottom now refuses the capture rather than blanking nothing, and the capture pins core.fileMode=true so an exec-bit edit survives a repo-local fileMode=false. * fix(review): seed the second fix-delta capture from the snapshot tree and keep the capture out of nested repositories The `--since` capture is seeded from the snapshot's own tree instead of from HEAD. Seeded from HEAD at both moments, the comparison also carried every path the tracked set gained or lost between the moments (a commit in the window) and every path an ignore rule written in the window hid from `add -A`, and a deletion classifier had to guess, per record, which of the two had made it — one entrance per round (a rule hiding an embedded repository's gitlink; a HEAD gate asked after the move). With the first tree as the second seed an entry the first capture recorded is an entry the second one holds whatever the rules say now, so a deletion in the hunks is a path gone from the disk and nothing else: the deletion classifier and its note are removed. What the seed cannot carry — a HEAD-tracked path absent at the snapshot and on disk again — is re-admitted off the snapshot's recorded HEAD. A commit in the window is disclosed as a moved HEAD and withholds the bare all-clear; so is a record that names no HEAD, or one whose HEAD the repository no longer holds. The addition-side classifier records what the rules hid by kind — files, links, nested repositories (a `sub/` listing, or a staged gitlink typed off its index mode) — and reads an addition as a rule's removal only when its kind matches what stood there; a file or a link the fix put in a hidden repository's or file's place rides the hunks. The re-admission skips a path that now sits behind a link, inside a repository the capture recorded as a gitlink, or under a component that became a plain file, instead of dying on the pathspec. `git add -A` decides whether a gitlink is modified by running a status inside the checkout, and that child executes the checkout's own repo-local filters, fsmonitor command and hooks — measured, and no `-c` on the parent reaches it. The seed's gitlinks are now excluded from `add -A` by pathspec and refreshed by hand off `rev-parse HEAD` with `update-index --index-info`, entry for entry as git rules them; both discovery statuses run `--ignore-submodules=all`; every gitlink is probed by the index sweep after its own config has been screened, with gitlinks ordered before symlinks so a repository answers under its registered name. A checkout whose `.git` git does not accept as its own — a hollow directory, a gitfile naming an enclosing checkout's git dir — stands rather than being refreshed off the HEAD discovery would find by walking up. Also: an empty gitlink checkout (a clone's, or a non-recursive `submodule update --init`'s at level 2) is skipped like an absent one at both levels, read with readdirSync rather than existsSync; a nested repository's skip-worktree bits take the sparse-checkout exemption `local-anchor.ts` already pays for; every name that reaches a probed repository is recorded against its filesystem identity and `--since` reads the baseline by identity too — only where both names resolve to the same place now, so a rename or an inode reused after a delete stays disclosed — and a route that changed is not a vanished-plus-appeared pair; the discovery-status and `add` refusals are escaped at the render boundary; and the fix auditor's input renders the finding's path through `inertPath`. * fix(review): close the round-29 fix-delta findings and the audit-found siblings The capture's gitlink refresh writes a null OID as long as the repository's object names (64 hex under sha256), and leaves an entry standing when the checkout's HEAD is of the other length. The scratch directory's parent is resolved from `rev-parse --absolute-git-dir` as bytes, with only git's one trailing newline removed; a git dir whose name cannot be spelled into `GIT_INDEX_FILE` falls back to the system temp directory when that is outside the tree, and refuses otherwise. `core.ignoreCase=false` is pinned on every spawn that measures a working tree — the capture's and the nested-repository probes' — wherever that tree's filesystem is case-sensitive: a `true` there is never git's own, and under it `add -A` and a nested `status` fold a case-colliding sibling into silence. The probe is read-only, once per path, on the tree's own volume: its `.git` entry lstat'd under `.GIT`, with ENOENT, a different inode, an unverifiable inode, or a hard-linked gitfile read as sensitive. The hidden set's `--cached` half probes the entry before typing it, so a staged gitlink whose checkout is gone is recorded nowhere. Both family listings read their recorded mode: a mode-160000 entry is never handed to `add -u` or `add -A -f` (an explicit gitlink pathspec runs a status inside the checkout, measured), and is named on stderr once; the staged half hands `add -f` only a path that lstats and names what it left out. A baseline recorded through a link out of the tree is withdrawn when the probe no longer reaches the name as scope, so a removed or replaced external link routes as vanished or appeared, and the identity alias reads the withdrawn baseline rather than the snapshot. The nested-repository probe asks about skip-worktree bits before it rules on dirt, so a hand-set bit cannot hide an edit behind pre-existing dirt; the sparse-checkout exemption is granted only where the rules govern the worktree (every unflagged tracked path in-rules, with the index's gitlinks kept out of both sets), and the ungoverned or unaskable paths are named on stderr beside `git sparse-checkout reapply`. SKILL.md routes "edits no outcome owns" one way — a foreign write leaves the ledger alone; only an unrecorded fix of a finding is a ledger to correct — with pins. Six new tests use a spawn-only nested-repo helper so they run on the Windows lane; the newline-name and exec-bit cases are gated `win32`; case-sensitivity cases skip on an insensitive volume. * docs(review): tell the fix-audit agent apart from the fix-audit round With #10136 on the branch two things answer to "fix audit": the PR re-review's narrowed round, read off the plan's `incremental.posture`, and Step 6B's one agent over the hunks a local `--fix` applied. Step 6B now says which one it is and why the two never meet in one run (the round needs a pull-request target, where `fix.effective` is false), so a reader cannot carry the round's convergence carve-out into a local `--fix` review. DESIGN.md records the same; SKILL.test.ts pins the sentence. * refactor(review): state the fix-delta scope instead of certifying it Thirty review rounds went into making `fix-delta` certify that its hunks were the whole edit: nested-repository digests, ignored-path and symlink classification, sparse/skip-worktree bits, redirected excludes, filter screening, record fingerprints. fix-delta.ts grew from 222 to 5,680 lines and held 142 of the loop's 185 Criticals, each round's fixes opening the next round's entrances — for a check whose output is a disclosure that changes no verdict. Replace the certification with a fixed scope printed on every `--since` run: the hunks hold what `git add -A` records in this repository, and an edit inside a submodule or nested repository, or to a gitignored file, is named as outside it. What stays is what the audit needs: the throwaway-index capture, the review's own side files excluded (both the name and the directory form, at any depth, plus the command's own --out/--since), the second capture seeded from the snapshot tree, and a moved-HEAD disclosure. HEAD is now read once and the capture is seeded from that sha, so the record and the tree describe one moment (R30-1). agent-prompt drops --hunks-fingerprint and the C-quoted patch-path parser; whether a fixed finding's edit is among the hunks is now the auditor's check, which has both in front of it. SKILL.md Step 6B, DESIGN.md and the user docs follow; lib/worktree.ts is back to main and lib/git.ts keeps only gitWithEnv. * fix(review): keep fix-delta working under .qwen/ ignore rules Five everyday shapes the shrink regressed, each measured in a scratch repository: - A literal exclude for --out/--since under an ignored directory made `add` refuse the whole capture ("The following paths are ignored"), so wherever `.qwen/tmp` is ignored — qwen-code's own `.qwen/*`, the `.qwen/` rule /setup-github writes, a bare `tmp/` — the audit never ran. A side file an ignore rule already hides is no longer excluded (add -A cannot capture it anyway). - The throwaway index lived under os.tmpdir(); a TMPDIR inside the working tree captured the index itself. It now lives under the absolute git dir, which also holds in a linked worktree or submodule. - `core.autocrlf` printed one conversion warning per file into the stderr the orchestrator relays, and past Node's 1 MiB default the add was killed (ENOBUFS). The add pins core.safecrlf=false (the stored blob is unchanged) and gitWithEnv takes gitRaw's 512 MiB ceiling. - In a cone-mode sparse checkout an untracked file outside the cone made `add` exit 1; the capture passes --sparse. Its snapshot cost in very large sparse checkouts is measured and stated in DESIGN.md. - The fix-audit input showed a finding's first location only, so a fix landing at its second location read as unattested. Every location is listed and the brief (and the ledger-note template) ask about any. Names on stderr now go through inertPath, the header check requires the patch to open with `diff --git`, the user docs tell this agent apart from the fix-audit-shaped review round, and every guard has a witness that goes red when it is removed (43 mutants), including runs from a subdirectory and from a linked worktree. * fix(review): make the fix-delta scope line say what the capture does R31-1: the scope line claimed coverage the capture does not deliver. It now describes the capture as it behaves: the hunks hold the files HEAD tracks and every other file no ignore rule hides; an edit to a gitignored file HEAD does not track, or to any path in the review's .qwen/tmp name families (tracked or not), is out of scope; and a hunk shows a file as git stores it (a binary file as `Binary files … differ`, a Git LFS file as its pointer). SKILL.md, DESIGN.md and the user docs relay the same wording, and tests pin each qualification against the behaviour. The command's own --out/--since files are now excluded from the diff range only, never from a capture. `add` refuses a pathspec whose literal prefix names an ignored path, which is why the previous version asked `check-ignore` first — and asked it of the user's index, so a file HEAD tracks but the user's index dropped leaked into the hunks (triage finding). `diff-tree` has no such refusal, so the probe is gone. The unreachable `(no location)` branch in renderFixAuditInput is gone; validateFindings guarantees a location. --------- Co-authored-by: Qwen Code Autofix <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: probe <probe@local> Co-authored-by: wenshao <[email protected]>
What this PR does
When a multi-round
/reviewre-review is knowably headed for the critical-only posting posture and a usable incremental anchor exists, the round now runs a narrowed fix-audit shape instead of round 1's full shape:fetch-prpredicts the compose-time floor resolution from the side filepr-contextpersisted (the round scheduleround+1 >= 6, or the latchedflatRoundsstreak — monotone arms only) or from the CLI-recorded explicit floor (recovered with the sameresolveGhHosthost formula the compose/submit boundary uses), and recordsincremental.posture: "critical"with itspostureCausein the plan. Every doubt state reads as "no posture" — the ordinary full round.isTerritoryFanOut) reads the posture, so a fix-audit round is a territory fan-out whatever its narrowed sizes say — the roster (chunk agents, no Agent 0; 1b/1c/7/test-matrix and heavy-file invariant agents stay), the round-cap tier (the 3B tier of 5, read besidesizeTierso the plan's default wall stays the one the delta's own size earns), the review: --all-chunks fans out per chunk without checking the plan's topology #9242 note andcheck-coverageall follow from the one shared predicate. Chunk briefs carry a fix-audit frame ("what did each fix change, what could that change break"); severities are unchanged — the floor governs posting, never finding.floorResolvesCritical), so a round that ran the narrow shape defers sub-Critical findings even where the auto arms cannot re-derive it at compose time (a context-unavailable compose, a side file rewritten between capture and compose). An explicit--severity-floor suggestionstill wins over a stale plan record, and the body then states the floor resolved open — and, beside a deferral list, that the deferrals carry no posture licence. The capture reads the operator's recorded floor through the same host evidence chainsubmituses (flag, else the remote under review, else the gh fallback).posture narrowing:note beside the retirement note, and a "Round shape" paragraphcompose-reviewrenders into the posted body.Why it's needed
Once a multi-round review settles into the critical-only posting posture (
floor: c), the re-review round still ran round 1's full shape: the whole territory fan-out plus up to five full-width reverse-audit waves, while everything found below Critical was deferred anyway. Measured on PR #9729 round 15 (3h13m, ~131M input tokens): the entire 18-agent finder fan-out contributed nothing postable, all three posted Criticals first surfaced in the reverse-audit waves, and the one-hop import widening re-entered 89% of the diff because every fix commit touches hub files the rest of the PR imports. Sixteen such rounds cost ~50 runner-hours without converging. The signal on these rounds lives in the fix commits and their import seams; the shape should match it.Reviewer Test Plan
How to verify
Non-UI change; everything is covered by deterministic unit and real-handler tests. From
packages/cli:npx vitest run src/commands/review # 123 files, 6546 passed npx vitest run src/commands/review/lib/posture.test.ts src/commands/review/lib/retirement.test.ts src/commands/review/lib/budget.test.ts src/commands/review/lib/roster.test.tsand from
packages/core:npx vitest run src/skills/bundled/review/SKILL.test.ts(69 passed).tsc --noEmitis clean for the review tree andeslint --max-warnings 0is clean on every touched file.What the new tests pin, end to end: the posture prediction's arms and clamps against compose's own resolution (
posture.test.ts); the fix-audit roster and tier stamping (roster.test.ts,budget.test.ts); wave narrowing vs plain retirement (retirement.test.ts); the realfetch-prhandler wiring side file → posture → header-only publication → recorded budget tier (fetch-pr.test.ts); briefs and the round's narrowing note through the real builder (agent-prompt.test.ts); and the body disclosure with both floor branches, including the context-unavailable alignment arm and the explicit-suggestionoverride (compose-review.test.ts).Twenty-five review rounds have run over this diff. Every reported finding is addressed here and every review thread is answered; all are resolved except one human design thread on the plan file's trust boundary, which is waiting on the reviewer. The last takeover closed ten carried Criticals (the seam oracle's uncovered binding shapes, the continuity stamp's round identity and its channel, the staleness arms' round-level folds and unanchored entry regex, the clamped pure-deletion hunk, and the format gate) and then reverse-audited its own delta over six further rounds, which added the fail-closed reading of an uncomparable sibling list, the base's own shed rung, the end-to-end wiring test, and the disclosures the deletion-hunk keep made inaccurate. Round 24's two Criticals are closed as well. An anonymously adopted round counter now carries its own provenance stamp, which the posture prediction refuses. And the reverse-audit narrowing no longer reads the model-edited findings list at all: it rules on launch order, so the class R20-3 filed across five rounds goes away with the parse it lived in. The branch is also merged with main's explicit-deadline change (#11686): a fix-audit round keeps the 3B round cap, while its default wall stays sized by the delta. Round 25's Critical is closed as well: a launch the scheduler cannot tie to a prompt record still counts, through the brief it names or its agent opened, or the rest of its block it delivered, as a member of the round it ran in, and both the posture narrowing and the ordinary retirement rule read it, so a lost record no longer turns a stale dry launch or a lost cold check into a clean convergence, apart from the residuals listed in DESIGN.md. A return that was not dry, written after a dry launch was built, now holds the narrowing too. Every behavioural change here has a test that goes red when the change alone is reverted.
Evidence (Before & After)
N/A (no UI change). Behavioral delta in one line: a critical-posture re-review that previously ran 13 territory chunks + Agent 0/1b/1c/7 + 5 full-width audit waves over 89% of the diff now runs chunk agents over the delta and its import-seam interaction files only, no Agent 0, and audit waves that shed provably-dry non-delta territories — with the reduction disclosed in the plan, the briefs, the round notes, and the posted body.
Tested on
Environment (optional)
Unit tests only (vitest aliases to src; no build needed).
Risk & Scope
incremental.posture,postureCause); plans without them behave exactly as before, and every reader fails toward the full round on malformed input.Linked Issues
Closes #10104. Related: #9790 (continuous agent-budget scaling), #9783 / #9919 (model-side scaffolding reduction), #9578 (fix-induced defect measurement).
中文说明
本 PR 做了什么
当多轮
/reviewre-review 可预知进入 critical-only 发布姿态且存在可用增量锚点时,该轮不再跑第 1 轮的全量形态,改跑收窄的 fix-audit 形态:fetch-pr从pr-context持久化的 side file(轮次日程round+1 >= 6,或已锁存的flatRounds信号——只取单调臂)或 CLI 记录的显式下限(用与 compose/submit 边界相同的resolveGhHosthost 公式恢复)预测 compose 期的下限决议,并在 plan 里记录incremental.posture: "critical"与postureCause。一切疑态都读作"无姿态"——普通全量轮。isTerritoryFanOut)读取姿态,fix-audit 轮无论收窄后的尺寸如何都走领地扇出——roster(chunk agents、去掉 Agent 0;1b/1c/7/test-matrix 与 heavy 文件 invariant agents 保留)、round-cap tier(3B 档的 5;它在sizeTier之外读取姿态,所以 plan 的默认 wall 仍按 delta 自身尺寸定)、review: --all-chunks fans out per chunk without checking the plan's topology #9242 提示与check-coverage全部从这一个共享谓词得出。chunk brief 带 fix-audit 框架("每个修复改了什么、改动可能破坏什么");严重度语义不变——下限只管发布,不管发现。floorResolvesCritical)的一个证据臂,因此跑了收窄形态的轮次即使 compose 期无法重推(context-unavailable、side file 在 capture 与 compose 之间被改写)也照样延后 sub-Critical 发现。本轮显式--severity-floor suggestion仍胜过陈旧的 plan 记录,此时正文如实声明下限实际为开放——并在延后清单旁说明这些延后没有姿态授权。capture 用与submit相同的 host 证据链(flag,否则被审远端,否则 gh 回退)读取操作者记录的下限。posture narrowing:note、以及compose-review渲染进发布正文的"轮次形态"段落。为什么需要
多轮评审进入 critical-only 发布姿态(
floor: c)后,re-review 轮仍然跑第 1 轮的全量形态:全量领地扇出加最多五波全宽反向审计,而所有低于 Critical 的发现最终都进 deferral。在 PR #9729 第 15 轮(3h13m、约 1.31 亿 input tokens)上的测量:整个 18-agent finder 扇出没有产出任何可发布内容,该轮发布的全部三条 Critical 均首现于反审波,import 一跳加宽把 89% 的 diff 拉回范围(每个修复 commit 都会碰被全 PR import 的枢纽文件)。这样的轮次跑了 16 轮、约 50 runner 小时仍未收敛。这类轮次的信号在修复 commit 及其 import 接缝上;形态应当与之匹配。Reviewer Test Plan
如何验证
非 UI 改动;全部行为由确定性单元测试与真实 handler 测试覆盖。在
packages/cli下:npx vitest run src/commands/review # 123 个文件,6546 例通过 npx vitest run src/commands/review/lib/posture.test.ts src/commands/review/lib/retirement.test.ts src/commands/review/lib/budget.test.ts src/commands/review/lib/roster.test.ts在
packages/core下:npx vitest run src/skills/bundled/review/SKILL.test.ts(69 例通过)。review 目录tsc --noEmit干净;触及文件eslint --max-warnings 0干净。新增测试端到端钉住:姿态预测各臂与钳制对 compose 决议的对齐(
posture.test.ts);fix-audit roster 与 tier 落章(roster.test.ts、budget.test.ts);波次收窄对比普通退役(retirement.test.ts);真实fetch-prhandler 的 side file → posture → header-only 发布 → 预算档位接线(fetch-pr.test.ts);真实 builder 的 brief 与轮次收窄 note(agent-prompt.test.ts);正文披露的两个下限分支,含 context-unavailable 对齐臂与显式suggestion优先(compose-review.test.ts)。本 diff 已历经 25 轮评审。所报发现全部在本 PR 内解决,全部评审线程均已答复;除一条关于 plan 文件信任边界、正在等待评审者回复的人工设计线程外,其余均已关闭。最近一次接管关闭了 10 条沿用的 Critical(接缝 oracle 未覆盖的绑定形状、连续性落章的轮次身份与通道、过时判定分支的轮级折叠与未锚定条目正则、被钳位的纯删除 hunk、格式门),随后对自身增量又做了 6 轮反向审计,补上了「无法比较的兄弟清单按 fail-closed 读取」「base 自己的字节脱落档位」「端到端接线测试」以及删除 hunk 保留所导致的披露口径修正。第 24 轮的两条 Critical 也已关闭。匿名采纳的轮次计数器现在带有独立的来源戳记,姿态预测据此拒绝。反审收窄不再读取模型编辑的发现清单,改按启动顺序判定;R20-3 连续五轮所报的那一类问题随这层解析一起消失。分支还合并了 main 的显式 deadline 改动(#11686):fix-audit 轮保持 3B 轮次上限,默认 wall 仍按 delta 尺寸定。第 25 轮的 Critical 也已关闭:调度器没法对上 prompt 记录的启动,仍会凭它点名或其 agent 打开过的 brief、或它投递到位的其余块内容,计为它所在轮次的成员;姿态收窄和普通退役规则都读取这一点,所以丢失一条记录不再让过时的干燥启动或丢失的冷检变成一次干净收敛,DESIGN.md 列出的遗留情形除外。写在干燥启动构建之后的非干燥返回,现在同样会拦住收窄。此处每一处行为变更都有一个「单独回退即变红」的测试。
证据(Before & After)
N/A(无 UI 改动)。行为差一句话:critical 姿态的 re-review 从"13 个领地 chunk + Agent 0/1b/1c/7 + 5 波全宽反审覆盖 89% diff"变为"chunk agents 只覆盖 delta 及其 import 接缝 interaction 文件、无 Agent 0、反审波次剔除已证干燥的非 delta 领地"——且全部收窄在 plan、brief、轮次 note 与发布正文中披露。
测试环境
环境(可选)
仅单元测试(vitest 直连 src,无需构建)。
风险与范围
incremental.posture、postureCause);不带这些字段的 plan 行为与之前完全一致,所有读取端对畸形输入一律回退到全量轮。关联 Issue
Closes #10104。相关:#9790(agent 预算随 diff 连续伸缩)、#9783 / #9919(模型侧脚手架精简)、#9578(fix-induced 缺陷测量)。