mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
Add a ratchet mechanism (helpers/ratchet.ts): per-key counts against a JSON
baseline under src/__tests__/baselines/. A new key or a higher count fails;
a lower count fails until it is written back with
`RATCHET_WRITE=1 pnpm vitest run <guard>`, which only ever lowers numbers.
Raising one is a hand edit in a reviewed change. The baselines are
machine-written, so prettier leaves them alone.
helpers/source-metrics.ts measures the production tree through the
TypeScript compiler: the file list (dot directories included, tests and
generated code out), line counts, every function node with a stable
"outer > inner" name and its length, and the runtime import graph.
Guards and their baselines, measured on this tree:
- source-size-ratchet: 129 files over 800 lines and 415 functions over 200
lines (catalog and data files exempt, each named with its reason; slack
25 and 10 lines before a shrink must be written back).
- test-realism-ratchet: 321 route tests mocking @/lib/db wholesale, 138
stubbing @/lib/api-handler, 6014 `as never` casts in 570 test files, and
400 test files that read a source file by path (reads through readSource
are not counted).
- version-stamp-ratchet: 10634 comment lines in 2985 files that open with a
release version; none may be added.
Proof each can fail: a planted 953-line file with a 253-line function failed
both size ratchets, and one line appended to app/page-client.tsx failed the
file ratchet (2326 → 2327); a planted route test with vi.mock("@/lib/db")
and one `as never` failed both of those ratchets; a planted comment
"// v1.45.0 — probe stamp" failed the stamp ratchet. Raising one as-never
baseline entry by 5 failed with the write-back message, and RATCHET_WRITE=1
restored the file byte for byte. All probes were removed again.
86 lines
3.2 KiB
TypeScript
86 lines
3.2 KiB
TypeScript
/**
|
|
* No new comment that opens with a release version.
|
|
*
|
|
* `// v1.4.39.1 — wipe the rollup partition … Pre-fix the partition kept …`
|
|
* is a history note: it describes a state of the code the reader cannot see,
|
|
* and it repeats what CHANGELOG and `git log` already hold. About ten
|
|
* thousand comment lines in the tree open that way. They stay until their
|
|
* file is split or rewritten anyway; a mass edit would be churn. New ones do
|
|
* not get added: say what the code does and why, and leave when it changed
|
|
* to the history.
|
|
*
|
|
* A comment line counts when, after its marker (`//`, `/*`, or the `*` of a
|
|
* block comment), it opens with `vMAJOR.MINOR[.PATCH…]`. A version later in
|
|
* the sentence ("since v1.39 the gate …") is not an opener and is not
|
|
* counted. The count is per file, against `baselines/version-stamp-comments.json`:
|
|
* a file that gains one fails, a new file with one fails, and a file that
|
|
* sheds some fails until the lower count is written back with
|
|
* `RATCHET_WRITE=1 pnpm vitest run src/__tests__/version-stamp-ratchet.test.ts`.
|
|
*
|
|
* Limit, stated: it reads lines, not the parse tree, so a line inside a
|
|
* template string that happens to start with `* v1.2` would count. None does
|
|
* today.
|
|
*/
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { requireFloor, walkSourceFiles } from "./helpers/source-files";
|
|
import { SRC, readSrc } from "./helpers/source-metrics";
|
|
import { runRatchet, writeBackHint } from "./helpers/ratchet";
|
|
|
|
const GUARD = "src/__tests__/version-stamp-ratchet.test.ts";
|
|
|
|
const STAMP_OPENER = /^\s*(?:\/\/+|\/\*+|\*+)\s*\(?v\d+\.\d+(?:\.\d+){0,3}\b/;
|
|
|
|
function stampCount(text: string): number {
|
|
let n = 0;
|
|
for (const line of text.split("\n")) if (STAMP_OPENER.test(line)) n += 1;
|
|
return n;
|
|
}
|
|
|
|
describe("version-stamp comments", () => {
|
|
const files = walkSourceFiles(SRC, { floor: 5000 }).filter(
|
|
(rel) => !rel.startsWith("generated/") && !rel.endsWith(".d.ts"),
|
|
);
|
|
const counts: Record<string, number> = {};
|
|
for (const rel of files) {
|
|
const n = stampCount(readSrc(rel));
|
|
if (n > 0) counts[rel] = n;
|
|
}
|
|
|
|
it("finds the stamps that are there", () => {
|
|
requireFloor("files with a version-stamp opener", Object.keys(counts), 500);
|
|
});
|
|
|
|
it("gains none, and records every one that goes", () => {
|
|
const r = runRatchet("version-stamp-comments.json", counts, {
|
|
comment: `Comment lines that open with a release version, per file under src/. Maintained by ${GUARD}.`,
|
|
});
|
|
expect(
|
|
r.grew,
|
|
"New comment lines opening with a release version. Say what the code does and why; when it changed belongs in CHANGELOG and git.",
|
|
).toEqual([]);
|
|
expect(r.stale, writeBackHint(GUARD)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("the opener matcher", () => {
|
|
it.each([
|
|
"// v1.4.39.1 — wipe the partition",
|
|
" // v1.23: notes are encrypted",
|
|
" * v1.37.19 (#219 parity) — per-slot units",
|
|
"/* v1.5.0 — one-shot backfill */",
|
|
"/** v1.16.12 — stale window widened */",
|
|
])("counts %s", (line) => {
|
|
expect(stampCount(line)).toBe(1);
|
|
});
|
|
|
|
it.each([
|
|
"// Since v1.39 every AI decision is one resolution",
|
|
"const v = 'v1.2.3';",
|
|
"// version 1.2",
|
|
"// ve1.2",
|
|
])("does not count %s", (line) => {
|
|
expect(stampCount(line)).toBe(0);
|
|
});
|
|
});
|