refactor(simplify): one plan read in red proof, one list marker, one put-back rule [skip-docs]

/simplify over ed6699d..HEAD (closing review of the remediation):
- red-proof reads and parses the step once (planStep) for the test list and
  tests_only; normTest goes, plan-schema exports normPlanPath, also used by
  plan-scope-sweep.
- plan-schema builds LIST_ITEM and the 1.9 File: line from one LIST_MARKER;
  the below-1.9 File: regex ends in a lookahead instead of slicing back one.
- the workflow script hoists tripletOf and puts back through dedupByTriplet.
- coordinator-contract keeps the rank in its JSDoc only; engine-findings
  drops a redundant String().
- tests: reuse knownIssues, trekplan19Block, step1Fields, one red-proof cli
  helper with childEnv, one payload-pair builder; drop a test the decision-20
  definition test covers.

No behaviour change. Suite 2462 (2460/0/2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-13 19:47:08 +02:00
commit bcf3adbba7
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
11 changed files with 90 additions and 127 deletions

View file

@ -52,6 +52,8 @@ const RULE_KEYS = Object.keys(RULE_TIERS);
const sevRank = (s) => (SEVERITIES.includes(s) ? SEVERITIES.indexOf(s) : SEVERITIES.length);
// The catalogue tier of a rule_key, or null when the key is missing or unknown.
const tierOf = (key) => (typeof key === 'string' && Object.prototype.hasOwnProperty.call(RULE_TIERS, key) ? RULE_TIERS[key] : null);
// The (file,line,rule_key) triplet a finding is deduped and matched on.
const tripletOf = (f) => `${f.file}\u0000${f.line}\u0000${f.rule_key}`;
// Findings schema — mirrors lib/review/findings-schema.mjs (NW1). The enum on
// rule_key enforces the catalogue at the StructuredOutput layer (stronger than
@ -125,7 +127,7 @@ const VERDICT_SCHEMA = {
function dedupByTriplet(findings) {
const groups = new Map();
for (const f of findings) {
const key = `${f.file}\u0000${f.line}\u0000${f.rule_key}`;
const key = tripletOf(f);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(f);
}
@ -278,7 +280,6 @@ const returned = Array.isArray(coord?.findings) ? coord.findings : merged;
// move or relabel of an engine finding no longer lowers the verdict.
// The raises and put-backs are written into `returned`, the marks kept, so the
// rendered review.md carries them.
const tripletOf = (f) => `${f.file}\u0000${f.line}\u0000${f.rule_key}`;
const counterparts = new Map(merged
.filter((m) => m && typeof m === 'object')
.map((m) => [tripletOf(m), m]));
@ -312,15 +313,10 @@ for (const f of returned) {
}
// (c) the put-back (decision 23).
const returnedTriplets = new Set(returned.filter((f) => f && typeof f === 'object').map(tripletOf));
const putBack = new Map();
for (const f of allFindings) {
if (!f || typeof f !== 'object' || !f.engine) continue;
const key = tripletOf(f);
if (returnedTriplets.has(key)) continue;
const kept = putBack.get(key);
if (!kept || sevRank(f.severity) < sevRank(kept.severity)) putBack.set(key, f);
}
for (const f of putBack.values()) {
// Engine findings only, so dedupByTriplet keeps the higher severity, else the first seen.
const putBack = dedupByTriplet(allFindings.filter((f) =>
f && typeof f === 'object' && f.engine && !returnedTriplets.has(tripletOf(f))));
for (const f of putBack) {
const { brief_lowered_from: _lowered, ...copy } = f;
returned.push(copy);
}