- The raw-source contract tests read files from disk (rawSources.testkit) instead of import.meta.glob, which knip counted as importing every src file — its unused-files lane could never name an orphan. knip now names src/watchMain.tsx as an entry (registered in docs/gates.md). - vite build fails when index.html or watch.html is not emitted. - The watch-entry import walk follows dynamic import() too. - audit_metrics.py's plan-code patterns are held equal to the guard's. - CrashBoundary is domain-free: it moves into components/ui.
117 lines
4.9 KiB
TypeScript
117 lines
4.9 KiB
TypeScript
import { execFileSync } from "node:child_process";
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
// The code says WHY; the plan that produced it stays in the private ops repo. A comment that cites
|
|
// `E28 review J4`, `(R6 S3b)` or `deep-review round 5` names a document no reader of this repo has,
|
|
// so the citation is noise at best, and at worst a rule that only makes sense next to the plan. All
|
|
// of them were taken out at once (2026-10-04); this keeps new ones out of every tracked text file and
|
|
// every new one not yet added (comments, docstrings, test titles, lint messages alike).
|
|
//
|
|
// Not scanned: Markdown (the docs and the changelog keep a decision's date and its epic as history)
|
|
// and the Alembic migrations (each one is a dated record of what changed).
|
|
|
|
const REPO = new URL("../../../", import.meta.url);
|
|
const SKIPPED =
|
|
/\.(md|webp|png|jpe?g|ico|woff2?)$|^backend\/alembic\/versions\/|package-lock\.json$/;
|
|
// the guard itself, and the metric that counts epics by the same definition
|
|
const ALLOWED = new Set([
|
|
"frontend/src/lib/planCodes.contract.test.ts",
|
|
"scripts/audit_metrics.py",
|
|
]);
|
|
// Named exceptions, cut from a line before it is matched: TV episode notation (`S2 · E5`), a test
|
|
// fixture's season or episode title (`title="S1"`, `title: "E1"`) and ruff's default rule families.
|
|
// Anything else that looks like a code is one — `"""E30:`, `it("E36:`, `# S6:` and `S3's` included.
|
|
const NOT_CODES = /\bS\d{1,2} ?·? ?E\d+|\btitle[=:] ?"[ES]\d{1,2}"|\bE4\/E7\/E9\b/g;
|
|
const PLAN_CODES = [
|
|
// an epic (`E28`, `E9.5`, `E28/E30`), an older roadmap item (`R14`), a sprint (`S6`, `S3e2`), a
|
|
// phase (`P1`) or a plan's decision (`D1`, `D6-A`) — not `E2E`, not a ruff rule (`S110`), not
|
|
// `S3h`, the media-card decision docs/DESIGN.md is headed by
|
|
/\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2}|S(?!3h\b)\d{1,2}(?:[a-z]\d?)?|P\d|D\d{1,2})\b/,
|
|
// a review round (`round 4`, `deep-review`)
|
|
/\b[Rr]ound[ -]\d|deep-review/,
|
|
];
|
|
|
|
const isPlanCode = (line: string) => {
|
|
const rest = line.replace(NOT_CODES, "");
|
|
return PLAN_CODES.some((re) => re.test(rest));
|
|
};
|
|
|
|
function tracked(): string[] {
|
|
const out = execFileSync(
|
|
"git",
|
|
["ls-files", "-z", "--cached", "--others", "--exclude-standard"],
|
|
{
|
|
cwd: REPO,
|
|
encoding: "utf-8",
|
|
},
|
|
);
|
|
// a file deleted but not yet staged is still listed
|
|
return out
|
|
.split("\0")
|
|
.filter((f) => f && !SKIPPED.test(f) && !ALLOWED.has(f) && existsSync(new URL(f, REPO)));
|
|
}
|
|
|
|
describe("plan codes", () => {
|
|
it("are recognised in each form the purged citations took", () => {
|
|
const cited = [
|
|
'"""E30: the new sink.',
|
|
'it("E36: keeps x", () => {',
|
|
"// both sides (E28/E30)",
|
|
"# D1: a newly shared download is downloadable by default",
|
|
"// binge stays under the viewer's control (D6-A)",
|
|
"// (owner decision D4)",
|
|
"# the roadmap's R6/R7 pair",
|
|
"// held since E9.5",
|
|
"// (S6) moved it",
|
|
"# S6: attach the acting user's cookie",
|
|
'"""S6 — per-user cookies',
|
|
"// S3's canonical upsert",
|
|
"// S1 justified that",
|
|
"// the order since P1",
|
|
'"""P1\'s invariant',
|
|
"// a sprint then a roadmap item: S2 R14",
|
|
"// deep-review round 4 found it",
|
|
];
|
|
const clean = [
|
|
'title="E1",',
|
|
'title: "S1",',
|
|
"# ruff's default rule set (E4/E7/E9 + F)",
|
|
"// an E2E run",
|
|
"// S2 · E5 is the second episode",
|
|
'expect(btn.textContent).toContain("S2 · E1");',
|
|
"// the S3h media card",
|
|
'"S110", "S112",',
|
|
"# the JPEG SOI marker (0xFF 0xD8 0xFF)",
|
|
"// a 3D view",
|
|
];
|
|
expect(cited.filter((l) => !isPlanCode(l))).toEqual([]);
|
|
expect(clean.filter(isPlanCode)).toEqual([]);
|
|
});
|
|
|
|
it("are what the audit metric counts by (its copy of the patterns is held equal here)", () => {
|
|
const py = readFileSync(new URL("scripts/audit_metrics.py", REPO), "utf8");
|
|
// A pattern there is `NAME = re.compile(r"…" r"…", re.ASCII)`: its raw literals, joined.
|
|
const pattern = (name: string) => {
|
|
const start = py.indexOf(`${name} = re.compile(`);
|
|
const call = py.slice(start, py.indexOf("re.ASCII", start));
|
|
return [...call.matchAll(/r(["'])(.*?)\1/g)].map((m) => m[2]).join("");
|
|
};
|
|
// JavaScript's source escapes the slash a regex literal needs; Python's has none to escape.
|
|
const js = (re: RegExp) => re.source.split(String.raw`\/`).join("/");
|
|
expect(pattern("NOT_CODES")).toBe(js(NOT_CODES));
|
|
expect(pattern("PLAN_CODE")).toBe(PLAN_CODES.map(js).join("|"));
|
|
});
|
|
|
|
it("are not cited in the code", () => {
|
|
const files = tracked();
|
|
expect(files.length).toBeGreaterThan(1000); // a scan that reads nothing is green over nothing
|
|
const hits = files.flatMap((f) =>
|
|
readFileSync(new URL(f, REPO), "utf-8")
|
|
.split("\n")
|
|
.flatMap((line, i) => (isPlanCode(line) ? [`${f}:${i + 1}: ${line.trim()}`] : [])),
|
|
);
|
|
expect(hits).toEqual([]);
|
|
});
|
|
});
|