Files
projectbrain-Public/scripts/generate-validation-matrix.mjs
T

77 lines
4.4 KiB
JavaScript

import { readFileSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
const root = process.cwd();
const packages = [
{ scope: "root", path: "package.json" },
{ scope: "backend", path: "backend/package.json" },
];
const classify = (name, command) => {
const value = `${name} ${command}`.toLowerCase();
if (value.includes("rollback")) return "rollback";
if (value.includes("prisma migrate") || value.includes("db:deploy") || value.includes("db:migrate") || value.includes("db:status")) return "migration";
if (value.includes("database") || value.includes("db:seed") || /^smoke:p[2-8]$/.test(name)) return "database integration";
if (value.includes("deploy") || value.includes("docker") || value.includes("unraid")) return "deployment";
if (value.includes("playwright") || value.includes("browser") || value.includes("clickthrough")) return "browser E2E";
if (value.includes("axe") || value.includes("accessib")) return "accessibility";
if (value.includes("audit") && (value.includes("security") || value.includes("secret") || value.includes("container"))) return "security";
if (value.includes("backup") || value.includes("restore") || value.includes("recovery") || value.includes("freshness")) return "recovery";
if (name.startsWith("smoke:")) return "API smoke";
if (name.startsWith("test") || value.includes("vitest")) return "unit";
if (name === "typecheck" || name === "lint" || name === "build" || name.startsWith("validate:")) return "static";
if (name.startsWith("audit:") || name === "doctor") return "source guard";
if (name.startsWith("release:")) return "deployment";
return "static";
};
const modeFor = (name, command, category) => {
const value = `${name} ${command}`.toLowerCase();
if (category === "database integration" || category === "migration") return "real database";
if (value.includes("fixture")) return "fixture";
if (value.includes("real")) return "real API (data mode depends on runtime)";
if (category === "source guard" || category === "static" || category === "security") return "source only";
return "runtime-dependent";
};
const rows = [];
for (const pkg of packages) {
const json = JSON.parse(readFileSync(resolve(root, pkg.path), "utf8"));
for (const [name, command] of Object.entries(json.scripts ?? {})) {
const category = classify(name, command);
rows.push({ scope: pkg.scope, name, command, category, mode: modeFor(name, command, category) });
}
}
rows.sort((a, b) => a.scope.localeCompare(b.scope) || a.name.localeCompare(b.name));
const escapeCell = (value) => String(value).replaceAll("|", "\\|").replaceAll("\n", " ");
const lines = [
"# ProjectBrain Validation Matrix",
"",
`Generated: ${new Date().toISOString()}`,
"",
"This inventory classifies every package script. Classification describes what the command actually proves, not what its name claims. Results and artifacts remain `Not run in current matrix` until a categorized runner records them.",
"",
"## Evidence policy",
"",
"Mock, fixture and real-database evidence are never interchangeable. A source guard cannot close a runtime, database, recovery, deployment or rollback gate.",
"",
"| Scope | Script | Category | Mode/environment | Command | Latest result | Evidence | Commit |",
"| --- | --- | --- | --- | --- | --- | --- | --- |",
...rows.map((row) => `| ${escapeCell(row.scope)} | \`${escapeCell(row.name)}\` | ${escapeCell(row.category)} | ${escapeCell(row.mode)} | \`${escapeCell(row.command)}\` | Not run in current matrix | — | — |`),
"",
"## Current release-evidence gaps",
"",
"The repository now contains unit, Fastify injection, disposable-PostgreSQL, Playwright/accessibility, recovery, secret and container-image gates. Script presence and local execution do not prove a release artifact.",
"",
"- The categorized Gitea workflow has not been observed on its remote runner for the candidate commit.",
"- Built OCI image scans, SBOM/provenance and archive digests are encoded in CI but no successful remote artifact run is attached yet.",
"- Clean-room target installation, target-host timed rollback and post-deploy telemetry remain unexecuted.",
"- Independent usability/screen-reader and operator-runbook executions remain unsigned.",
"",
];
writeFileSync(resolve(root, "docs/VALIDATION_MATRIX.md"), lines.join("\n"), "utf8");
console.log(`Wrote docs/VALIDATION_MATRIX.md with ${rows.length} script classifications.`);