mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 06:32:03 +00:00
Forty-two guard files carried their own comment stripper, block comments first in one non-greedy pass, so a /* inside a line comment opened a block that hid the code after it, and the stricter copies hid code behind a /* inside a string, which every e2e route glob has. They all read through the shared stripComments now, and it scans the way the parser does: strings, template literals and regular expressions keep every character, a comment inside a template expression goes, and line numbers are kept. A parity test strips every source file both ways, with the scanner and with the TypeScript parser, and fails on the first that differs. No guard reports anything new. Each one fails when the stripper returns nothing; three of them passed on an empty file and assert that the code they ban is read at all now.
124 lines
4.7 KiB
TypeScript
124 lines
4.7 KiB
TypeScript
/**
|
|
* v1.41 — which background calls may reason, frozen.
|
|
*
|
|
* Reasoning in the background is paid for every night, for every account, by
|
|
* nobody watching. So it is admitted where it changes the result a person
|
|
* reads (the daily briefing and the period narratives) and nowhere else: not
|
|
* on status cards, batch notes, derived scores, workout and reaction lines,
|
|
* nudges, document reads or OCR, which are routine, latency-bound and
|
|
* seed-reproducible.
|
|
*
|
|
* The only way a background call asks is the `reasoningJob` argument of the
|
|
* two chokepoints (`runStatusCompletion`, `runBriefingCompletion`). This test
|
|
* finds every place that sets it and holds the set, file by file and job by
|
|
* job, against the inventory below. A status card that started passing a job
|
|
* goes red here; so does a new caller nobody admitted.
|
|
*
|
|
* Its limit: it reads source text for a `reasoningJob:` property and the job
|
|
* literals beside it. A call that built the argument object elsewhere and
|
|
* spread it in would slip the matcher; the chokepoint signature is the only
|
|
* documented way in, and the wire test (`background-reasoning-wire.test.ts`)
|
|
* proves a call without the argument sends nothing.
|
|
*
|
|
* Mutation check: add `reasoningJob: "daily_briefing"` to
|
|
* `src/lib/insights/status-batch.ts`, or delete it from the narrative
|
|
* generator: either goes red.
|
|
*/
|
|
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { BACKGROUND_REASONING_JOBS } from "@/lib/ai/reasoning/levels";
|
|
|
|
import { stripComments, walkSourceFiles } from "./helpers/source-files";
|
|
|
|
const SRC = join(process.cwd(), "src");
|
|
|
|
/** File → the jobs it may name, each with the reason it may. */
|
|
const ADMITTED: Record<string, { jobs: string[]; why: string }> = {
|
|
"lib/insights/comprehensive-generate.ts": {
|
|
jobs: ["daily_briefing"],
|
|
why: "The daily briefing's generation and its phrasing re-roll; the JSON and grounding repairs do not reason.",
|
|
},
|
|
"lib/insights/briefing-request/completion.ts": {
|
|
jobs: ["daily_briefing"],
|
|
why: "A briefing asked for by hand reasons like the scheduled one; its JSON and grounding repairs do not.",
|
|
},
|
|
"lib/insights/narrative/period-narrative-generate.ts": {
|
|
jobs: ["period_narrative_month", "period_narrative_week"],
|
|
why: "The weekly and monthly narratives' first pass; the grounding repair does not reason.",
|
|
},
|
|
};
|
|
|
|
/** The routine generators that must never name a job. */
|
|
const NEVER = [
|
|
"lib/insights/status-batch.ts",
|
|
"lib/insights/status-card-generation.ts",
|
|
"lib/insights/status-pipeline.ts",
|
|
"lib/insights/metric-status.ts",
|
|
"lib/insights/biomarker-status.ts",
|
|
"lib/insights/derived/derived-assessment-ai.ts",
|
|
"lib/jobs/workout-insight-generate.ts",
|
|
];
|
|
|
|
/** `reasoningJob:` as a value, not the optional declaration `reasoningJob?:`. */
|
|
const SETTER = /\breasoningJob\s*:(?!\s*BackgroundReasoningJob)/g;
|
|
|
|
function scan(): Map<string, { setters: number; jobs: Set<string> }> {
|
|
const found = new Map<string, { setters: number; jobs: Set<string> }>();
|
|
const files = walkSourceFiles(SRC, { floor: 3000 })
|
|
.filter((p) => !p.startsWith("generated/"))
|
|
.filter((p) => !p.includes("__tests__"))
|
|
.filter((p) => !p.endsWith(".test.ts") && !p.endsWith(".test.tsx"));
|
|
for (const rel of files) {
|
|
const code = stripComments(readFileSync(join(SRC, rel), "utf8"));
|
|
const setters = code.match(SETTER)?.length ?? 0;
|
|
if (setters === 0) continue;
|
|
const jobs = new Set<string>();
|
|
for (const m of code.matchAll(SETTER)) {
|
|
const window = code.slice(m.index, m.index + 160);
|
|
for (const job of BACKGROUND_REASONING_JOBS) {
|
|
if (window.includes(`"${job}"`)) jobs.add(job);
|
|
}
|
|
}
|
|
found.set(rel, { setters, jobs });
|
|
}
|
|
return found;
|
|
}
|
|
|
|
describe("background reasoning inventory", () => {
|
|
const found = scan();
|
|
|
|
it("finds the admitted callers (an empty set is a failure, not a pass)", () => {
|
|
expect(found.size).toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it("only the admitted files name a job", () => {
|
|
expect([...found.keys()].sort()).toEqual(Object.keys(ADMITTED).sort());
|
|
});
|
|
|
|
it.each(Object.entries(ADMITTED))(
|
|
"%s names exactly its admitted jobs",
|
|
(rel, { jobs }) => {
|
|
expect([...(found.get(rel)?.jobs ?? [])].sort()).toEqual(
|
|
[...jobs].sort(),
|
|
);
|
|
},
|
|
);
|
|
|
|
it.each(NEVER)("%s never reasons", (rel) => {
|
|
const code = stripComments(readFileSync(join(SRC, rel), "utf8"));
|
|
expect(code).not.toMatch(/\breasoningJob\b/);
|
|
expect(code).not.toMatch(/\breasoning\s*:/);
|
|
});
|
|
|
|
it("every admitted job is a known background job", () => {
|
|
for (const { jobs } of Object.values(ADMITTED)) {
|
|
for (const job of jobs) {
|
|
expect(BACKGROUND_REASONING_JOBS as readonly string[]).toContain(job);
|
|
}
|
|
}
|
|
});
|
|
});
|