diff --git a/docs/operations/adapter-remediation-dispositions.json b/docs/operations/adapter-remediation-dispositions.json index 754ca14..d0e9edc 100644 --- a/docs/operations/adapter-remediation-dispositions.json +++ b/docs/operations/adapter-remediation-dispositions.json @@ -120,6 +120,7 @@ "previous": "PARTIAL", "disposition": "FIXED", "summary": "The shared abort primitive settles once by observation order, and all four consumers use it with bound timer snapshots.", + "markers": ["X-AUDIT-01", "X-AUDIT-02"], "evidence": [ "tests/unit/abortable-operation.test.ts", "tests/unit/image-cdn-runtime.test.ts", @@ -180,6 +181,7 @@ "previous": "OPEN", "disposition": "FIXED", "summary": "Duplicate abort mechanics were consolidated onto the shared primitive and the file-transfer budget was reset to cover the remaining correctness code.", + "markers": [], "evidence": ["config/recipes/frontend-capability-recipes.json"] } ] diff --git a/scripts/check-remediation-ledger.ts b/scripts/check-remediation-ledger.ts index eb3f247..9f322cc 100644 --- a/scripts/check-remediation-ledger.ts +++ b/scripts/check-remediation-ledger.ts @@ -18,6 +18,12 @@ type Disposition = Readonly<{ disposition: string; summary: string; evidence: readonly string[]; + /** + * Labels the evidence files actually carry. Defaults to the finding id; a row + * declares its own when the review labelled the work differently, as the + * cross-audit ids did. + */ + markers?: readonly string[]; }>; const DISPOSITIONS_PATH = "docs/operations/adapter-remediation-dispositions.json"; @@ -29,6 +35,9 @@ const LEDGER_PATH = "docs/operations/adapter-remediation-ledger.md"; */ const SECTION_HEADING = "## Third re-review (2026-08-14)"; const CLOSED_DISPOSITIONS: ReadonlySet = new Set(["FIXED"]); +const RECIPES_PATH = "config/recipes/frontend-capability-recipes.json"; +/** The table is `| id | prior verdict | disposition | evidence |`. */ +const DISPOSITION_OFFSET_FROM_ID = 2; /** Sentences that assert everything is done, and therefore need proof. */ const BLANKET_CLOSURE = /All\s+(?:\d+|findings|rows)[^.\n]*\b(?:FIXED|closed)\b/giu; @@ -69,11 +78,22 @@ async function main(): Promise { problems.push(`${row.id}: at least one evidence path is required`); continue; } + const markers = row.markers ?? [row.id]; for (const path of row.evidence) { + let contents: string; try { - await access(path); + contents = await readFile(path, "utf8"); } catch { problems.push(`${row.id}: evidence path does not exist: ${path}`); + continue; + } + // A path that exists proves nothing on its own. The file has to name the + // finding it is evidence for, so a row cannot point at an unrelated suite + // and look substantiated. + if (markers.length > 0 && !markers.some((mark) => contents.includes(mark))) { + problems.push( + `${row.id}: ${path} does not mention ${markers.join(" or ")}`, + ); } } // The prose must carry the same verdict for the same id, so a reader of the @@ -85,9 +105,16 @@ async function main(): Promise { problems.push(`${row.id}: no row in ${LEDGER_PATH}`); continue; } - if (!line.includes(`\`${row.disposition}\``)) { + // The row carries the prior verdict as well, so the disposition is read + // from its own column. Matching anywhere in the line let the "previous" + // cell satisfy the check and hid a disagreement between the two records. + const cells = line.split("|").map((cell) => cell.trim()); + const recorded = cells[cells.indexOf(row.id) + DISPOSITION_OFFSET_FROM_ID]; + if (recorded !== `\`${row.disposition}\``) { problems.push( - `${row.id}: ${LEDGER_PATH} does not record \`${row.disposition}\``, + `${row.id}: ${LEDGER_PATH} records ${ + recorded ?? "nothing" + } where the receipt says \`${row.disposition}\``, ); } } @@ -106,6 +133,24 @@ async function main(): Promise { ); } + // GOV-05 / X-AUDIT-04. The most drift-prone evidence in this document is a + // number someone typed. The one number that gates a release is checked + // against its source of truth rather than trusted. + const recipes: unknown = JSON.parse(await readFile(RECIPES_PATH, "utf8")); + const fileTransfer = ( + (recipes as { recipes?: readonly Record[] }).recipes ?? [] + ).find((recipe) => recipe["id"] === "file-transfer"); + const budget = fileTransfer?.["bundleBudgetGzipBytes"]; + if (typeof budget !== "number") { + problems.push(`${RECIPES_PATH}: file-transfer has no bundle budget`); + } else if (!ledger.includes(budget.toLocaleString("en-US"))) { + problems.push( + `${LEDGER_PATH} does not state the configured file-transfer budget of ${budget.toLocaleString( + "en-US", + )} gzip bytes`, + ); + } + if (problems.length > 0) { for (const problem of problems) console.error(problem); process.exitCode = 1;