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.
129 lines
4.0 KiB
TypeScript
129 lines
4.0 KiB
TypeScript
// The draft a session suggests, and the markdown an issue carries. Pure, so the composer's
|
|
// preview and what it saves are provably one computation.
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import type { ServerLogRow } from "./api";
|
|
import { draftSteps, osName, renderIssue, type DraftForm, type IssueText } from "./issueDraft";
|
|
|
|
let nextId = 1;
|
|
const row = (over: Partial<ServerLogRow>): ServerLogRow => ({
|
|
id: nextId++,
|
|
ts: "2026-09-19T14:05:31.000Z",
|
|
level: "INFO",
|
|
source: "web",
|
|
logger: "web",
|
|
event: "x.y",
|
|
msg: "",
|
|
attrs: null,
|
|
exc: null,
|
|
request_id: null,
|
|
user_id: 1,
|
|
job_id: null,
|
|
debug_session_id: "0123456789abcdef",
|
|
version: null,
|
|
...over,
|
|
});
|
|
const nav = (page: string) => row({ event: "nav.view", attrs: { via: "push", page } });
|
|
const step = (label?: string) =>
|
|
row({ event: "debug.step", attrs: label ? { n: 1, label } : { n: 1 } });
|
|
const pageName = (p: string) => p.toUpperCase();
|
|
const clock = () => "14:05:31";
|
|
|
|
describe("the steps a session suggests", () => {
|
|
it("are its markers and the page changes between them, in order", () => {
|
|
const steps = draftSteps(
|
|
[nav("plex"), nav("plex"), step("opened the player"), nav("settings")],
|
|
pageName,
|
|
clock,
|
|
);
|
|
expect(steps.map((s) => [s.kind, s.text])).toEqual([
|
|
["nav", "PLEX"], // the same page twice in a row is one step
|
|
["marker", "opened the player"],
|
|
["nav", "SETTINGS"],
|
|
]);
|
|
});
|
|
|
|
it("name a marker set without a label by its time, so the issue keeps it", () => {
|
|
expect(draftSteps([step()], pageName, clock)[0]?.text).toBe("14:05:31");
|
|
});
|
|
|
|
it("hang a screenshot on the step before it, and leave server rows out", () => {
|
|
const steps = draftSteps(
|
|
[
|
|
step("spinner"),
|
|
row({ event: "debug.screenshot" }),
|
|
row({ source: "api", event: "nav.view" }),
|
|
],
|
|
pageName,
|
|
clock,
|
|
);
|
|
expect(steps).toHaveLength(1);
|
|
expect(steps[0]?.shot).toBe(true);
|
|
});
|
|
});
|
|
|
|
const TEXT: IssueText = {
|
|
steps: "Steps",
|
|
environment: "Environment",
|
|
envKeys: { version: "Version" },
|
|
screenshot: "screenshot",
|
|
attachments: "Attachments",
|
|
clientLog: (n) => `${n} browser lines`,
|
|
serverLog: (n) => `${n} server lines`,
|
|
screenshots: (n) => `${n} screenshots`,
|
|
};
|
|
const form = (over: Partial<DraftForm> = {}): DraftForm => ({
|
|
title: "t",
|
|
description: "It spins.",
|
|
steps: [
|
|
{ id: "a", kind: "nav", text: "Plex" },
|
|
{ id: "b", kind: "marker", text: " ", shot: false },
|
|
{ id: "c", kind: "marker", text: "Resume", shot: true },
|
|
],
|
|
excluded: [],
|
|
...over,
|
|
});
|
|
|
|
describe("the issue text", () => {
|
|
it("carries the description, the non-empty steps, the environment and the attachments", () => {
|
|
const md = renderIssue(
|
|
form(),
|
|
[["version", "0.93.0 | beta"]],
|
|
{ client: 3, server: 0, screenshots: 1 },
|
|
TEXT,
|
|
);
|
|
expect(md).toBe(
|
|
[
|
|
"It spins.",
|
|
"### Steps\n\n1. Plex\n2. Resume _(screenshot)_",
|
|
"### Environment\n\n| | |\n|---|---|\n| Version | 0.93.0 \\| beta |",
|
|
"### Attachments\n\n- `client.log` — 3 browser lines\n- 1 screenshots",
|
|
].join("\n\n"),
|
|
);
|
|
});
|
|
|
|
it("says when the logs were cut", () => {
|
|
const md = renderIssue(
|
|
form(),
|
|
[],
|
|
{ client: 1, server: 1, screenshots: 0 },
|
|
{
|
|
...TEXT,
|
|
truncated: "cut at 5000",
|
|
},
|
|
);
|
|
expect(md.endsWith("_cut at 5000_")).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("the system a browser runs on", () => {
|
|
it.each([
|
|
["Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 Chrome/152.0.0.0 Mobile", "Android 10"],
|
|
["Mozilla/5.0 (iPad; CPU OS 26_6_1 like Mac OS X) AppleWebKit/605.1.15", "iPad 26.6.1"],
|
|
["Mozilla/5.0 (iPhone; CPU iPhone OS 18_2 like Mac OS X)", "iPhone 18.2"],
|
|
["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/152.0.0.0", "Windows NT 10.0"],
|
|
["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1.15", "macOS 10.15.7"],
|
|
["Mozilla/5.0 (X11; Linux x86_64) Firefox/140.0", "Linux"],
|
|
])("%s → %s", (ua, os) => expect(osName(ua)).toBe(os));
|
|
});
|