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:
parent
d492abb5e9
commit
f04fecea7f
4 changed files with 134 additions and 45 deletions
|
|
@ -16,8 +16,10 @@
|
|||
// placeholders and lib/review/fill-finding-ids.mjs computes them (the
|
||||
// old doc named a 4-tuple with `title` the code never computed).
|
||||
//
|
||||
// What IS implemented, purely: Pass 1 (triplet dedup → highest-severity-wins
|
||||
// survivor + conformance tiebreak + detail concat + raised_by provenance),
|
||||
// What IS implemented, purely: Pass 1 (triplet dedup → survivor ranked by
|
||||
// catalogue tier, then reviewer over engine, asserted severity, conformance,
|
||||
// first seen; the survivor carries its group's highest asserted severity
|
||||
// (review bc7f4e23) + detail concat + raised_by provenance),
|
||||
// Pass 2 succinctness (trims MINOR/SUGGESTION text, never removes) +
|
||||
// actionability-presence, Pass 3 reasonableness
|
||||
// (citation / unknown-rule_key suppression, severity-mismatch correction),
|
||||
|
|
@ -238,11 +240,16 @@ export function ingest(reviewerPayloads) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Pass 1 — dedup by (file, line, rule_key) triplet. Survivor = highest
|
||||
* catalogue severity; severity tie → prefer a reviewer-agent finding over an
|
||||
* engine finding, then the conformance reviewer; carries
|
||||
* raised_by provenance, concatenates other reviewers' attribution into detail,
|
||||
* and recomputes the id over the triplet.
|
||||
* Pass 1 — dedup by (file, line, rule_key) triplet. The survivor is ranked by
|
||||
* (1) the catalogue tier (`getRule(rule_key)?.severity`, else the finding's own
|
||||
* severity; lower `severityRank` wins), (2) a reviewer-agent finding over an
|
||||
* engine finding, (3) among findings of one kind the higher asserted severity,
|
||||
* (4) the conformance reviewer, (5) first seen. It is written with the highest
|
||||
* severity any finding in its group asserted, so dropping a duplicate never
|
||||
* lowers the group (review bc7f4e23; plan 2026-10-11-files-backticks-trial4,
|
||||
* ADR "Pass 1 survivor order and group severity"). Carries raised_by
|
||||
* provenance, concatenates other reviewers' attribution into detail, and
|
||||
* recomputes the id over the triplet.
|
||||
* @param {object[]} findings
|
||||
* @returns {object[]}
|
||||
*/
|
||||
|
|
@ -256,21 +263,43 @@ export function dedupByTriplet(findings) {
|
|||
const out = [];
|
||||
for (const group of groups.values()) {
|
||||
// A brief-conflict mark is not carried over from a group member to the
|
||||
// survivor: when the higher-severity survivor is unmarked, the group stays
|
||||
// unmarked and is judged at its full severity. On a severity tie a
|
||||
// reviewer-agent finding survives over an engine finding (decision 13,
|
||||
// operator 12.10), so an engine's mark never lowers what a reviewer agent
|
||||
// asserted, whatever the payload order; among findings of one kind the
|
||||
// conformance reviewer wins the tie, else the first-seen one stays.
|
||||
// survivor: when the survivor is unmarked, the group stays unmarked and is
|
||||
// judged at its full severity. The survivor is ranked by catalogue tier
|
||||
// first; for a catalogue key the tier always ties inside one triplet (the
|
||||
// rule_key is shared), so a reviewer-agent finding survives over an engine
|
||||
// finding (decision 13, operator 12.10; review bc7f4e23), then the higher
|
||||
// asserted severity among findings of one kind, then the conformance
|
||||
// reviewer, else the first-seen one stays. The survivor is written with the
|
||||
// highest severity any finding in its group asserted, so an engine's mark
|
||||
// never lowers what a reviewer agent asserted, whatever the payload order
|
||||
// (plan 2026-10-11-files-backticks-trial4, ADR "Pass 1 survivor order and
|
||||
// group severity").
|
||||
const tier = (f) => severityRank(getRule(f.rule_key)?.severity ?? f.severity);
|
||||
let survivor = group[0];
|
||||
for (const f of group.slice(1)) {
|
||||
const tf = tier(f);
|
||||
const ts = tier(survivor);
|
||||
if (tf !== ts) {
|
||||
if (tf < ts) survivor = f;
|
||||
continue;
|
||||
}
|
||||
const ef = isEngineFinding(f);
|
||||
const es = isEngineFinding(survivor);
|
||||
if (ef !== es) {
|
||||
if (!ef) survivor = f;
|
||||
continue;
|
||||
}
|
||||
const rf = severityRank(f.severity);
|
||||
const rs = severityRank(survivor.severity);
|
||||
const tieToReviewer = rf === rs && !isEngineFinding(f) && isEngineFinding(survivor);
|
||||
const tieToConformance =
|
||||
rf === rs && isEngineFinding(f) === isEngineFinding(survivor) &&
|
||||
isConformance(f.reviewer) && !isConformance(survivor.reviewer);
|
||||
if (rf < rs || tieToReviewer || tieToConformance) survivor = f;
|
||||
if (rf !== rs) {
|
||||
if (rf < rs) survivor = f;
|
||||
continue;
|
||||
}
|
||||
if (isConformance(f.reviewer) && !isConformance(survivor.reviewer)) survivor = f;
|
||||
}
|
||||
let severity = survivor.severity;
|
||||
for (const f of group) {
|
||||
if (severityRank(f.severity) < severityRank(severity)) severity = f.severity;
|
||||
}
|
||||
const raised_by = [...new Set(group.map((f) => f.reviewer).filter(Boolean))];
|
||||
const others = group.filter((f) => f !== survivor);
|
||||
|
|
@ -282,7 +311,10 @@ export function dedupByTriplet(findings) {
|
|||
}
|
||||
}
|
||||
const id = computeFindingId(survivor.file, survivor.line, survivor.rule_key);
|
||||
out.push({ ...survivor, id, ...(detail !== undefined ? { detail } : {}), raised_by });
|
||||
out.push({
|
||||
...survivor, ...(severity !== undefined ? { severity } : {}), id,
|
||||
...(detail !== undefined ? { detail } : {}), raised_by,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue