fix(gate): knip sees the watch entry, the build must emit watch.html, and two hand copies are held

- 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.
This commit is contained in:
2026-10-06 03:19:33 +02:00
parent b20e9a3947
commit d44d8d6dda
19 changed files with 119 additions and 55 deletions
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import { readSources } from "../../lib/rawSources.testkit";
// docs/DESIGN.md "Text stays selectable": a click surface whose text is a title or a name (a card, a
// row, a person, a channel, a playlist, a log line) is `<Pressable selectable`, so its words select and
@@ -8,11 +9,11 @@ import { describe, expect, it } from "vitest";
// pinned here with the reason: a new Pressable, or one that lost its flag, fails this test until
// someone decides.
const files = import.meta.glob(["../**/*.tsx", "!../**/*.test.*", "!../design-lab/**"], {
query: "?raw",
import: "default",
eager: true,
}) as Record<string, string>;
const files = readSources(__dirname, [
"../**/*.tsx",
"!../**/*.test.*",
"!../design-lab/**",
]) as Record<string, string>;
/** file → [how many plain Pressables, why they are not titles]. */
const PLAIN: Record<string, [number, string]> = {