import { access, readFile } from "node:fs/promises"; /** * GOV-03. Joins the machine-readable remediation dispositions against the prose * ledger. * * The previous ledger declared "All 38 are now FIXED" while six of those rows * were reproducibly partial. A sentence is cheap and a reviewer reads it as * evidence, so this gate makes the claim derivable rather than authored: every * disposition must name a test path that exists, the prose must carry the same * verdict for the same id, and a blanket closure sentence is only allowed when * nothing is still open. */ type Disposition = Readonly<{ id: string; previous: string; disposition: string; summary: string; evidence: readonly string[]; }>; const DISPOSITIONS_PATH = "docs/operations/adapter-remediation-dispositions.json"; const LEDGER_PATH = "docs/operations/adapter-remediation-ledger.md"; /** * Finding ids are only unique within a review pass — the second pass also used * `SW-01` — so rows are matched inside this pass's section rather than anywhere * in the document. */ const SECTION_HEADING = "## Third re-review (2026-08-14)"; const CLOSED_DISPOSITIONS: ReadonlySet = new Set(["FIXED"]); /** Sentences that assert everything is done, and therefore need proof. */ const BLANKET_CLOSURE = /All\s+(?:\d+|findings|rows)[^.\n]*\b(?:FIXED|closed)\b/giu; async function main(): Promise { const problems: string[] = []; const raw: unknown = JSON.parse(await readFile(DISPOSITIONS_PATH, "utf8")); if ( raw === null || typeof raw !== "object" || !Array.isArray((raw as { dispositions?: unknown }).dispositions) ) { console.error(`${DISPOSITIONS_PATH}: dispositions array is required`); process.exitCode = 1; return; } const dispositions = (raw as { dispositions: Disposition[] }).dispositions; const document = await readFile(LEDGER_PATH, "utf8"); const sectionStart = document.indexOf(SECTION_HEADING); if (sectionStart < 0) { console.error(`${LEDGER_PATH}: missing section "${SECTION_HEADING}"`); process.exitCode = 1; return; } const ledger = document.slice(sectionStart); const seen = new Set(); for (const row of dispositions) { if (typeof row.id !== "string" || row.id.length === 0) { problems.push("a disposition row has no id"); continue; } if (seen.has(row.id)) problems.push(`duplicate disposition id: ${row.id}`); seen.add(row.id); if (typeof row.disposition !== "string" || row.disposition.length === 0) { problems.push(`${row.id}: disposition is required`); } if (!Array.isArray(row.evidence) || row.evidence.length === 0) { problems.push(`${row.id}: at least one evidence path is required`); continue; } for (const path of row.evidence) { try { await access(path); } catch { problems.push(`${row.id}: evidence path does not exist: ${path}`); } } // The prose must carry the same verdict for the same id, so a reader of the // document and a reader of the receipt cannot reach different conclusions. const line = ledger .split("\n") .find((candidate) => candidate.includes(`| ${row.id} |`)); if (!line) { problems.push(`${row.id}: no row in ${LEDGER_PATH}`); continue; } if (!line.includes(`\`${row.disposition}\``)) { problems.push( `${row.id}: ${LEDGER_PATH} does not record \`${row.disposition}\``, ); } } const open = dispositions.filter( (row) => !CLOSED_DISPOSITIONS.has(row.disposition), ); const blanketClaims = [...document.matchAll(BLANKET_CLOSURE)]; if (open.length > 0 && blanketClaims.length > 0) { problems.push( `${LEDGER_PATH} declares a blanket closure (${blanketClaims .map((match) => `"${match[0]}"`) .join(", ")}) while ${open.length} finding(s) are still open: ${open .map((row) => `${row.id}=${row.disposition}`) .join(", ")}`, ); } if (problems.length > 0) { for (const problem of problems) console.error(problem); process.exitCode = 1; return; } console.log( `Remediation ledger: ${dispositions.length} dispositions joined to ` + `${LEDGER_PATH}; ${open.length} open; evidence paths verified`, ); } await main();