diff --git a/backend/app/downloads/edit.py b/backend/app/downloads/edit.py index 24498872..d191d14a 100644 --- a/backend/app/downloads/edit.py +++ b/backend/app/downloads/edit.py @@ -734,7 +734,7 @@ def _step_failure(err: BaseException) -> str: def _is_jpeg(path: Path) -> bool: - """True if the file starts with the JPEG SOI marker (FF D8 FF).""" + """True if the file starts with the JPEG SOI marker (0xFF 0xD8 0xFF).""" try: with path.open("rb") as f: return f.read(3) == b"\xff\xd8\xff" diff --git a/backend/app/routes/downloads.py b/backend/app/routes/downloads.py index cb9e3e23..d2e1c5d2 100644 --- a/backend/app/routes/downloads.py +++ b/backend/app/routes/downloads.py @@ -1033,7 +1033,7 @@ def list_links( class CreateLinkIn(BaseModel): password: str = "" - # D1: a newly shared download is downloadable by default — the common intent is "let the + # A newly shared download is downloadable by default — the common intent is "let the # recipient have the file", not stream-only. Forward-only: this only sets the default for links # minted from now on; existing links keep whatever `allow_download` they were created with (the # column default stays False, so no back-catalogue link silently flips). The owner can still diff --git a/backend/app/sync/tagsuggest.py b/backend/app/sync/tagsuggest.py index 14d0f7e6..a4e2d99e 100644 --- a/backend/app/sync/tagsuggest.py +++ b/backend/app/sync/tagsuggest.py @@ -12,7 +12,7 @@ result (measured on real data: every untagged channel got a suggestion, most on alone). Topic overlap is what makes "you tagged similar channels this way" meaningful. Purely read-only and deterministic — the Channel Manager surfaces the result as one-tap-accept -"ghost" chips; nothing is written here (D4 = suggest-and-confirm, no silent auto-apply). The +"ghost" chips; nothing is written here (suggest-and-confirm, never a silent auto-apply). The thresholds are tuning constants rather than admin knobs on purpose: they mirror how ``autotag.py`` keeps its mappings inline, and sysconfig only stores int/str/bool (no float), so a knob would mean storing percentages — not worth the surface for values an operator has no reason to tune per-install. diff --git a/backend/tests/test_links.py b/backend/tests/test_links.py index 78c18096..f3e8e47d 100644 --- a/backend/tests/test_links.py +++ b/backend/tests/test_links.py @@ -99,7 +99,7 @@ class TestPublicMetaAudioFlag: class TestCreateLinkDefault: - """D1: a newly minted share link is downloadable by default. The one-click Share relies on + """A newly minted share link is downloadable by default. The one-click Share relies on this — it POSTs no `allow_download`, so the API default decides. Forward-only lives here (the API default), NOT in a migration: the DownloadLink column default stays False, so no existing back- catalogue link silently flips to downloadable.""" diff --git a/frontend/src/components/PlexBrowse.tsx b/frontend/src/components/PlexBrowse.tsx index c65a1cf7..d8f9f0a6 100644 --- a/frontend/src/components/PlexBrowse.tsx +++ b/frontend/src/components/PlexBrowse.tsx @@ -68,7 +68,7 @@ const PlexCollectionView = lazy(() => import("./PlexCollectionView")); const PAGE = 40; -// D3: the "last watched" sort auto-applied when you enter the in-progress watch-state view (most +// The "last watched" sort auto-applied when you enter the in-progress watch-state view (most // recently watched first). Frozen for the same reason as PLEX_SORT_FALLBACK (handed out by reference). const LASTWATCHED_SORT: NonNullable = Object.freeze({ key: "lastwatched", @@ -229,7 +229,7 @@ export default function PlexBrowse() { return () => window.removeEventListener("keydown", onKey); }, [sub, canStepBack]); - // D3: entering the in-progress watch-state view auto-applies the "last watched" sort (most recently + // Entering the in-progress watch-state view auto-applies the "last watched" sort (most recently // watched first); leaving restores the sort that was active before entering. Only the ENTER edge // forces the sort, so a manual sort change while in the in-progress view still wins — it's just not // carried back out (exit returns to the pre-entry preference). We stash the raw provider `sort`, not diff --git a/frontend/src/components/PlexDiscover.dom.test.tsx b/frontend/src/components/PlexDiscover.dom.test.tsx index 63c4ace6..4dd6537a 100644 --- a/frontend/src/components/PlexDiscover.dom.test.tsx +++ b/frontend/src/components/PlexDiscover.dom.test.tsx @@ -273,7 +273,7 @@ describe("PlexDiscover", () => { }); // Every rail and the genre grid are lists the keyboard walks (lib/listNav), so `j` goes on -// from one to the next in page order (owner decision D4). +// from one to the next in page order (the owner's call). describe("PlexDiscover · keyboard", () => { it("registers every rail and the genre grid, and j walks on from rail to rail", async () => { plexDiscover.mockResolvedValue(fullPayload); diff --git a/frontend/src/components/ShareDialog.tsx b/frontend/src/components/ShareDialog.tsx index d9a9f76b..77453bdd 100644 --- a/frontend/src/components/ShareDialog.tsx +++ b/frontend/src/components/ShareDialog.tsx @@ -235,7 +235,7 @@ function LinkShare({ job }: { job: DownloadJob }) { queryFn: () => api.downloadLinks(job.id), }); const [creating, setCreating] = useState(false); - // D1: downloadable by default — matches the one-click Share (which relies on the backend + // Downloadable by default — matches the one-click Share (which relies on the backend // default) and the common "let them have the file" intent. The owner can uncheck it here. const [allowDownload, setAllowDownload] = useState(true); const [expiryDays, setExpiryDays] = useState(null); @@ -260,7 +260,7 @@ function LinkShare({ job }: { job: DownloadJob }) { }); // Open with a link already generated: sharing is almost always the goal, so mint a default - // (no password, no expiry, downloadable per D1) link the moment the dialog finds none — as if the + // (no password, no expiry, downloadable) link the moment the dialog finds none — as if the // user had pressed "New link" → "Create". All further controls (password, download toggle, expiry, // extra links, revoke) stay available on the created link and via the "New link" button. const autoTried = useRef(false); diff --git a/frontend/src/components/UpNextCard.tsx b/frontend/src/components/UpNextCard.tsx index 8b541533..72d15ece 100644 --- a/frontend/src/components/UpNextCard.tsx +++ b/frontend/src/components/UpNextCard.tsx @@ -10,7 +10,7 @@ import { Button } from "./ui/Button"; import { IconButton } from "./ui/IconButton"; // Up-next / end-card. Shown at episode end when a next item exists and the pref is on, INSTEAD -// of the old instant jump — so binge stays under the viewer's control (D6-A). Counts down ~10s then +// of the old instant jump — so binge stays under the viewer's control. Counts down ~10s then // advances; "Play now" advances immediately; Cancel/✕ dismisses and stays on the finished frame. It only // lives on the play-to-the-end path; credits-auto-skip advances earlier and never reaches this card. const COUNTDOWN_S = 10; diff --git a/frontend/src/components/plexCards.dom.test.tsx b/frontend/src/components/plexCards.dom.test.tsx index af05785c..6f508f8c 100644 --- a/frontend/src/components/plexCards.dom.test.tsx +++ b/frontend/src/components/plexCards.dom.test.tsx @@ -258,7 +258,7 @@ describe("EpisodeCard a11y", () => { }); // In a keyboard-walkable grid or rail the WHOLE card is the item (the ring goes round the -// poster and its title, D2) and the poster — the card's own role=button — is what Enter opens. +// poster and its title) and the poster — the card's own role=button — is what Enter opens. describe("Plex cards in a keyboard-walkable list", () => { const cases = [ [ diff --git a/frontend/src/lib/keymap.sequence.test.ts b/frontend/src/lib/keymap.sequence.test.ts index cf32f0d5..7d43cb1c 100644 --- a/frontend/src/lib/keymap.sequence.test.ts +++ b/frontend/src/lib/keymap.sequence.test.ts @@ -87,7 +87,7 @@ describe("two-step sequences", () => { expect(calls).toEqual(["goDownloads"]); }); - // No second step needs a modifier (D1), so a chord with one is the reader changing their mind: it + // No second step needs a modifier (see the page-level rows below), so a chord with one is the reader changing their mind: it // must reach its own row. Swallowed, `g` then Ctrl+K opened the browser's search, not the palette. it("lets a modifier chord abandon the sequence and match as itself", () => { const calls: string[] = []; @@ -148,7 +148,7 @@ describe("page-level scopes under a layer", () => { }); describe("the page-level rows", () => { - // Owner rule D1, enforced rather than commented: on the Hungarian layout the owner types on, a + // The owner's rule, enforced rather than commented: on the Hungarian layout the owner types on, a // chord with a modifier (or `/`, `?`, which ARE Shift chords there) is not a quick key. it("need no modifier and no Shift-only character", () => { for (const s of SHORTCUTS) { diff --git a/frontend/src/lib/planCodes.contract.test.ts b/frontend/src/lib/planCodes.contract.test.ts index 4e3cf420..aefb05ed 100644 --- a/frontend/src/lib/planCodes.contract.test.ts +++ b/frontend/src/lib/planCodes.contract.test.ts @@ -24,10 +24,10 @@ const ALLOWED = new Set([ // Anything else that looks like a code is one — `"""E30:`, `it("E36:`, `# S6:` and `S3's` included. const NOT_CODES = /\bS\d{1,2} ?·? ?E\d+|\btitle[=:] ?"[ES]\d{1,2}"|\bE4\/E7\/E9\b/g; const PLAN_CODES = [ - // an epic (`E28`, `E9.5`, `E28/E30`), an older roadmap item (`R14`), a sprint (`S6`, `S3e2`) or a - // phase (`P1`) — not `E2E`, not a ruff rule (`S110`), not `S3h`, the media-card decision - // docs/DESIGN.md is headed by - /\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2}|S(?!3h\b)\d{1,2}(?:[a-z]\d?)?|P\d)\b/, + // an epic (`E28`, `E9.5`, `E28/E30`), an older roadmap item (`R14`), a sprint (`S6`, `S3e2`), a + // phase (`P1`) or a plan's decision (`D1`, `D6-A`) — not `E2E`, not a ruff rule (`S110`), not + // `S3h`, the media-card decision docs/DESIGN.md is headed by + /\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2}|S(?!3h\b)\d{1,2}(?:[a-z]\d?)?|P\d|D\d{1,2})\b/, // a review round (`round 4`, `deep-review`) /\b[Rr]ound[ -]\d|deep-review/, ]; @@ -58,6 +58,9 @@ describe("plan codes", () => { '"""E30: the new sink.', 'it("E36: keeps x", () => {', "// both sides (E28/E30)", + "# D1: a newly shared download is downloadable by default", + "// binge stays under the viewer's control (D6-A)", + "// (owner decision D4)", "# the roadmap's R6/R7 pair", "// held since E9.5", "// (S6) moved it", @@ -79,6 +82,8 @@ describe("plan codes", () => { 'expect(btn.textContent).toContain("S2 · E1");', "// the S3h media card", '"S110", "S112",', + "# the JPEG SOI marker (0xFF 0xD8 0xFF)", + "// a 3D view", ]; expect(cited.filter((l) => !isPlanCode(l))).toEqual([]); expect(clean.filter(isPlanCode)).toEqual([]); diff --git a/scripts/audit_metrics.py b/scripts/audit_metrics.py index 5e86fe3b..7e250918 100644 --- a/scripts/audit_metrics.py +++ b/scripts/audit_metrics.py @@ -27,12 +27,13 @@ BIG_FILE = 800 BE_COMPLEXITY = 10 # ruff's C901 default FE_COMPLEXITY = 20 # the frontend's line -# planCodes.contract.test.ts's definition, copied: an epic, roadmap item, sprint or phase code, or a -# review round; NOT_CODES are its named exceptions, cut first (TV episode notation, a fixture's -# season or episode title, ruff's default `E4/E7/E9`). ASCII, because JavaScript's `\b` is. +# planCodes.contract.test.ts's definition, copied: an epic, roadmap item, sprint, phase or decision +# code, or a review round; NOT_CODES are its named exceptions, cut first (TV episode notation, a +# fixture's season or episode title, ruff's default `E4/E7/E9`). ASCII, because JavaScript's `\b` is. NOT_CODES = re.compile(r'\bS\d{1,2} ?·? ?E\d+|\btitle[=:] ?"[ES]\d{1,2}"|\bE4/E7/E9\b', re.ASCII) PLAN_CODE = re.compile( - r"\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2}|S(?!3h\b)\d{1,2}(?:[a-z]\d?)?|P\d)\b|\b[Rr]ound[ -]\d|deep-review", + r"\b(?:E\d{1,2}(?:\.\d+)?|R\d{1,2}|S(?!3h\b)\d{1,2}(?:[a-z]\d?)?|P\d|D\d{1,2})\b" + r"|\b[Rr]ound[ -]\d|deep-review", re.ASCII, ) NARRATION = re.compile(r"\b(used to|no longer)\b", re.IGNORECASE)