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

@ -80,6 +80,79 @@ export const UNVERIFIED_REASONS = Object.freeze([
'unknown-rule_key',
'file-existence:indeterminate',
]);
// The brief-conflict mark (`contradicts_brief`) is in neither set on purpose:
// it is a field on a KEPT finding, never a `suppressed_reason`. As a reason it
// would fail closed through `classifySuppression`, land the finding in
// `unverified` and forbid ALLOW — the opposite of what the mark is for.
// ---- The brief-conflict rule ---------------------------------------------------
//
// The one place that decides when a finding marked as contradicting the brief
// (`contradicts_brief: "<the Non-Goal or stated choice, verbatim>"`) may drop
// to MINOR. Only an engine finding (`engine` `code-review` or `security-review`)
// is ever marked; only a `/code-review` finding at the MAJOR tier ever drops,
// to MINOR, writing `brief_lowered_from: 'MAJOR'`. A BLOCKER and any
// `/security-review` finding keep their severity and only carry the mark.
// The verdict gate (verdict-gate.mjs) imports `illegalBriefLowering`; the
// opt-in workflow review mirrors it. `original_severity` is never read here.
const ENGINE_NAMES = Object.freeze(['code-review', 'security-review']);
function carries(f, field) {
return f?.[field] !== undefined && f?.[field] !== null;
}
function isMark(v) {
return typeof v === 'string' && v.trim().length > 0;
}
/**
* True when a finding, before the lowering, may drop to MINOR: a non-empty
* string mark, `engine: 'code-review'`, a known `rule_key` whose catalogue tier
* is MAJOR, and severity MAJOR. Anything else (missing engine, unknown key,
* non-string mark, a BLOCKER-tier or MINOR-tier key) → false.
* @param {object} f
* @returns {boolean}
*/
export function lowerableBriefConflict(f) {
return isMark(f?.contradicts_brief) &&
f.engine === 'code-review' &&
getRule(f.rule_key)?.severity === 'MAJOR' &&
f.severity === 'MAJOR';
}
function isLegalBriefLowering(f) {
return isMark(f?.contradicts_brief) &&
f.engine === 'code-review' &&
getRule(f.rule_key)?.severity === 'MAJOR' &&
f.severity === 'MINOR' &&
f.brief_lowered_from === 'MAJOR';
}
/**
* True when what the coordinator wrote is not the one legal shape and (a)
* carries `brief_lowered_from`, or (b) carries a mark without an engine, or
* (c) carries a mark below its catalogue tier, or (d) carries a mark with a
* missing or unknown `rule_key` (fail-closed), or (e) is an engine finding
* below its catalogue tier, marked or not. An unmarked reviewer finding (no
* `engine`) without `brief_lowered_from` is never judged by this rule.
* @param {object} f
* @returns {boolean}
*/
export function illegalBriefLowering(f) {
if (f === null || typeof f !== 'object') return false;
if (isLegalBriefLowering(f)) return false;
const marked = carries(f, 'contradicts_brief');
const engine = ENGINE_NAMES.includes(f.engine);
const rule = getRule(f.rule_key);
const belowTier = rule !== null && severityRank(f.severity) > severityRank(rule.severity);
if (carries(f, 'brief_lowered_from')) return true; // (a)
if (marked && !engine) return true; // (b)
if (marked && belowTier) return true; // (c)
if (marked && rule === null) return true; // (d)
if (engine && belowTier) return true; // (e)
return false;
}
/**
* Classify a suppression reason. Anything not declared refuting is
@ -165,6 +238,10 @@ 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. Conservative on purpose —
// a mark can only lower, so dropping it never makes a verdict greener.
let survivor = group[0];
for (const f of group.slice(1)) {
const higher = severityRank(f.severity) < severityRank(survivor.severity);
@ -202,11 +279,13 @@ export const TRIM_MARKER = ' […trimmed]';
* The tier a finding will be judged at: the catalogue tier when the rule_key is
* known (Pass 3 resets to it anyway), else the reviewer-asserted severity. The
* trim decision reads this, so a reviewer cannot get a MAJOR trimmed by
* mislabelling it MINOR.
* mislabelling it MINOR. A finding the brief rule will lower (Pass 3) is
* judged at MINOR, the tier it will be counted at.
* @param {object} f
* @returns {string}
*/
function effectiveSeverity(f) {
if (lowerableBriefConflict(f)) return 'MINOR';
return getRule(f.rule_key)?.severity ?? f.severity;
}
@ -287,6 +366,10 @@ export function judgeFilter(findings) {
* checkable claim) and is dropped. `unknown-rule_key` does not: an ad-hoc key
* is a real defect wearing the wrong label — v5.1.1 high-effort mode already
* KEEPS these, normalised to PLAN_EXECUTE_DRIFT — so it routes to `unverified`.
*
* The brief rule runs here too: a finding `lowerableBriefConflict` accepts is
* set to MINOR with `brief_lowered_from: 'MAJOR'`; every other finding gets
* the catalogue reset above.
* @param {object[]} findings
* @returns {{ kept: object[], dropped: object[], unverified: object[] }}
*/
@ -294,7 +377,17 @@ export function reasonablenessFilter(findings) {
const kept = [];
const dropped = [];
const unverified = [];
for (const f of findings) {
for (const incoming of findings) {
// The brief rule, first: only this pass writes `brief_lowered_from`, so an
// incoming one is removed; only an engine finding is ever marked, so a mark
// on any other finding is removed. Every finding written here then makes
// `illegalBriefLowering` false.
const { brief_lowered_from: _lowered, ...rest } = incoming;
let f = rest;
if (!ENGINE_NAMES.includes(f.engine) && 'contradicts_brief' in f) {
const { contradicts_brief: _mark, ...unmarked } = f;
f = unmarked;
}
if (typeof f.file !== 'string' || f.file.length === 0 ||
(typeof f.line === 'number' && f.line < 0)) {
suppress(f, 'no-citation', dropped, unverified);
@ -305,7 +398,13 @@ export function reasonablenessFilter(findings) {
continue;
}
const rule = getRule(f.rule_key);
if (rule && f.severity !== rule.severity) {
if (lowerableBriefConflict(f)) {
// The one legal lowering: a marked /code-review MAJOR on a MAJOR-tier key.
kept.push({ ...f, severity: 'MINOR', brief_lowered_from: 'MAJOR' });
} else if (rule && f.severity !== rule.severity) {
// Today's catalogue reset. A marked engine finding arrives at its tier
// (engine-findings.mjs), so it lands here only when its severity was
// changed on the way, and the reset puts it back with its mark kept.
kept.push({ ...f, severity: rule.severity, original_severity: f.severity });
} else {
kept.push(f);

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))) {

View file

@ -0,0 +1,55 @@
---
type: trekreview
review_version: "1.1"
task: "t"
slug: s
project_dir: .claude/projects/2026-10-06-s/
brief_path: .claude/projects/2026-10-06-s/brief.md
scope_sha_end: abc
reviewed_files_count: 1
verdict: ALLOW
findings:
- 8530de651325215c87cc8ba418c42ba3e8a66ed2
---
# Review: t
## Executive Summary
One reviewer-agent finding (no engine) carries a brief-conflict mark at MINOR: only an engine finding is ever marked, so the gate refuses it.
## Coverage
| File | Treatment | Reason |
|------|-----------|--------|
| lib/b.mjs | summary-only | default |
## Remediation Summary
x
```json
{
"verdict": "ALLOW",
"counts": {
"BLOCKER": 0,
"MAJOR": 0,
"MINOR": 1,
"SUGGESTION": 0
},
"allow_blocked_by": [],
"findings": [
{
"id": "8530de651325215c87cc8ba418c42ba3e8a66ed2",
"severity": "MINOR",
"rule_key": "MISSING_TEST",
"file": "lib/b.mjs",
"line": 7,
"title": "No test for the retry path",
"detail": "The retry branch has no test.",
"recommended_action": "Add a test for the retry branch.",
"contradicts_brief": "No retry layer around the HTTP client (Non-Goal 2)"
}
]
}
```

View file

@ -0,0 +1,57 @@
---
type: trekreview
review_version: "1.1"
task: "t"
slug: s
project_dir: .claude/projects/2026-10-06-s/
brief_path: .claude/projects/2026-10-06-s/brief.md
scope_sha_end: abc
reviewed_files_count: 1
verdict: ALLOW
findings:
- d3c22f4b1309d6001a0f3948e377a8e80a4b9c55
---
# Review: t
## Executive Summary
One /code-review finding contradicts a brief Non-Goal; it is kept, marked and lowered from MAJOR to MINOR (the one legal shape).
## Coverage
| File | Treatment | Reason |
|------|-----------|--------|
| lib/a.mjs | summary-only | default |
## Remediation Summary
x
```json
{
"verdict": "ALLOW",
"counts": {
"BLOCKER": 0,
"MAJOR": 0,
"MINOR": 1,
"SUGGESTION": 0
},
"allow_blocked_by": [],
"findings": [
{
"id": "d3c22f4b1309d6001a0f3948e377a8e80a4b9c55",
"severity": "MINOR",
"rule_key": "CODE_REVIEW_DEFECT",
"file": "lib/a.mjs",
"line": 12,
"title": "No retry on transient failure",
"detail": "A 503 fails the call.",
"recommended_action": "Wrap the call in a retry with backoff.",
"engine": "code-review",
"contradicts_brief": "No retry layer around the HTTP client (Non-Goal 2)",
"brief_lowered_from": "MAJOR"
}
]
}
```

View file

@ -0,0 +1,308 @@
// tests/review/brief-conflict-findings.test.mjs
// The brief-conflict rule (plan 2026-10-11-files-backticks-trial4, Step 8).
//
// A `/code-review` finding at the MAJOR tier whose recommended action
// contradicts a named brief Non-Goal (or stated choice) is kept, marked with
// `contradicts_brief: "<the Non-Goal, verbatim>"`, and drops to MINOR with
// `brief_lowered_from: 'MAJOR'`. Nothing else is ever lowered by the mark: a
// BLOCKER stays BLOCKER, a `/security-review` finding keeps its severity, a
// reviewer-agent finding (no `engine`) is never marked. The verdict gate
// refuses (exit 2) every shape outside the one legal one.
//
// The new exports are probed via `import * as m` so a missing export is an
// assertion failure, never a load-time crash. Fixtures are read inside tests.
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { spawnSync } from 'node:child_process';
import { readFileSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import * as m from '../../lib/review/coordinator-contract.mjs';
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
const GATE = join(ROOT, 'lib', 'review', 'verdict-gate.mjs');
const LEGAL_FIXTURE = join(ROOT, 'tests', 'fixtures', 'review-brief-conflict-legal.md');
const ILLEGAL_FIXTURE = join(ROOT, 'tests', 'fixtures', 'review-brief-conflict-illegal.md');
const NON_GOAL = 'No retry layer around the HTTP client (Non-Goal 2)';
function illegal(f) {
assert.equal(typeof m.illegalBriefLowering, 'function',
'coordinator-contract.mjs must export illegalBriefLowering(f)');
return m.illegalBriefLowering(f);
}
function lowerable(f) {
assert.equal(typeof m.lowerableBriefConflict, 'function',
'coordinator-contract.mjs must export lowerableBriefConflict(f)');
return m.lowerableBriefConflict(f);
}
function codeReviewDefect(extra = {}) {
return {
file: 'lib/http.mjs', line: 42, rule_key: 'CODE_REVIEW_DEFECT', severity: 'MAJOR',
title: 'No retry on transient failure', detail: 'A 503 fails the call.',
recommended_action: 'Wrap the call in a retry with backoff.', engine: 'code-review',
...extra,
};
}
function payload(reviewer, findings) {
return [{ reviewer, findings }];
}
function legalShape(extra = {}) {
return codeReviewDefect({
id: 'd3c22f4b1309d6001a0f3948e377a8e80a4b9c55',
severity: 'MINOR', contradicts_brief: NON_GOAL, brief_lowered_from: 'MAJOR', ...extra,
});
}
function assertAllOutputLegal(result) {
for (const f of result.findings) {
assert.equal(illegal(f), false,
`runContract wrote a finding illegalBriefLowering refuses: ${JSON.stringify(f)}`);
}
}
// ---- runContract (Pass 3) ----------------------------------------------------
test('runContract — a marked /code-review MAJOR drops to MINOR, keeps the Non-Goal, verdict ALLOW, not unverified', () => {
const result = m.runContract(payload('claude-code-review', [codeReviewDefect({ contradicts_brief: NON_GOAL })]));
assert.equal(result.findings.length, 1, 'the brief-conflicting finding is kept, never removed');
const f = result.findings[0];
assert.equal(f.contradicts_brief, NON_GOAL, 'the Non-Goal is named verbatim');
assert.equal(f.severity, 'MINOR');
assert.equal(f.brief_lowered_from, 'MAJOR');
assert.equal(result.verdict, 'ALLOW');
assert.equal(result.unverified.length, 0, 'the mark is not a suppression reason');
assert.deepEqual(result.allow_blocked_by, []);
assertAllOutputLegal(result);
});
test('runContract — the same /code-review MAJOR without the mark stays MAJOR and WARN', () => {
const result = m.runContract(payload('claude-code-review', [codeReviewDefect()]));
assert.equal(result.findings[0].severity, 'MAJOR');
assert.equal('brief_lowered_from' in result.findings[0], false);
assert.equal(result.verdict, 'WARN');
assertAllOutputLegal(result);
});
test('runContract — a marked /code-review BLOCKER-tier finding stays BLOCKER and BLOCK', () => {
const f = codeReviewDefect({ rule_key: 'SECURITY_INJECTION', severity: 'BLOCKER', contradicts_brief: NON_GOAL });
const result = m.runContract(payload('claude-code-review', [f]));
assert.equal(result.findings[0].severity, 'BLOCKER');
assert.equal('brief_lowered_from' in result.findings[0], false);
assert.equal(result.findings[0].contradicts_brief, NON_GOAL, 'the mark is kept on an engine finding');
assert.equal(result.verdict, 'BLOCK');
assertAllOutputLegal(result);
});
test('runContract — a marked /security-review finding keeps BLOCKER', () => {
const f = {
file: 'lib/auth.mjs', line: 9, rule_key: 'SECURITY_REVIEW_VULNERABILITY', severity: 'BLOCKER',
title: 'Token in log', detail: 'd', recommended_action: 'Remove the log line.',
engine: 'security-review', contradicts_brief: NON_GOAL,
};
const result = m.runContract(payload('claude-security-review', [f]));
assert.equal(result.findings[0].severity, 'BLOCKER');
assert.equal('brief_lowered_from' in result.findings[0], false);
assert.equal(result.verdict, 'BLOCK');
assertAllOutputLegal(result);
});
test('runContract — a marked MINOR-tier CODE_REVIEW_IMPROVEMENT stays MINOR with no brief_lowered_from', () => {
const f = codeReviewDefect({ rule_key: 'CODE_REVIEW_IMPROVEMENT', severity: 'MINOR', contradicts_brief: NON_GOAL });
const result = m.runContract(payload('claude-code-review', [f]));
assert.equal(result.findings[0].severity, 'MINOR');
assert.equal('brief_lowered_from' in result.findings[0], false);
assert.equal(result.findings[0].contradicts_brief, NON_GOAL);
assertAllOutputLegal(result);
});
test('runContract — a reviewer finding (no engine) that arrives marked loses the mark and keeps today\'s reset', () => {
const f = {
file: 'lib/b.mjs', line: 7, rule_key: 'MISSING_TEST', severity: 'BLOCKER',
title: 'No test', detail: 'd', recommended_action: 'Add a test.', contradicts_brief: NON_GOAL,
};
const result = m.runContract(payload('code-correctness-reviewer', [f]));
const out = result.findings[0];
assert.equal('contradicts_brief' in out, false, 'only an engine finding is ever marked');
assert.equal(out.severity, 'MAJOR');
assert.equal(out.original_severity, 'BLOCKER');
assert.equal('brief_lowered_from' in out, false);
assertAllOutputLegal(result);
});
test('runContract — an incoming brief_lowered_from is removed from every finding', () => {
const reviewer = {
file: 'lib/b.mjs', line: 7, rule_key: 'MISSING_TEST', severity: 'MAJOR',
title: 'No test', detail: 'd', recommended_action: 'Add a test.', brief_lowered_from: 'MAJOR',
};
const engineBlocker = codeReviewDefect({ line: 50, rule_key: 'SECURITY_INJECTION', severity: 'BLOCKER', brief_lowered_from: 'BLOCKER' });
const engineUnmarked = codeReviewDefect({ line: 60, brief_lowered_from: 'MAJOR' });
const lowerableOne = codeReviewDefect({ line: 70, contradicts_brief: NON_GOAL, brief_lowered_from: 'BLOCKER' });
const result = m.runContract([
{ reviewer: 'code-correctness-reviewer', findings: [reviewer] },
{ reviewer: 'claude-code-review', findings: [engineBlocker, engineUnmarked, lowerableOne] },
]);
const byLine = new Map(result.findings.map((f) => [f.line, f]));
assert.equal('brief_lowered_from' in byLine.get(7), false);
assert.equal('brief_lowered_from' in byLine.get(50), false);
assert.equal(byLine.get(50).severity, 'BLOCKER');
assert.equal('brief_lowered_from' in byLine.get(60), false);
assert.equal(byLine.get(60).severity, 'MAJOR');
assert.equal(byLine.get(70).severity, 'MINOR');
assert.equal(byLine.get(70).brief_lowered_from, 'MAJOR', 'only the lowering itself writes the field');
assertAllOutputLegal(result);
});
test('lowerableBriefConflict — only a marked /code-review finding at MAJOR on a MAJOR-tier key', () => {
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: NON_GOAL })), true);
assert.equal(lowerable(codeReviewDefect()), false, 'no mark');
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: '' })), false, 'empty mark');
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: true })), false, 'non-string mark');
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: NON_GOAL, engine: 'security-review' })), false);
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: NON_GOAL, engine: undefined })), false, 'missing engine');
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: NON_GOAL, rule_key: 'NOPE' })), false, 'unknown key');
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: NON_GOAL, rule_key: 'SECURITY_INJECTION', severity: 'BLOCKER' })), false);
assert.equal(lowerable(codeReviewDefect({ contradicts_brief: NON_GOAL, severity: 'BLOCKER' })), false, 'severity must be MAJOR');
});
// ---- effectiveSeverity (Pass 2 trimming) -------------------------------------
test('judgeFilter — a marked /code-review MAJOR with a long detail is trimmed, its mark intact; unmarked it is kept whole', () => {
const long = 'x'.repeat(900);
const marked = codeReviewDefect({ detail: long, contradicts_brief: NON_GOAL });
const unmarked = codeReviewDefect({ detail: long });
const a = m.judgeFilter([marked]);
assert.equal(a.trimmed.length, 1, 'the marked finding is judged MINOR, so its text may be trimmed');
assert.equal(a.trimmed[0].contradicts_brief, NON_GOAL, 'trimming never touches the mark');
assert.ok(a.trimmed[0].detail.length <= m.JUDGE_DETAIL_MAX);
const b = m.judgeFilter([unmarked]);
assert.equal(b.trimmed.length, 0, 'an unmarked MAJOR keeps every character');
assert.equal(b.kept[0].detail, long);
const result = m.runContract(payload('claude-code-review', [marked]));
assert.equal(result.trimmed_count, 1);
assert.equal(result.findings[0].contradicts_brief, NON_GOAL);
assert.equal(result.findings[0].severity, 'MINOR');
});
// ---- illegalBriefLowering ----------------------------------------------------
test('illegalBriefLowering — the legal shape is false', () => {
assert.equal(illegal(legalShape()), false);
});
test('illegalBriefLowering — a marked BLOCKER-tier engine finding below BLOCKER is true, with and without brief_lowered_from', () => {
const base = { rule_key: 'SECURITY_INJECTION', contradicts_brief: NON_GOAL };
assert.equal(illegal(codeReviewDefect({ ...base, severity: 'MINOR', brief_lowered_from: 'MAJOR' })), true);
assert.equal(illegal(codeReviewDefect({ ...base, severity: 'MAJOR', brief_lowered_from: 'BLOCKER' })), true);
assert.equal(illegal(codeReviewDefect({ ...base, severity: 'MAJOR' })), true);
assert.equal(illegal(codeReviewDefect({ ...base, severity: 'MINOR' })), true);
});
test('illegalBriefLowering — a marked /security-review finding below its tier is true', () => {
const f = {
id: 'a'.repeat(40), file: 'lib/auth.mjs', line: 9, rule_key: 'SECURITY_REVIEW_VULNERABILITY',
engine: 'security-review', contradicts_brief: NON_GOAL,
};
assert.equal(illegal({ ...f, severity: 'MAJOR' }), true);
assert.equal(illegal({ ...f, severity: 'MINOR', brief_lowered_from: 'MAJOR' }), true);
});
test('illegalBriefLowering — a marked reviewer finding (no engine) is true at any severity', () => {
const f = { id: 'b'.repeat(40), file: 'lib/b.mjs', line: 7, rule_key: 'MISSING_TEST', contradicts_brief: NON_GOAL };
assert.equal(illegal({ ...f, severity: 'MINOR' }), true, 'below its tier');
assert.equal(illegal({ ...f, severity: 'MAJOR' }), true, 'at its tier');
assert.equal(illegal({ ...f, severity: 'BLOCKER' }), true, 'above its tier');
});
test('illegalBriefLowering — brief_lowered_from without a mark is true', () => {
assert.equal(illegal(legalShape({ contradicts_brief: undefined })), true,
'brief_lowered_from MAJOR on an otherwise legal finding without a mark');
const reviewer = { id: 'b'.repeat(40), file: 'lib/b.mjs', line: 7, rule_key: 'MISSING_TEST', severity: 'MAJOR', brief_lowered_from: 'MAJOR' };
assert.equal(illegal(reviewer), true, 'brief_lowered_from on an unmarked reviewer finding');
});
test('illegalBriefLowering — a marked engine finding with an unknown or missing rule_key is true', () => {
assert.equal(illegal(codeReviewDefect({ rule_key: 'NOT_A_KEY', contradicts_brief: NON_GOAL })), true);
assert.equal(illegal(codeReviewDefect({ rule_key: undefined, contradicts_brief: NON_GOAL })), true);
});
test('illegalBriefLowering — contradicts_brief: true (not a string) on the otherwise legal shape is true', () => {
assert.equal(illegal(legalShape({ contradicts_brief: true })), true);
});
test('illegalBriefLowering — an unmarked engine finding below its tier is true', () => {
const sec = {
id: 'c'.repeat(40), file: 'lib/auth.mjs', line: 9, rule_key: 'SECURITY_REVIEW_VULNERABILITY',
severity: 'MINOR', engine: 'security-review',
};
assert.equal(illegal(sec), true, 'unmarked /security-review SECURITY_REVIEW_VULNERABILITY at MINOR');
assert.equal(illegal(codeReviewDefect({ severity: 'MINOR' })), true, 'unmarked /code-review CODE_REVIEW_DEFECT at MINOR');
});
test('illegalBriefLowering — false for engine findings at their tier and for unmarked reviewer findings', () => {
assert.equal(illegal(codeReviewDefect({ contradicts_brief: NON_GOAL })), false, 'a marked engine finding at its tier');
assert.equal(illegal(codeReviewDefect()), false, 'an unmarked engine finding at its tier');
const reviewer = { id: 'b'.repeat(40), file: 'lib/b.mjs', line: 7, rule_key: 'MISSING_TEST' };
assert.equal(illegal({ ...reviewer, severity: 'MINOR' }), false,
'an unmarked reviewer MISSING_TEST at MINOR without brief_lowered_from (the quick-mode shape)');
assert.equal(illegal({ ...reviewer, severity: 'BLOCKER' }), false,
'an unmarked reviewer MISSING_TEST asserted BLOCKER (above its tier)');
});
// ---- review → plan handover --------------------------------------------------
test('review → plan handover — the lowered finding is left out by /trekplan\'s severity filter', () => {
const prose = readFileSync(join(ROOT, 'commands', 'trekplan.md'), 'utf-8');
const line = prose.split('\n').find((l) => l.includes('Filter findings to severity'));
assert.ok(line, 'commands/trekplan.md carries the "Filter findings to severity" line');
const set = line.match(/\{([^}]*)\}/);
assert.ok(set, 'the filter line names a {…} severity set');
const severities = new Set(set[1].split(',').map((s) => s.trim()));
assert.deepEqual([...severities].sort(), ['BLOCKER', 'MAJOR']);
const lowered = m.runContract(payload('claude-code-review', [codeReviewDefect({ contradicts_brief: NON_GOAL })]));
assert.equal(lowered.findings.filter((f) => severities.has(f.severity)).length, 0,
'a brief-lowered finding does not become a plan goal');
const unmarked = m.runContract(payload('claude-code-review', [codeReviewDefect()]));
assert.equal(unmarked.findings.filter((f) => severities.has(f.severity)).length, 1,
'known-positive control: the same finding unmarked does');
});
// ---- the verdict gate --------------------------------------------------------
function runGate(path) {
return spawnSync(process.execPath, [GATE, path], { encoding: 'utf-8' });
}
test('verdict gate — exit 0 on the legal brief lowering', () => {
assert.ok(existsSync(LEGAL_FIXTURE), 'tests/fixtures/review-brief-conflict-legal.md exists');
const r = runGate(LEGAL_FIXTURE);
assert.equal(r.status, 0, `legal lowering must pass: ${r.stdout}${r.stderr}`);
});
test('verdict gate — exit 2 on the illegal brief lowering, the reason naming the finding id', () => {
assert.ok(existsSync(ILLEGAL_FIXTURE), 'tests/fixtures/review-brief-conflict-illegal.md exists');
const text = readFileSync(ILLEGAL_FIXTURE, 'utf-8');
const json = JSON.parse(text.slice(text.lastIndexOf('```json') + 7, text.lastIndexOf('```')));
assert.equal(json.verdict, 'ALLOW', 'the fixture claims ALLOW');
const id = json.findings[0].id;
assert.match(id, /^[0-9a-f]{40}$/);
const r = runGate(ILLEGAL_FIXTURE);
assert.equal(r.status, 2, `a marked reviewer finding must be refused: ${r.stdout}${r.stderr}`);
assert.ok(r.stdout.includes(id), `the reason names the finding id ${id}: ${r.stdout}`);
});
test('verdict gate — imports illegalBriefLowering from coordinator-contract.mjs (the rule lives once)', () => {
const src = readFileSync(GATE, 'utf-8');
assert.match(src,
/^import\s*\{[^}]*\billegalBriefLowering\b[^}]*\}\s*from\s*['"]\.\/coordinator-contract\.mjs['"]/m,
'verdict-gate.mjs must import illegalBriefLowering from ./coordinator-contract.mjs');
assert.doesNotMatch(src, /function\s+illegalBriefLowering\b/,
'verdict-gate.mjs must not carry its own copy of the rule');
});