test: give every scanning guard a floor on what it found

A guard that compares a sweep against an allowlist, or against "none of
these may exist", passes when the sweep comes back empty. walkSourceFiles
already refuses a short walk; this adds the same refusal for what a guard
does with the walk.

- requireFloor(label, found, floor) throws below the floor and refuses a
  floor of zero.
- scanSourceMatches(root, pattern, { fileFloor, matchFloor }) walks, matches
  and floors both the files read and the hits found.

Floors added, pinned at the current counts:

- coach_usage owner columns: the clauses it actually examined (6).
- measurement reference index: the foreign keys it extracted (2).
- job outcome fact inventory: job files (130, floor 120) and fact keys
  (399, floor 350).
- tone harness: assessment prompt modules (11).
- coach locale default: turn pipeline modules (15, floor 12).
- locale integrity: message bundles (7).
- settings card header coverage and settings anatomy: the walked cards.
- coach metric scope inventory: insight route sources.
- side-effect drift guard: both enums populated, since two empty keysets
  are equal.

The pill-mapper guard walked with globSync, which skips dot-prefixed
directories; it now walks through walkSourceFiles like the rest.
This commit is contained in:
Marc-André Bombeck committed 2026-10-01 10:52:48 +02:00
1 parent a3b5480d04
commit b4577f5c31
12 files changed
+166 -37

No files matched your search

@@ -98,12 +98,14 @@ describe("coach_usage — the two counters move together", () => {
it("names operator_tokens in every clause that names total_tokens", () => {
const offences: string[] = [];
let examined = 0;
for (const file of files) {
const source = readFileSync(file, "utf8");
if (!source.includes("coach_usage")) continue;
for (const literal of sqlLiterals(source)) {
for (const clause of columnClauses(literal)) {
if (!clause.text.includes("total_tokens")) continue;
examined += 1;
if (clause.text.includes("operator_tokens")) continue;
offences.push(
`${relative(REPO_ROOT, file)} — ${clause.label}: ${clause.text
@@ -118,6 +120,8 @@ describe("coach_usage — the two counters move together", () => {
offences,
`Every raw coach_usage clause that touches total_tokens must touch operator_tokens too:\n${offences.join("\n")}`,
).toEqual([]);
// No clauses examined would agree with the rule above on nothing.
expect(examined).toBeGreaterThanOrEqual(6);
});
it("keeps the raw-SQL writer set to the files that reason about the ledger", () => {
+91 -2
View File
@@ -50,8 +50,8 @@
* worktrees, each a full second copy of the tree that would double every
* match. Every caller roots at `src/`, `e2e/` or a subtree of one.
*/
import { readdirSync } from "node:fs";
import { sep } from "node:path";
import { readdirSync, readFileSync } from "node:fs";
import { join, sep } from "node:path";
export function walkSourceFiles(
root: string,
@@ -140,3 +140,92 @@ export function stripComments(source: string): string {
return lines.join("\n");
}
/**
* Asserts that a sweep found at least `floor` things, and returns them.
*
* `walkSourceFiles` puts a floor under the walk. This puts one under what a
* guard does with it: the narrowed file set, the call sites a matcher picked
* out, the routes an inventory extracted. A matcher that drifts out of step
* with the code — a renamed helper, a call split across two lines, a regex
* that stopped compiling the way it was meant to — returns an empty list, and
* an empty list agrees with every allowlist and every "none of these may
* exist" rule. That is how the Bearer-scope guard stayed green for weeks while
* matching nothing.
*
* Pin `floor` at the real count when the set is a closed inventory, and below
* it with headroom only when the count legitimately moves. A floor of zero is
* refused: it is the bug this exists to prevent, spelled as a parameter.
*/
export function requireFloor<T>(
label: string,
found: readonly T[],
floor: number,
): readonly T[] {
if (!Number.isInteger(floor) || floor < 1) {
throw new Error(
`requireFloor(${label}): floor must be a positive integer, got ${floor}.`,
);
}
if (found.length < floor) {
throw new Error(
`${label}: found ${found.length}, below the stated floor of ${floor}. ` +
`A matcher that finds this little has drifted from the code it ` +
`reads; fix the matcher before lowering the floor.`,
);
}
return found;
}
export interface SourceMatch {
/** Path relative to the scanned root, posix-separated. */
file: string;
/** 1-based line of the match start. */
line: number;
/** The matched text. */
text: string;
}
/**
* Walk `root`, run `pattern` over every kept file, and return each match —
* with a floor on the files read AND a floor on the matches found.
*
* `pattern` must carry the `g` flag; it is the only way to report more than
* one hit per file, and a non-global regex silently finding one hit per file
* is the kind of quiet narrowing this helper is for.
*/
export function scanSourceMatches(
root: string,
pattern: RegExp,
options: {
fileFloor: number;
matchFloor: number;
extensions?: readonly string[];
include?: (rel: string) => boolean;
stripComments?: boolean;
},
): SourceMatch[] {
if (!pattern.global) {
throw new Error(`scanSourceMatches: pattern ${pattern} needs the g flag.`);
}
const files = walkSourceFiles(root, {
floor: options.fileFloor,
extensions: options.extensions,
}).filter(options.include ?? (() => true));
requireFloor(`scanSourceMatches(${root}) files`, files, options.fileFloor);
const matches: SourceMatch[] = [];
for (const file of files) {
const raw = readFileSync(join(root, file), "utf8");
const source = options.stripComments ? stripComments(raw) : raw;
for (const m of source.matchAll(pattern)) {
const line = source.slice(0, m.index).split("\n").length;
matches.push({ file, line, text: m[0] });
}
}
return requireFloor(
`scanSourceMatches(${root}, ${pattern})`,
matches,
options.matchFloor,
) as SourceMatch[];
}
@@ -64,6 +64,7 @@ describe("columns that point into measurements are indexed", () => {
const refs = measurementReferences();
it("finds the references (a matcher that finds none proves nothing)", () => {
expect(refs.length).toBeGreaterThanOrEqual(2);
expect(refs).toEqual(
expect.arrayContaining([
"ecg_recordings.measurement_id",
@@ -4,6 +4,7 @@ import { join, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import type { CoachScopeSource } from "@/lib/ai/coach/types";
import { requireFloor } from "@/__tests__/helpers/source-files";
import {
scopeSourceFromMetricKey,
scopeSourceMetricLabelKey,
@@ -38,8 +39,10 @@ function sourceFilesBelow(directory: string): string[] {
}
function genericMetricCardInventory(): Set<string> {
const insightRouteSources = sourceFilesBelow(
join(PROJECT_ROOT, "src/app/insights"),
const insightRouteSources = requireFloor(
"insight route sources",
sourceFilesBelow(join(PROJECT_ROOT, "src/app/insights")),
60,
).map((path) => readFileSync(path, "utf8"));
const recoverySource = readFileSync(
join(PROJECT_ROOT, "src/components/insights/recovery/recovery-section.tsx"),
@@ -9,6 +9,7 @@ import { PageHeader } from "@/components/ui/page-header";
import { SettingsCard } from "@/components/settings/settings-card";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { SettingsCardActions } from "@/components/settings/_card-actions";
import { requireFloor } from "@/__tests__/helpers/source-files";
/**
* Settings + Admin read as one surface. These pin the parts of that claim
@@ -179,8 +180,12 @@ describe("the Settings and Admin shells route their heading through PageHeader",
describe("the abolished body gutter stays abolished", () => {
it("has no `pl-7` left under Settings or Admin", () => {
const offenders: string[] = [];
for (const dir of ["settings", "admin"]) {
for (const file of walk(join(ROOT, "src", "components", dir))) {
for (const [dir, floor] of [
["settings", 130],
["admin", 30],
] as const) {
const files = walk(join(ROOT, "src", "components", dir));
for (const file of requireFloor(`${dir} components`, files, floor)) {
const source = readFileSync(file, "utf8");
for (const [i, line] of source.split("\n").entries()) {
// Comments may name the class they retired; class strings may not.
@@ -23,6 +23,8 @@ import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { requireFloor } from "@/__tests__/helpers/source-files";
const ROOT = join(__dirname, "../../../..");
const SETTINGS_DIR = join(ROOT, "src/components/settings");
@@ -59,7 +61,8 @@ describe("Settings card headers route through SettingsCardHeader", () => {
it("has no hand-rolled <h2 text-lg> outside the primitive + allowlist", () => {
const offenders: string[] = [];
for (const file of walk(SETTINGS_DIR)) {
const files = requireFloor("settings cards", walk(SETTINGS_DIR), 80);
for (const file of files) {
const base = file.slice(file.lastIndexOf("/") + 1);
if (base === PRIMITIVE) continue;
if (ALLOWLIST.has(base)) continue;
@@ -9,8 +9,9 @@
*/
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { join, sep } from "node:path";
import { globSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "@/__tests__/helpers/source-files";
import type { SyncVerdict } from "@/lib/integrations/sync-verdict";
import {
@@ -113,14 +114,10 @@ describe("structural guard — no second pill dialect", () => {
const SRC = join(process.cwd(), "src");
function sourceFiles(): string[] {
return globSync("**/*.{ts,tsx}", { cwd: SRC })
.filter(
(p) => !p.startsWith(`generated${sep}`) && !p.startsWith("generated/"),
)
return walkSourceFiles(SRC, { floor: 3000 })
.filter((p) => !p.startsWith("generated/"))
.filter((p) => !p.includes("__tests__"))
.filter((p) => !p.endsWith(".test.ts") && !p.endsWith(".test.tsx"))
.map((p) => p.split(sep).join("/"))
.sort();
.filter((p) => !p.endsWith(".test.ts") && !p.endsWith(".test.tsx"));
}
/**
@@ -2,6 +2,8 @@ import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { requireFloor } from "@/__tests__/helpers/source-files";
const ROOT = join(__dirname, "../../..");
const MESSAGES_DIR = join(ROOT, "messages");
const EN_PATH = join(MESSAGES_DIR, "en.json");
@@ -20,7 +22,9 @@ function discoverLocales(): Array<{ locale: string; path: string }> {
}));
}
const ALL_LOCALES = discoverLocales();
// Every parity check below iterates this list, so an empty discovery would
// run none of them and pass. Seven bundles ship today.
const ALL_LOCALES = requireFloor("message bundles", discoverLocales(), 7);
const NON_EN_LOCALES = ALL_LOCALES.filter((l) => l.locale !== "en");
// JSON.parse silently keeps the last value when an object has duplicate keys,
@@ -16,6 +16,8 @@ import { describe, it, expect } from "vitest";
import { readdirSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { requireFloor } from "@/__tests__/helpers/source-files";
const ROOT = resolve(__dirname, "../../../..");
/**
@@ -23,10 +25,14 @@ const ROOT = resolve(__dirname, "../../../..");
* so every module there is guarded — read from the directory, so a step added
* later is covered without editing this list.
*/
const TURN_PIPELINE_FILES = readdirSync(resolve(ROOT, "lib/ai/coach/turn"))
.filter((name) => name.endsWith(".ts"))
.sort()
.map((name) => `lib/ai/coach/turn/${name}`);
const TURN_PIPELINE_FILES = requireFloor(
"coach turn pipeline modules",
readdirSync(resolve(ROOT, "lib/ai/coach/turn"))
.filter((name) => name.endsWith(".ts"))
.sort()
.map((name) => `lib/ai/coach/turn/${name}`),
12,
);
const GUARDED_FILES = [
"app/api/insights/chat/route.ts",
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { requireFloor } from "@/__tests__/helpers/source-files";
import { locales, type Locale } from "@/lib/i18n/config";
import { openingShape } from "@/lib/ai/prompts/shared-contracts";
import {
@@ -110,14 +111,18 @@ describe("tone harness — prompt surfaces", () => {
"native-prompts.ts",
"status-batch.ts",
]);
const modules = readdirSync(dir).filter(
(f) =>
f.endsWith(".ts") &&
!f.endsWith(".d.ts") &&
!NON_ASSESSMENT.has(f) &&
/export function get\w*SystemPrompt/.test(
readFileSync(join(dir, f), "utf8"),
),
const modules = requireFloor(
"assessment prompt modules",
readdirSync(dir).filter(
(f) =>
f.endsWith(".ts") &&
!f.endsWith(".d.ts") &&
!NON_ASSESSMENT.has(f) &&
/export function get\w*SystemPrompt/.test(
readFileSync(join(dir, f), "utf8"),
),
),
11,
);
const registered = new Set(
ASSESSMENT_SURFACES.map((s) =>
@@ -13,6 +13,7 @@ import { join } from "node:path";
import ts from "typescript";
import { describe, expect, it } from "vitest";
import { requireFloor } from "@/__tests__/helpers/source-files";
import { JOB_FACT_ALLOWLIST } from "@/lib/jobs/job-outcome";
const JOBS_ROOT = join(process.cwd(), "src/lib/jobs");
@@ -109,14 +110,18 @@ function successFactKeys(file: string): string[] {
describe("durable production job-outcome fact inventory", () => {
it("allows every explicit production success-fact key", () => {
const unknown = productionJobFiles()
.flatMap((file) =>
successFactKeys(file).map((key) => ({
file: file.replace(`${process.cwd()}/`, ""),
key,
})),
)
.filter(({ key }) => !JOB_FACT_ALLOWLIST.has(key));
const facts = requireFloor(
"jobDone fact keys",
requireFloor("production job files", productionJobFiles(), 120).flatMap(
(file) =>
successFactKeys(file).map((key) => ({
file: file.replace(`${process.cwd()}/`, ""),
key,
})),
),
350,
);
const unknown = facts.filter(({ key }) => !JOB_FACT_ALLOWLIST.has(key));
expect(unknown).toEqual([]);
});
@@ -44,6 +44,13 @@ function sortedKeys<T extends string>(keys: readonly T[]): T[] {
}
describe("side-effect taxonomy drift guard", () => {
it("compares populated keysets, not two empty ones", () => {
// Every check below is an equality, and two empty sets are equal. A
// mocked or mis-resolved enum would pass all of them on nothing.
expect(Object.values(MedicationSideEffectCategory).length).toBe(5);
expect(Object.values(MedicationSideEffectEntry).length).toBe(21);
});
describe("category keysets", () => {
it("validator categories equal Prisma enum categories", () => {
const prisma = Object.values(MedicationSideEffectCategory);