fix(review): Pass 1 ranks by catalogue tier, and a duplicate never lowers its group

Review finding bc7f4e23 (library side).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-13 14:24:33 +02:00
commit f04fecea7f
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
4 changed files with 134 additions and 45 deletions

View file

@ -88,13 +88,21 @@ re-reading, and fresh finding generation are all forbidden.
Two findings collide when their `(file, line, rule_key)` triplets are
identical. When findings collide:
- Keep the finding with the highest catalogue severity (BLOCKER >
MAJOR > MINOR > SUGGESTION).
- If the severity ties, prefer a reviewer-agent finding over an engine
finding (one from `claude-code-review` / `claude-security-review`), so an
engine's brief-conflict mark never lowers what a reviewer agent asserted;
among reviewer-agent findings, prefer the finding from
`brief-conformance-reviewer` (its findings are anchored to the brief).
- Rank the colliding findings and keep the first:
1. the higher catalogue tier: the severity `lib/review/rule-catalogue.mjs`
gives the `rule_key`, or the finding's own severity when the key is
not in the catalogue (BLOCKER > MAJOR > MINOR > SUGGESTION);
2. if the catalogue tier ties (for a catalogue key it always does,
since the triplet shares the `rule_key`), prefer a reviewer-agent
finding over an engine finding (one from `claude-code-review` /
`claude-security-review`);
3. among findings of one kind, the higher asserted severity;
4. among reviewer-agent findings, prefer the finding from
`brief-conformance-reviewer` (its findings are anchored to the brief);
5. then the first seen.
- Write the kept finding with the highest severity any finding in its
group asserted, so dropping a duplicate never lowers the group and an
engine's brief-conflict mark never lowers what a reviewer agent asserted.
- Concatenate the kept finding's `detail` with a one-line note: "Also
flagged by {other reviewer}: {their title}." This preserves
attribution without duplicating the row.
@ -408,8 +416,9 @@ for the ID list.
- **Severity floor is the catalogue.** It holds for every finding
except a lowerable brief conflict, which is MINOR (Pass 3, `brief_lowered_from: MAJOR`).
Pass 3 corrects mismatches by
resetting to the catalogue tier — never by dropping. Pass 1's severity
tiebreak uses the catalogue tier, not the reviewer's emitted value.
resetting to the catalogue tier — never by dropping. Pass 1 ranks by
the catalogue tier first, in the order Pass 1 gives, and the kept finding
carries the highest severity its group asserted.
- **Block-style YAML for findings list.** The frontmatter parser
(`lib/util/frontmatter.mjs`) does not support flow-style arrays.
- **IDs are computed by code, not by you.** You have no shell, so you