The code cited its plan instead of saying why: `E28 review J4`, `(R6 S3b)`, `S1 justified…`,
`deep-review round 5`. That plan lives in the private ops repo, so for every reader of this one the
citation was noise, and a sentence whose subject was the plan step ("round 2 showed…") hid its
lesson behind an id. Each one is gone and the "why" kept: a bare citation is deleted, a sentence
carrying a lesson is reworded around it ("a later attempt showed…").
Measured (`scripts/audit_metrics.py`, product code): 652 lines in 270 files carried an epic code by
the definition the next commit holds (507 / 241 by the old, two-digit-only one) -> 0. Across all
code, tests, scripts and configs: ~1100 epic-code lines plus ~320 sprint/round ids.
Behaviour unchanged, checked mechanically: every changed .py file has the same AST once docstrings
are blanked; css/sh/toml/yml/json files are identical once comments are stripped; the only
non-comment changes in TS/JS are test titles, one description string in a contract table, and five
ESLint messages, whose text is all the gates snapshot diff holds. Not touched: Markdown (docs and
the changelog keep their history), Alembic migrations, TV episode notation (`S2 · E5`), DESIGN.md
ids (`C1`, `B4`, `S3h`), tracker ids (`SEC-16`, `DX-17`).
One stale claim fixed on the way: test_hls_reaper's docstring said the player checkpoints "every
10 s whether or not it is playing", which plex/stream.py calls wrong; it now says what the code does.
75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
// docs/DESIGN.md "Copy": the parts of the copy rule a machine can read, held over every
|
|
// value in both languages. The rest (one name per feature, one verb per meaning, "…" when a command
|
|
// asks first) needs a reader and is checked by the DESIGN conformance step.
|
|
|
|
const files = import.meta.glob("./locales/*/*.json", { eager: true, import: "default" }) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
|
|
function values(): [string, string][] {
|
|
const out: [string, string][] = [];
|
|
const walk = (o: unknown, path: string) => {
|
|
if (typeof o === "string") out.push([path, o]);
|
|
else if (o && typeof o === "object")
|
|
for (const [k, v] of Object.entries(o)) walk(v, path ? `${path}.${k}` : k);
|
|
};
|
|
for (const [file, json] of Object.entries(files))
|
|
walk(json, file.replace("./locales/", "").replace(".json", ":"));
|
|
return out;
|
|
}
|
|
|
|
const offenders = (test: (v: string) => boolean) =>
|
|
values()
|
|
.filter(([, v]) => test(v))
|
|
.map(([k, v]) => `${k} = ${v}`);
|
|
|
|
describe("the copy rule", () => {
|
|
it("quotes a name with curly quotes, never straight ones", () => {
|
|
expect(offenders((v) => v.includes('"'))).toEqual([]);
|
|
});
|
|
|
|
it("writes an ellipsis as one character", () => {
|
|
expect(offenders((v) => v.includes("..."))).toEqual([]);
|
|
});
|
|
|
|
it("ends no message with a tick", () => {
|
|
expect(offenders((v) => v.includes("✓"))).toEqual([]);
|
|
});
|
|
|
|
it("spells no unit as a bare letter after a number ('90m', '24h', '{{count}}d', '10 s')", () => {
|
|
expect(offenders((v) => /(\d|\}\})\s?(m|h|d|s)(?![\p{L}\d/])/u.test(v))).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("one name per thing", () => {
|
|
// The options of one choice are told apart by their names alone: two playback classes once shared
|
|
// "Közvetlen lejátszás" in the HU filter. A set = a `…Opt` object, or one choice key's
|
|
// options under config.choices.
|
|
const sets = (): [string, Record<string, unknown>][] => {
|
|
const out: [string, Record<string, unknown>][] = [];
|
|
const walk = (o: unknown, path: string, parent: string) => {
|
|
if (!o || typeof o !== "object") return;
|
|
if (/Opt$/.test(path.split(".").pop() ?? "") || parent.endsWith("/config:.choices"))
|
|
out.push([path, o as Record<string, unknown>]);
|
|
for (const [k, v] of Object.entries(o)) walk(v, `${path}.${k}`, path);
|
|
};
|
|
for (const [file, json] of Object.entries(files))
|
|
walk(json, file.replace("./locales/", "").replace(".json", ":"), "");
|
|
return out;
|
|
};
|
|
|
|
it("names the options of one choice differently", () => {
|
|
const found = sets();
|
|
expect(found.some(([p]) => p.endsWith("playableOpt"))).toBe(true);
|
|
expect(found.some(([p]) => p.endsWith("choices.log_level"))).toBe(true);
|
|
const clashes = found.flatMap(([path, opts]) => {
|
|
const names = Object.values(opts).filter((v): v is string => typeof v === "string");
|
|
return names.length === new Set(names).size ? [] : [path];
|
|
});
|
|
expect(clashes).toEqual([]);
|
|
});
|
|
});
|