Helpers flush and handlers commit — but every test request shared the test's session, so a
follow-up GET or an assertion read the flushed write and the suite stayed green over a handler
whose commit was missing (production's get_db closes the session: the write is rolled back behind
a 200). The db fixture now hands out `db.info["request"]`: the same session, recording each request
that ends normally with a flushed, uncommitted write, and failing the test on it at teardown. All
28 get_db overrides use it (a convention test keeps it so); the full suite passes under it, and
dropping the commit from add_item turns a playlist test red.
Deep-review round 3 of bug hunt 2026-09.
- `failed` is now final only for a track that can never be text (bitmap, no item/track/file). A Plex
hiccup or a stretch ffmpeg could not read answers `unavailable`: said, polled again every 5 s, and
the <track> stays mounted when cues exist elsewhere (gen > 0). Re-picking the selected track asks
the server again at once.
- Within LOOKAHEAD_S of a covered stretch's end the next window is read ahead, instead of only once
the playhead has fallen off it (subtitles dropped out at every edge while the full pass was backed
off or slower than a window lasts).
- Windows and full passes run on separate pools with separate in-flight caps, so a window never
queues behind (or is refused by) minute-long full passes — the pool comment promised that and
nothing enforced it.
- A pass that times out is TERMinated and keeps what it reached (published as partial) instead of
being killed and discarded.
- Lapsed failure memos are swept on every write (they were only pruned when the same key was read
again, so polling fresh keys grew the process without bound).
- `publish` no longer merges into a sidecar with no VTT behind it, or claims a region whose VTT write
failed, or overwrites an unreadable track with one window.
- A NUL in a rating_key names no row (it was a DataError, a 500, on every Plex route) and no longer
500s the status poll.
Tests: 16 new, each mutation-checked (18 mutations, all red on the test that names them).
Deep-review of the bug-hunt round (R1). The background extractor from ed4ab4e1 held, but its seams
did not:
- Window + full pass doubled every cue in the first hour. merge_vtt de-duplicated on the raw timing
text, and the two passes spell a timestamp differently (shift_vtt writes HH:MM:SS.mmm, ffmpeg's
webvtt muxer drops the hour below 1 h; shift_vtt(-0.0) never normalised the full pass). Timing is
now parsed and re-emitted canonically, keyed with the cue text so two cues sharing a time survive.
- A full pass that exited non-zero was published as complete: "ready" forever, subtitles ending
where ffmpeg died, and no eviction to ever retry. It now covers only up to its last cue, is never
complete, and records the failure memo.
- The window's dedup identity held the live playhead, so every 1 s poll queued another read of the
file. It is keyed on the track now, and a queued job re-checks coverage before reading anything.
- A sidecar track waited behind an unrelated embedded full pass (the full job is per item). Its
window job now fetches it whole — one small request.
- The <track> fetch ran the scheduler at position 0: an unrequested window over the head of the file,
and a refusal of a window that was on disk. It is a pure read of the cache now.
- publish() is serialised (a lost update became permanent once complete was set); the pool is shut
down and running ffmpeg children killed at app shutdown (they held interpreter exit for minutes);
?at=inf is a 422, not a 500; and both subtitle routes use current_user instead of the cookie-only
poster gate, which let a suspended or epoch-revoked session keep scheduling reads (pre-existing).
_extract_and_publish finally has tests of its own — every earlier test stubbed it out. 19 mutations,
each red on the assertion that names it.