fix(gate): close the gaps in the plan-code guard, the button lists and the tooling

- planCodes: the quote and slash lookarounds let the commonest form through (a docstring or test
  title opening with a code, a slash pair); the two real exceptions (an episode titled "E1", ruff's
  E4/E7/E9) are now named and cut first, with cases that fail on the old pattern. audit_metrics
  counts by the same definition.
- button-primitive: both file lists live in the rule body, out of sight of the snapshot and the
  register; each is now pinned to its exact set of ui/ files (red when either is widened).
- check.sh --brief shows a short log whole and keeps the line before each hit, so ruff's rule and
  eslint's file path survive the excerpt.
- lint-changed runs prettier over every changed frontend file, as the gate does, not only code.
- CLAUDE.md and gates.md no longer claim the basedpyright baseline was found harmless: it was never
  triaged, and the reportOptional* entries are the likeliest real bugs.
This commit is contained in:
2026-10-05 00:19:50 +02:00
parent eecc195b7e
commit d1bbe4524c
10 changed files with 137 additions and 33 deletions
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { eslint } from "./lintProbe.testkit";
import { eslint, uiFiles } from "./lintProbe.testkit";
// `local/button-primitive` (eslint.config.js): one primitive per button family (docs/DESIGN.md).
// 268 hand-rolled buttons were swept onto ui/Button, IconButton, Pressable, Chip and
@@ -43,6 +43,37 @@ describe("a hand-rolled button", () => {
).toBe(0);
});
// The primitive list lives in the rule body, where neither the gate snapshot nor docs/gates.md
// sees it, so this holds it to exactly these files of ui/.
it("is allowed in exactly the control primitives of ui/", { timeout: 60_000 }, async () => {
const allowed: string[] = [];
for (const file of uiFiles())
if (
(await hits(
'export const R = () => <button type="button">x</button>;\n',
`src/components/ui/${file}`,
)) === 0
)
allowed.push(file);
expect(allowed).toEqual([
"AnchoredPopover.tsx",
"Button.tsx",
"Chip.tsx",
"Combobox.tsx",
"DragHandle.tsx",
"FilterSelect.tsx",
"FocusSafeButton.tsx",
"IconButton.tsx",
"Pressable.tsx",
"PriorityStepper.tsx",
"SegmentedToggle.tsx",
"SelectTrigger.tsx",
"SortControl.tsx",
"Tabs.tsx",
"form.tsx",
]);
});
it("is refused spelled as an element with the button role; a component's role is its own", async () => {
expect(await hits('export const F = () => <div role="button" tabIndex={0}>x</div>;\n')).toBe(1);
expect(await hits('export const G = () => <span role="button">x</span>;\n')).toBe(1);
+10 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { eslint } from "./lintProbe.testkit";
import { eslint, uiFiles } from "./lintProbe.testkit";
// `local/button-primitive`'s disabled check (eslint.config.js): a raw `<button disabled>` throws a
// focused keyboard user to <body> the moment it turns disabled. ~60 were swept onto ui/FocusSafeButton;
@@ -34,4 +34,13 @@ describe("a raw button that can be disabled", () => {
expect(await hits(DISABLED, "src/components/ui/FocusSafeButton.tsx")).toBe(0);
expect(await hits(DISABLED, "src/components/ui/AnchoredPopover.tsx")).toBe(0);
});
// The exemption lives in the rule body, where neither the gate snapshot nor docs/gates.md sees it,
// so this holds it to exactly those two: every other file in ui/ is refused.
it("is exempt in exactly those two of the ui/ files", { timeout: 60_000 }, async () => {
const exempt: string[] = [];
for (const file of uiFiles())
if ((await hits(DISABLED, `src/components/ui/${file}`)) === 0) exempt.push(file);
expect(exempt).toEqual(["AnchoredPopover.tsx", "FocusSafeButton.tsx"]);
});
});
+9 -4
View File
@@ -1323,10 +1323,15 @@
" rm -f \"$out\"",
"}",
"brief_excerpt() {",
" local hits",
" hits=\"$(grep -v -E '^testdb-[0-9]+ ' \"$1\" |",
" grep -E -i -A 2 -m 20 'error|fail|✗|×|warn|would reformat|unused|vulnerab|found [0-9]')\"",
" if [ -n \"$hits\" ]; then printf '%s\\n' \"$hits\"; else tail -n 20 \"$1\"; fi",
" local log hits",
" log=\"$(grep -v -E '^testdb-[0-9]+ ' \"$1\")\"",
" if [ \"$(printf '%s\\n' \"$log\" | wc -l)\" -le 60 ]; then",
" printf '%s\\n' \"$log\"",
" return",
" fi",
" hits=\"$(printf '%s\\n' \"$log\" |",
" grep -E -i -B 1 -A 2 -m 20 'error|fail|✗|×|warn|would reformat|unused|vulnerab|found [0-9]')\"",
" if [ -n \"$hits\" ]; then printf '%s\\n' \"$hits\"; else printf '%s\\n' \"$log\" | tail -n 20; fi",
"}",
"ruff_pinned() {",
" local pin host",
+7
View File
@@ -1,3 +1,4 @@
import { readdirSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { ESLint } from "eslint";
@@ -35,6 +36,12 @@ export const eslint = new ESLint({
},
});
/** Every component file in ui/, by name — for a probe that pins which of them a rule exempts. */
export const uiFiles = (): string[] =>
readdirSync(new URL("../components/ui/", import.meta.url))
.filter((f) => f.endsWith(".tsx") && !f.includes(".test."))
.sort();
beforeAll(async () => {
await eslint.lintText("", { filePath: "src/components/__probe.tsx" });
}, 60_000);
+34 -6
View File
@@ -19,15 +19,24 @@ 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: a test show's episode titled "E1" (the
// whole string, quotes included) and ruff's default rule families. Anything else that looks like a
// code is one — a docstring or a test title opening with `"""E30:` / `it("E36:` included.
const NOT_CODES = /"E\d{1,2}"|\bE4\/E7\/E9\b/g;
const PLAN_CODES = [
// an epic (`E28`, `E9.5`) or an older roadmap item (`R14`) — not `E2E`, not a ruff rule family
// (`E4/E7/E9`), not a TV episode (`S2 · E5`, a test show's episode titled "E1")
/(?<!S\d{1,2}\s?·?\s?)(?<![/"])\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2})\b(?![/"E])/,
// an epic (`E28`, `E9.5`, `E28/E30`) or an older roadmap item (`R14`) — not `E2E`, not a TV
// episode (`S2 · E5`)
/(?<!S\d{1,2}\s?·?\s?)\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2})\b(?!E)/,
// a sprint cited on its own (`(S6)`, `S3e2`) or a review round (`round 4`, `deep-review`) — not
// `S3h`, the media-card decision docs/DESIGN.md is headed by and the design lab labels
/\(S\d{1,2}[a-z]?\d?\)|\bS(?!3h\b)\d{1,2}[a-z]\d?\b|\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",
@@ -44,15 +53,34 @@ function tracked(): string[] {
}
describe("plan codes", () => {
it("are recognised in every form a citation takes", () => {
const cited = [
'"""E30: the new sink.',
'it("E36: keeps x", () => {',
"// both sides (E28/E30)",
"# the roadmap's R6/R7 pair",
"// held since E9.5",
"// (S6) moved it",
"// deep-review round 4 found it",
];
const clean = [
'title="E1",',
"# ruff's default rule set (E4/E7/E9 + F)",
"// an E2E run",
"// S2 · E5 is the second episode",
"// the S3h media card",
];
expect(cited.filter((l) => !isPlanCode(l))).toEqual([]);
expect(clean.filter(isPlanCode)).toEqual([]);
});
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) =>
PLAN_CODES.some((re) => re.test(line)) ? [`${f}:${i + 1}: ${line.trim()}`] : [],
),
.flatMap((line, i) => (isPlanCode(line) ? [`${f}:${i + 1}: ${line.trim()}`] : [])),
);
expect(hits).toEqual([]);
});