fix(review): a code-review finding against a brief Non-Goal may drop to MINOR, never a BLOCKER

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-12 18:04:36 +02:00
commit ce7700b71b
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
5 changed files with 533 additions and 4 deletions

View file

@ -8,7 +8,9 @@
// 0 = ALLOW or WARN
// 1 = BLOCK
// 2 = could not decide: missing/unreadable file, not a review, no verdict,
// an unknown verdict, a file that contradicts its own verdict, or a
// an unknown verdict, a file that contradicts its own verdict, a
// finding lowered or marked against the brief outside the one legal
// shape (coordinator-contract `illegalBriefLowering`), or a
// WARN because a reviewer or engine did not run (`missing-reviewer:` /
// `unattributable-payload` in allow_blocked_by — operator decision
// 2026-10-06: a review not performed in full stops a merge).
@ -29,6 +31,7 @@
import { readFileSync, existsSync } from 'node:fs';
import { validateReviewContent, VERDICT_VALUES } from '../validators/review-validator.mjs';
import { fencedBlocks, isJsonBlock } from './findings-schema.mjs';
import { illegalBriefLowering } from './coordinator-contract.mjs';
import { isMain } from '../util/is-main.mjs';
export const EXIT = Object.freeze({ PASS: 0, BLOCK: 1, UNDECIDED: 2 });
@ -100,6 +103,13 @@ export function decideVerdict(text) {
if (blockerFindings > 0 || blockerCount > 0 || blockerSection > 0) {
return undecided(`verdict ${verdict} over BLOCKER finding(s) (json: ${blockerFindings}, counts: ${blockerCount}, ## Findings (BLOCKER): ${blockerSection})`, verdict);
}
// The brief-conflict rule (coordinator-contract `illegalBriefLowering`, the
// one place it lives): a finding lowered or marked outside the one legal
// shape means the verdict may be greener than the findings — never green.
const illegal = payload.findings.filter((f) => illegalBriefLowering(f)).map((f) => f.id);
if (illegal.length > 0) {
return undecided(`verdict ${verdict} over a brief-conflict lowering outside the one legal shape (finding ${illegal.join(', ')})`, verdict);
}
const fmIds = new Set(Array.isArray(fm.findings) ? fm.findings : []);
const jsonIds = new Set(payload.findings.map((f) => f.id));
if (fmIds.size !== jsonIds.size || [...fmIds].some((id) => !jsonIds.has(id))) {