mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-26 17:13:32 +00:00
* feat(review): add the persistently-critical convergence advisory
The severity floor converges a healthy loop — Suggestions stop posting and
the volume falls to the Criticals, then to zero as those get fixed. But a
loop whose Criticals never clear — the security-sensitive PR under
adversarial review — posts Criticals every round forever: the floor engages,
the Suggestions stop, and the volume flatlines at the Critical count instead
of falling. Nothing before this said so.
This adds the shape detector and its ONE recommendation:
- lib/convergence.ts — `convergenceAssessment` computes one fact from the
carried telemetry (Criticals stood in the previous round's work-list AND
stand again this round, with the two-round posting window present and not
shrinking) and, when it fires, returns the `land-with-residual-risk`
recommendation. Pure data, never authority: no threshold, no blocking, no
merge/close — every input degrades OPEN, so absence is fail-safe, never a
suppressed finding.
- compose-review wires it: `prevLedgerFacts` now recovers the previous
work-list's Critical presence beside the round and volume; the assessment
surfaces on three surfaces — a structured `convergence` field on the
composed JSON, a rank-1 non-capping body disclosure, and a terminal
CONVERGENCE line — each advisory-only and self-disclaiming, with a blank
residual-risk inventory scaffold (attack surface · attacker-dependency ·
blast radius) for the maintainer's risk-acceptance decision.
The exit the floor cannot provide: when the loop is provably stuck on
Criticals, the tool names the maintainer's decision (merge, carrying the
residual risk) instead of opening another round. Advisory only — it never
blocks this review.
Closes the convergence-exit gap in #9278; evidence and design in #9410.
* fix(review): surface the convergence advisory on every reachable event, gated on floor engagement (#9526)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(review): give the trimmed convergence advisory its own disclosure rank (#9526)
The advisory shared trim rank 1 with the deferral display, but every
rank-1 disclosure surface names "the deferred-findings list" — a fired
zero-deferral round whose body overflowed posted a trim notice asserting
a deferral list that never existed while the dropped advisory went
unnamed. The advisory now holds its own rank (and RANK_NAMES entry),
yielding after the deferral display and before the not-reviewed
disclosures. Adds the overflow fixture that pins the yield and the
relocated-arm firing fixture that pins the third thisCriticals term,
and corrects the prevLedgerFacts threat docstring: under `auto` the
floor-engagement conjunct is forgeable via the carried round, so the
only unforgeable conjunct is this round's own standing Critical.
* fix(review): count the script-lint gate's Criticals in the convergence signal (#9526)
The persistently-critical signal read `thisCriticals` before the gate
pushed its Criticals into `bodyCriticals`, and the ledger work-list
feeding the next round's persistence half omitted them too. A loop
whose standing blocker is the deterministic [lint] gate — the exact
shape the signal exists to name — held the whole conjunction
semantically while the advisory stayed silent: the count was taken
before the array was complete, and the gate-only round recorded no
sev 'C' for its successor to recover.
The assessment now runs after the relocated and gate pushes and reads
the completed array with the same semantics as the verdict's own `c`
(the explicit relocated term drops with the push that already carries
it), and the gate's Criticals join the marker work-list. Adds the
handler fixture arming the gate end to end — advisory fires, marker
records sev 'C' — and pins both branches of the trim notice's
copy-location conditional, which had no oracle on either side.
* fix(review): close the round-5 oracle gaps on the residual-risk advisory (#9526)
Round 5 reviewed the merge that landed #9461 underneath this branch and
found four suppress paths the merge introduced with no end-to-end oracle,
plus one standing comment overclaim. Each finding was reproduced as a
surviving mutant before the fix and re-run after, so every test added here
is one that actually kills something.
R5-2 — `residualRisk` is carried into the durable artifact instead of being
omitted from it. The omission's stated reason ("the advisory rides the
persisted body") is false on exactly the rounds that need the record: rank 2
sheds before the not-reviewed disclosures, so a fired-but-trimmed round left
a maintainer reading `.qwen/reviews` a "did not fit" breadcrumb and none of
the facts behind the `land-with-residual-risk` call. Its sibling
`convergence` is allow-listed one paragraph up for that precise reason, and
the merge had put the two on opposite rules. Shape-checked like every other
field on this boundary rather than passed through.
R5-1 — the persistence conjunct had no silence fixture. Every firing
fixture carries sev `C` in the prev ledger, so replacing the derivation with
a bare `true` shipped the suite green while a round introducing its FIRST
Critical would fire `land-with-residual-risk`. Added a fixture whose
predecessor holds Suggestions only, all other conjuncts true.
R5-3 — the enforcement-vs-reporting floor reading had no oracle for the one
input where the two disagree: a genuinely ABSENT `severityFloor` at round
>= 6, which the reporting reading folds to `auto`. Every advisory fixture
passed `severityFloor: 'auto'` explicitly, so the swap shipped green and
would publish "The severity floor will not converge it" over a round whose
enforcement backstop moved nothing. Added a fixture with no `severityFloor`
key at all.
R5-4 — the two silence fixtures asserted only absences. `prevLedgerFacts`
swallows every recovery failure into round 0, so a predecessor that never
loaded produced the same silence and the arms they claim to pin were
vacuous. Both now assert the VOLUME line quoting the predecessor's volume
as a positive recovery sentinel.
R4-1 — the marker path's second `scriptLintGate` run is left in place: it
lives in a different function from the body composer's, threading the value
across would add a seventh positional parameter for plumbing, and the two
agree because the gate is pure in `planPath` over inputs immutable within
one synchronous compose. What was wrong was the comment claiming it was
"the same gate the body ran"; it now states the actual invariant and the
actual hazard — an edit that filters what the BODY pushes must change this
list too.
packages/cli: typecheck, ESLint and Prettier clean; src/commands/review
4301 pass / 1 skipped. Mutation matrix (compose-review + save-artifact,
475 tests): baseline green; `prevHadCritical: true`, the reporting-reading
swap, dropping `residualRisk` from the persisted verdict, and a vacuous
ledger recovery each turn the suite red.
* fix(review): act on the round-6 deferred list for the residual-risk advisory (#9526)
Round 6 posted no findings and deferred ten observations under the
convergence posture. Eight are addressed here; each was reproduced as a
surviving mutant first and re-run after, and the two that are not addressed
are recorded below with the reason rather than left silent.
Correctness:
- The volume window straddled a posture change. The round the floor engages
on compares a Critical-only volume against a predecessor that was still
posting Suggestions — a drop that is the posture, not the loop — and on a
flat pair the advisory could publish "the severity floor will not converge
it" after one round of the floor. `ConvergenceFacts` now carries
`prevFloor` and a recorded `o` predecessor suppresses. Read the way the
sibling diagnosis in the same module reads it: a floor that was never
recorded is not a floor that DIFFERS, so pre-field markers evaluate
exactly as before. Pinned in both directions — deleting the guard and
tightening it to reject unrecorded floors each turn the suite red.
- `noteTrimmedRanks`' tail clause keyed on the advisory instead of on the
disclosures. Over a combined rank-2-and-3 drop it read "another copy — the
advisory also rides the composed JSON", telling the operator the trimmed
set was backed up when the half that is not backed up was exactly the half
the sentence exists to rescue; over a rank-0 drop it read "their only
other copy" for a paragraph the composed result does carry. It now keys on
rank 3. The artifact stays unnamed here — naming it sent the operator to a
deferral list that does not exist, which the existing test caught.
- The terminal `RESIDUAL-RISK:` record spread one labelled line over seven,
six of them unlabelled, because the advisory carries a markdown table for
the body. Collapsed at the print site only: the pipes survive, so the
inventory's three columns still reach the operator on the round where the
body budget shed the formatted copy.
Accuracy of the record:
- The `PersistedVerdict` comment claimed `residualRisk` sheds "before
anything else". It is rank 2; `convergence` is rank 0. What they share is
that both CAN go.
- The bundled skill enumerated two of the four trim ranks and stated the
no-durable-copy rule without its exception. Both assertions in
`SKILL.test.ts` move with the prose.
New oracles (test-only):
- The advisory-only guarantee — the claim the whole feature rests on — was
unpinned: a fired round now asserts the event stays where the findings put
it and that `cappedBy` gains nothing.
- The floor-futility sentence was pinned only negatively; it now has a
positive assertion in both languages.
- The zh advisory's scaffold columns and its Critical-count interpolation
slot had no oracle. `FIRE` is deliberately asymmetric (2 Criticals, volume
3/3) so a template reading the wrong slot shows.
- The rank-ordering guard could not tell rank 2 from rank 3; the combined-
drop test closes it — the `trim: 3 -> 2` mutant now fails five tests.
Not done, deliberately:
- The validator does not re-assert `criticals >= 1` / `posted >= prevPosted`.
Those are `convergenceAssessment`'s construction invariants, and a second
statement of them at the save boundary is a rule free to drift from the
first — with the artifact, the durable record, as what gets thrown away
when it does. Identity is pinned instead (`shape`, `recommendation`) and
the counts are shape-checked.
- The marker path's second `scriptLintGate` run stands (R4-1); the reasoning
is on that thread and at the call site.
packages/cli: 4305 pass / 1 skipped. packages/core skills: 376 pass.
Typecheck, ESLint and Prettier clean on both workspaces.
* fix(review): measure the residual-risk window on fresh findings, not totals (#9526)
Round 7 posted one Critical and it is correct. The volume conjunct compared
posting TOTALS, and Step 6 re-posts every still-standing ledger Critical
under its original id — so the total only ever rises and a converging loop
reads as a stuck one. Reproduced through the real `composeReview` before
touching anything: round 6 posts 5 first-time Criticals; the author fixes 3;
round 7 re-posts the 2 that stand and drafts 4 new. Fresh 5 -> 4 is a loop
settling, the total went 5 -> 6, and the advisory fired
`land-with-residual-risk` over it.
The window now runs on the fresh pair the marker already carries —
`postedFresh` and `prev.fresh`, the same numbers the loop-settling
observation in the same module trends on, so the two features cannot
disagree about what a round produced. `prev.fresh` absent degrades open.
Applying only that change would have introduced a second false fire, so it
does not ship alone. The posting total was silently covering a case the
fresh window is blind to: a reviewer finding nothing new for two rounds
while the author clears blockers sits at fresh 0 against fresh 0, which
"not falling" reads as stuck. Probed on the pre-change code — backlog 5 -> 3
with zero fresh both rounds is silent today (3 < 5) and would have fired
under a fresh-only window. The assessment therefore also takes the standing
Critical count and vetoes on observed shrinkage. A veto rather than a
requirement, on positive evidence only: the work-list it counts is the one
the marker's byte budget may have shortened, and an undercount can only hide
shrinkage, never manufacture it — so an unknown predecessor abstains instead
of silencing a genuinely stuck loop.
Unlike the sibling diagnosis, this signal does NOT require `prev.fresh > 0`.
That module is about a loop generating work; this one is about work that
never clears, and Criticals standing round after round with nothing new is
the shape itself, not a quiet loop. The backlog veto is what separates it
from a backlog being worked down.
The reported numbers are renamed with what they now measure — `posted` /
`prevPosted` become `fresh` / `prevFresh` on `ConvergenceFacts`,
`ConvergenceAssessment` and the persisted artifact — and the advisory prose
follows in both languages. Feeding fresh counts into fields printed as "the
posting volume" would have swapped one false record for another.
Verified as five shapes through the real command, then pinned as tests: the
reported fresh-shrinking loop is silent; the clearing backlog is silent; a
pre-fresh marker is silent; and both firing shapes still fire — the same
Criticals re-posted at zero fresh, and new Criticals every round.
Mutation matrix (539 tests): reverting the window to totals, deleting the
backlog veto, and tightening the veto to suppress on an unknown predecessor
each turn the suite red.
packages/cli: 4310 pass / 1 skipped. Typecheck, ESLint and Prettier clean.
* fix(review): prove the predecessor's floor enforced, don't trust its stamp (#9526)
R8-1 is correct. The posture-change guard paired two different readings
across the window's ends: this round's engagement is the strict
`criticalFloorInEffect`, but the predecessor's `floor` stamp is written from
`criticalFloorKind`, the reporting fold — which folds an absent
`severityFloor` into `auto` and stamps `c` on any round >= 6 the enforcement
backstop never touched. Reproduced through the real code first:
criticalFloorKind(undefined, false, 6) = 'auto-resolved' -> stamps 'c'
criticalFloorInEffect(undefined, false, 6) = false -> Suggestions post
so a predecessor that still posted Suggestions passed the guard, and the
advisory published "the severity floor will not converge it" one round after
enforcement actually started.
Neither fix direction the finding names is taken. Restamping the marker from
the enforcement reading would leave the sibling diagnosis comparing this
round's reporting stamp against a predecessor's enforcement stamp — the same
cross-reading defect moved into #9623's feature — and #9623 chose the
reporting reading deliberately, because its advice quotes the floor back to
the author. Special-casing a "newly named" floor needs the predecessor's raw
`severityFloor`, which no marker carries.
The evidence is already in the work-list instead. Enforcement moves drafted
Suggestions out of the posting set before the marker is built, so an engaged
round's list is Critical-only and an un-enforced one is not — measured
through the real composer across all four postures:
floor=critical (engaged) work list ["C"] stamp c
floor=auto, round 7 (engaged) work list ["C"] stamp c
floor ABSENT, round 7 (folded c) work list ["C","S"] stamp c <- the hole
floor=suggestion (not engaged) work list ["C","S"] stamp o
`prevPostedSuggestion` is that fact, and it suppresses on the POSITIVE
observation so the two ways it can be wrong land on opposite sides: a
shortened list that shed its Suggestion reads as engaged (the truncation
caveat the backlog veto already carries), while a pathless Suggestion an
engaged round left inline reads as un-enforced and costs one round of
silence. Unknown abstains, like every other fact read off that list.
Mutation matrix: deleting the guard, tightening it so an unknown predecessor
suppresses, and pointing the wiring at the wrong severity each turn the suite
red — the first on both the unit arm and the end-to-end fixture built from
the finding's own witness.
packages/cli: 4432 pass / 1 skipped. Typecheck, ESLint and Prettier clean.
* fix(review): refuse a pure-foreign work-list as this account's history (#9526)
Correct, and reproduced through the real composer before changing anything.
Recovery adopts the highest-round marker whoever posted it. Where that marker
was NOT merged over this account's own findings, this account's entries are
in no work list at all — the state `openCriticals` already refuses to infer
across, one screen up in the same function. Every prev-round fact this signal
reads comes off that list, and it read it unconditionally:
pure-foreign {foreign:true, merged:false} -> FIRES
own list {foreign:false} -> FIRES
merged {foreign:true, merged:true} -> FIRES
An own round-6 marker that was a clean LGTM (empty findings, fresh 0, floor
stamped `c`), a foreign same-round marker carrying Criticals and no
Suggestions winning recovery, and one Critical drafted this round were enough
to publish "Criticals stood in the previous round's work-list and stand again
this round — land-with-residual-risk" over this account's own LGTM.
All three list-derived facts are withheld on that state, not just
`prevHadCritical`: it alone silences the assessment today, but leaving the
other two reading a stranger's list is a hole waiting for the next edit to
re-open. `prevPostedSuggestion` in particular reads ABSENCE, and a stranger's
Critical-only list is exactly the shape that reads as "the floor enforced".
Merged foreign lists are deliberately NOT withheld: the union keeps this
account's own certified entries under their own ids, which is the part that
makes the list speak for this account again — the same distinction
`openCriticals` draws.
The test drives all three arms and asserts them as one table, so the fix is
pinned in both directions: a mutant disabling the gate fires on the stranger,
and a mutant widening it to any `foreign` marker silences the merged arm.
Both turn the suite red, as does un-gating `prevHadCritical` alone.
Not changed, and recorded rather than left implicit: a TRUNCATED work-list
still reads as this account's. Truncation shortens our own list, which is a
different thing from a stranger's, and the direction it errs in is already
documented on `prevPostedSuggestion` and the backlog veto. Requiring
completeness would silence the advisory on precisely the deep-work-list
rounds it exists for.
packages/cli: 4433 pass / 1 skipped. Typecheck, ESLint and Prettier clean.
* fix(review): stop a gate Critical compounding, and qualify a truncated reading (#9526)
Round 11's two Criticals. Both reproduced through the real composer before
anything was changed.
R11-2 — a standing gate Critical entered the posting set twice, and the pair
compounded. This is a regression from this branch's own commit
|
||
|---|---|---|
| .. | ||
| src | ||
| .gitignore | ||
| index.ts | ||
| package.json | ||
| test-setup.ts | ||
| tsconfig.json | ||
| vitest.config.ts | ||