// Renders .superpowers/findings-ledger.json to FINDINGS.md, and validates it. // Run: node .superpowers/render-ledger.mjs # write FINDINGS.md // node .superpowers/render-ledger.mjs --check # validate only // node .superpowers/render-ledger.mjs --selftest # run built-in tests import assert from "node:assert/strict"; const VALID_STATUS = ["open", "fixed", "declined", "refuted", "duplicate", "blocked"]; // Must stay in lockstep with SEV_RANK below. render() sorts the open section by // SEV_RANK, and an unranked severity makes the comparator return NaN — which // leaves the sort order implementation-defined, so the rendering would stop // being a pure function of the ledger. The drift gate compares the rendering // against the ledger, so its whole premise rests on this being enforced. const VALID_SEVERITY = ["critical", "high", "medium", "low"]; export function validate(ledger) { const problems = []; const ids = new Set(); for (const r of ledger.findings) { if (ids.has(r.id)) problems.push(`duplicate id ${r.id}`); ids.add(r.id); if (!/^OC-\d{4}$/.test(r.id)) problems.push(`${r.id}: malformed id`); if (!VALID_STATUS.includes(r.status)) problems.push(`${r.id}: bad status ${r.status}`); if (!VALID_SEVERITY.includes(r.severity)) problems.push(`${r.id}: bad severity ${r.severity}`); if (r.status === "fixed" && (!r.fix || !r.fix.commit)) problems.push(`${r.id}: fixed without a commit`); if (r.status === "declined" && !r.rationale) problems.push(`${r.id}: declined without a rationale`); if (r.status === "duplicate" && !r.duplicateOf) problems.push(`${r.id}: duplicate without duplicateOf`); } return problems; } function selftest() { // Every fixture carries a severity: validate() now requires one, so omitting // it would make each case report two problems and assert against the wrong one. assert.deepEqual(validate({ findings: [] }), []); assert.deepEqual( validate({ findings: [{ id: "OC-0001", severity: "low", status: "fixed", fix: null }] }), ["OC-0001: fixed without a commit"], ); assert.deepEqual(validate({ findings: [{ id: "bad", severity: "low", status: "open" }] }), [ "bad: malformed id", ]); assert.deepEqual( validate({ findings: [ { id: "OC-0001", severity: "low", status: "open" }, { id: "OC-0001", severity: "low", status: "open" }, ], }), ["duplicate id OC-0001"], ); assert.deepEqual( validate({ findings: [{ id: "OC-0002", severity: "low", status: "declined" }] }), ["OC-0002: declined without a rationale"], ); // An unranked severity is what makes render()'s sort implementation-defined. assert.deepEqual( validate({ findings: [{ id: "OC-0003", severity: "moderate", status: "open" }] }), ["OC-0003: bad severity moderate"], ); assert.deepEqual(validate({ findings: [{ id: "OC-0004", status: "open" }] }), [ "OC-0004: bad severity undefined", ]); for (const sev of VALID_SEVERITY) { assert.deepEqual( validate({ findings: [{ id: "OC-0005", severity: sev, status: "open" }] }), [], ); } console.log("selftest: all assertions pass"); } const SEV_RANK = { critical: 0, high: 1, medium: 2, low: 3 }; export function render(ledger) { const by = (s) => ledger.findings.filter((f) => f.status === s); const open = by("open").sort((a, b) => SEV_RANK[a.severity] - SEV_RANK[b.severity]); const blocked = by("blocked"); const fixed = by("fixed"); const declined = by("declined"); const refuted = by("refuted"); const dup = by("duplicate"); const lines = []; lines.push("# OwnCord Findings Ledger", ""); lines.push( "Generated by `render-ledger.mjs`. Do not hand-edit — edit `findings-ledger.json`.", "", ); lines.push( `**${open.length} open** · ${blocked.length} blocked · ${fixed.length} fixed · ` + `${declined.length} declined · ${refuted.length} refuted · ${dup.length} duplicate`, "", ); const section = (title, rows, extra) => { if (!rows.length) return; lines.push(`## ${title}`, ""); for (const r of rows) { lines.push(`### ${r.id} — ${r.severity} — ${r.title}`, ""); lines.push( `\`${r.file}:${r.line}\` · found ${r.found} · hunt \`${r.hunt}\` · lens \`${r.lens}\``, "", ); if (r.why) lines.push(r.why, ""); if (r.repro) lines.push(`**Repro:** ${r.repro}`, ""); if (r.evidence) lines.push(`**Evidence:** ${r.evidence}`, ""); if (r.suggestedFix) lines.push(`**Suggested fix:** ${r.suggestedFix}`, ""); const e = extra && extra(r); if (e) lines.push(e, ""); } }; section("Open", open); section("Blocked — fix attempted, revert-proof failed", blocked); section( "Fixed", fixed, (r) => `**Fixed:** \`${r.fix.commit}\` · test \`${r.fix.test}\` · revert-proof ${r.fix.revertProof}`, ); section("Declined", declined, (r) => `**Declined:** ${r.rationale}`); section("Refuted", refuted); section("Duplicate", dup, (r) => `**Duplicate of** ${r.duplicateOf}`); return lines.join("\n"); } async function main() { const { readFileSync, writeFileSync } = await import("node:fs"); const { dirname, join } = await import("node:path"); const { fileURLToPath } = await import("node:url"); const here = dirname(fileURLToPath(import.meta.url)); const ledger = JSON.parse(readFileSync(join(here, "findings-ledger.json"), "utf8")); const problems = validate(ledger); if (problems.length) { for (const p of problems) console.error(`INVALID ${p}`); process.exit(1); } if (process.argv.includes("--check")) { console.log(`ledger valid: ${ledger.findings.length} finding(s)`); return; } writeFileSync(join(here, "FINDINGS.md"), render(ledger) + "\n"); console.log(`wrote FINDINGS.md (${ledger.findings.length} finding(s))`); } if (process.argv.includes("--selftest")) selftest(); else await main();