commit 7f7d14015a7acf7432aec0d1d9784b9f5d35d933
parent 5d4d2200921bb673425fca00631bfca322ff456b
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 8 Sep 2026 23:35:29 -0400
plans: every known unmount-the-log instance on the video page is closed
STATE.md carried the rule with "three pre-existing unfixed instances in
VideoPanel.tsx that no spec covers"; they are fixed, a fourth turned up in
PerFileTranscribeRow, and the transcode row was checked and is not one (its
condition is the file extension against a static format list). The suite line
for the new tip replaces the de-flake tip's, which is kept as the previous
number so the +3 is legible.
FACTS.md's "must not be unmounted" section now names the shape to copy rather
than listing what is broken: condition passed down, `ranHere` set inside the
trigger, two fixed slots with the keyed panel last, and StreamActionLog's
`disabled` fed the cleared condition so a panel kept for its log is not a
second Run button. Also the one fixture fact a future reader would otherwise
have to rediscover: the fake yt-dlp's app-extraction branch, matched on the
`source-media` output template, is what makes a persist run completable at all.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
3 files changed, 55 insertions(+), 14 deletions(-)
diff --git a/plans/FACTS.md b/plans/FACTS.md
@@ -3549,14 +3549,34 @@ fixed index); only the speaker panels were under a spec that could see it, which
they flaked — `attribution.spec.ts:346`, 1-2 runs in 10, for a batch that had finished in 65 ms
with every line written.
-**Dropped by a parent branch swap** — same effect, different mechanism, and `VideoPanel.tsx`
-has three instances of it, all pre-existing, none under a spec, so none of them flakes and all
-three are KNOWN AND UNFIXED (out of scope for `fae99f1`; the operator decides whether they are
-worth a commit). `SourceVideoSection` early-returns the persisted view once `savedVideo` is
-truthy (`:1060`), so the *Persist source video* log (`:1133`) is destroyed by the
-`router.refresh()` its own run fires. `IncompleteTranscriptBanner` (`:1494`) and
-`ShortAudioBanner` (`:1557`) are the same shape from the other side: the parent stops rendering
-them (`:237-255`) once the run clears the condition that drew them.
+**Dropped by a parent branch swap** — same effect, different mechanism. `VideoPanel.tsx` had
+four instances, and **every known instance on the video page is closed as of `12d1778`**
+(`c19098b` + follow-ups): `SourceVideoSection` early-returned the persisted view once
+`savedVideo` was truthy, `IncompleteTranscriptBanner` and `ShortAudioBanner` were dropped by
+the parent when the run cleared the condition that drew them, and `PerFileTranscribeRow`
+swapped its panel for "Transcribe disabled — transcript already present." when its own run
+wrote transcript.json (`hasTranscript` is server-derived at `VideoPanel.tsx:360`).
+
+**THE SHAPE TO COPY** is the same in all four and is what a fifth should be written as: the
+parent renders the component unconditionally and passes the CONDITION down; the component holds
+a `ranHere` flag set inside the `trigger` it hands `StreamActionLog`, returns null only while
+`!condition && !ranHere`, puts the message and the run panel in two FIXED slots (the panel last
+and keyed), and passes the now-cleared condition to `StreamActionLog`'s existing `disabled`
+prop — so the panel that stays for its log is not a second Run button
+(`VideoPanel.tsx:1085-1120` persist, `1550-1588` incomplete, `1638-1670` short-audio,
+`729-756` the transcribe row, at `12d1778`). A banner's
+`role="alert"` and warning aria-label are spread conditionally on that same condition, so the
+box that outlives the warning stops answering `getByLabel("incomplete transcript")` —
+`truncated-check.spec.ts:69` asserts exactly that. `PerFileTranscodeRow` (`:810` at `12d1778`)
+is NOT an instance: its `return null` depends on the file's extension against a static format
+list, which a transcode cannot change, and the rows are keyed by file name.
+
+The persist case needed a fixture: `editor/e2e/fixtures/bin/fake-ytdlp.mjs:680-725` (at
+`12d1778`) grows an app-extraction branch — the LAST branch checked, matched on the media
+output template naming `source-media` (only `plan.extractionMode === "app"` emits that), which
+writes `data/<id>/source-media.mp4` plus `metadata.info.json` when absent so
+`finalizeAppExtraction` has a container to extract and persist. Without it that invocation fell
+through to "unknown invocation", exit 2, and no pointer ever landed.
The full trace, and why five of the plan's hypotheses — H1's mechanism included — were all
killed by it, is in `plans/deflake-e2e.md`'s "4, as shipped".
diff --git a/plans/STATE.md b/plans/STATE.md
@@ -129,12 +129,28 @@ the spec asserts the log still says "1 done" AFTER the pill reads "current", whi
every run at `fea2996` (5/5) and green at the fix. `attribution.spec.ts --repeat-each 20`:
**20/20**. The durable rule — **a component carrying a `StreamActionLog` must not be UNMOUNTED
when the record its run produced lands**, whether by an unkeyed move or by a parent branch
-swap — is in `FACTS.md`, with three pre-existing unfixed instances in `VideoPanel.tsx` that no
-spec covers.
-
-**The last full suite: 515 passed, 0 failed of 515, 24.6 min** from a worktree of `65abc11`
-(the de-flake tip, one worker behind the queue lock). The four flakes are closed and the suite
-is clean end to end. Previously, at `2133d94`, it was 514/1 — slice 1.5's own new band
+swap — is in `FACTS.md`.
+
+**Those instances are now closed, and a fourth with them** (`c19098b`, follow-ups in
+`12d1778`; the specs that catch them in `b207e84`). `SourceVideoSection` early-returned the
+persisted view once the pointer landed; `IncompleteTranscriptBanner` and `ShortAudioBanner`
+were dropped by the parent when the run cleared their condition; and `PerFileTranscribeRow`
+swapped its panel for "Transcribe disabled — transcript already present." the moment its own
+run wrote transcript.json. All four are the same shape now: message and run panel in separate
+fixed slots, a `ranHere` flag set when a run starts there keeping the panel mounted across the
+refresh, and `StreamActionLog`'s existing `disabled` prop fed the now-cleared condition so a
+kept panel is not a second Run button. The banners' `role="alert"` and warning aria-label are
+conditional on the flag, so the box that outlives the warning does not keep announcing it.
+`PerFileTranscodeRow` was checked and is **not** an instance: its condition is the file's
+extension against a static format list, which a transcode cannot change. Each fix has an e2e
+case that reads the log's closing line only AFTER the state the run produced is on screen; all
+four are red without the fix and green with it, and `--repeat-each 10` over the four specs is
+340/340.
+
+**The last full suite: 518 passed, 0 failed of 518, 31.8 min** from a worktree of `12d1778`
+(one worker behind the queue lock). Previously **515/515 in 24.6 min at `65abc11`**, the
+de-flake tip; the three added cases are the difference. The four flakes are closed and the
+suite is clean end to end. Previously, at `2133d94`, it was 514/1 — slice 1.5's own new band
assertion, and it was right: writing the two new snapshot entries doubled `/channels`' Download
and Transcribe coverage, because that page passes the external ids into `buildOperationBands`
and `addRegistryEntry` folded them on top of `addExternalBands`. Fixed in `d8754d2`.
diff --git a/plans/deflake-e2e.md b/plans/deflake-e2e.md
@@ -182,6 +182,11 @@ Test: the spec asserts the panel STILL contains "1 done" AFTER the freshness pil
a second race. At `fea2996` that assertion is red **5/5** (`element(s) not found`); at
`fae99f1` `attribution.spec.ts --repeat-each 20` is **20/20**.
+**Follow-up:** the three `VideoPanel.tsx` instances this section left unfixed are closed in
+`c19098b` (specs `b207e84`), with a fourth found in `PerFileTranscribeRow` and the review's
+button/alert/timeout fixes in `12d1778` — same rule, the shape to copy is in `FACTS.md`. Full
+suite at that tip: **518/518**.
+
## Not changed, deliberately
- `reconcileVideoDirs` keeps mutating during snapshot generation; that is the product's