commit 50b404430af16a4ff0ac0a1d6ea5df60a751d25f
parent 8602b3c05676343a1ec9c3cb5ebd40821c72e1a3
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 02:23:22 -0400
plans: O3 review fixes recorded — the form's revalidate, sourceFetchFailure's order, persistKept, the Diagnostics wording
The record's Review fixes paragraph and commit table; the Diagnostics claim
corrected in the record, the editor changelog and FACTS (the fix reaches the
Download stage's three cards and the Transcribe stage's Fetch audio, not
Diagnostics, which drops an emptied card); the cut-release bullet names the
form. Gates on 8602b3c0: common 2,051, editor unit 85, scripts 175+1, mcp
269; e2e 28/28 in 1.3 min.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
3 files changed, 75 insertions(+), 19 deletions(-)
diff --git a/editor/CHANGELOG.md b/editor/CHANGELOG.md
@@ -4,8 +4,8 @@
- **The homepage can be built and deployed from `/sites`.** Under a new **Homepage** section, after Hub, there is **Build homepage** (tick **Deploy after build** to ship it in the same job, only if the build succeeds) and **Deploy homepage**, which ships the build already in `homepage/out`. A **Preview branch** box beside them sends either deploy to a Cloudflare Pages preview of the `archilyzer` project instead of production, and shows the preview's address as you type; a name Cloudflare would refuse or rewrite, or `main`, greys the deploy buttons out and says why. A line under the buttons says what a deploy would ship: when `homepage/out` was built (or that it holds no build yet), and where it goes, with the live URL. Deploy homepage with nothing built is refused before any job starts. The homepage reads the search index as it stands, so run **Build index** first when its numbers should move. The jobs run the same code as `archilyzer build homepage` / `deploy homepage`, and show on `/jobs` as `build-homepage`, `deploy-homepage` and `build-deploy-homepage`. The Hub section no longer describes the homepage.
- **`pnpm ops build-homepage` and `pnpm ops deploy-homepage`.** The same two jobs over HTTP: `build-homepage` takes `{"deploy": true}` to deploy after a successful build, and both take `{"preview": "<branch>"}` for a preview (`build-homepage` only with `deploy`). `deploy-homepage` answers with the preview's address, and refuses a bad preview name or a missing build before any job starts.
- **"Persist source video" or a whole-recording fetch that cannot get the source no longer marks the video's download failed.** When YouTube's subtitles came down but the source video did not, the video page said "Download failed" over a transcript that is fine. The download now keeps the subtitle pass's result and records only the failed media attempt, with yt-dlp's reason. The run itself now ends failed with that reason; it used to end done with no file, so `fetch_clip` could only say the job "finished but named no file". A partial source file is left for a retry to resume, and the run's log names it.
-- **A bucket's retry keeps its log when it empties the bucket.** On a channel's Download stage, "Download with cookies", the partial-download resume and the missing-transcript retry could lose their run log part-way: the video they fetched left the bucket, the page refreshed, and the card disappeared with the log in it. The card now stays, with its log and its button disabled, until the page is reloaded. Retry buttons elsewhere behave the same way.
-- **A release cut whose commit fails still refreshes the pages.** When the changelog's new heading was written but the commit after it failed, `pnpm ops cut-release` answered as if nothing had happened and no page showed the new heading until a reload. It now says the file was written and refreshes the changelog pages. Every refused cut's answer says whether anything was written (`untouched`), and a cut of both changelogs that stopped half-way names the one already cut as well as the failure.
+- **A bucket's retry keeps its log when it empties the bucket.** On a channel's Download stage, "Download with cookies", the partial-download resume and the missing-transcript retry could lose their run log part-way: the video they fetched left the bucket, the page refreshed, and the card disappeared with the log in it. The card now stays, with its log and its button disabled, until the page is reloaded. The Transcribe stage's "Fetch audio" button does the same. (A Diagnostics card still disappears, log and all, when its retry empties it.)
+- **A release cut whose commit fails still refreshes the pages.** When the changelog's new heading was written but the commit after it failed, the Cut release form and `pnpm ops cut-release` answered as if nothing had happened and no page showed the new heading until a reload. Both now refresh the changelog pages, and `pnpm ops cut-release` says the file was written. Every refused cut's answer says whether anything was written (`untouched`), and a cut of both changelogs that stopped half-way names the one already cut as well as the failure.
## [0.9.4] - 2026-09-28
- **On the Dark ground the sidebar's Archilyzer mark has a thin outline.** Its slate tile now has a 1-pixel ring just outside it, following its rounded corners, in the colour of the mark's unlit lines, so the tile's edge shows against the dark page. Light and Sepia are unchanged, and so is the favicon.
diff --git a/plans/FACTS.md b/plans/FACTS.md
@@ -3705,7 +3705,11 @@ a batch finishes, the pulse moves, and `AutoRefresh` refreshes the page MID-RUN.
above: `ranHere` set in the trigger (and `onRun` to tell the card), null only while
`ids.length === 0 && !ranHere`, `disabled={ids.length === 0}` (`RetryBucketControl.tsx:60-84`,
`DownloadStage.tsx:357,404,454`). `cookies-mode.spec.ts:241` waits for the refreshed EMPTY card
-(`needs cookies empty`), which makes the old race certain rather than likely.
+(`needs cookies empty`), which makes the old race certain rather than likely. The Transcribe
+stage's Fetch audio control is covered too (its section stays while `autoSubsCount > 0`). **NOT
+Diagnostics:** its two grids drop an emptied bucket's card before the control renders
+(`DiagnosticsStage.tsx:156` `populated`, `:694` `listed`), so a Diagnostics retry that empties its
+bucket still loses its log — open.
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
@@ -6818,13 +6822,15 @@ S4, as shipped"; `release-10.md` "Slice L2 / L1, as shipped". Every anchor below
`parseUnavailableFromStderr` that 410 is `deleted`: `rekietalaw-rumble/v7e07us` and
`the-quartering-rumble/v4vriou v4x5o1l v4yqask v501kfc`. A "Full-check unexpected" on those two
channels would settle them `deleted`.
-- **`sourceFetchFailure(record)`** (`common/ytdlp/downloadOneManaged.ts:528`) is how a caller that
- asked for the SOURCE tells whether it got it: the status for an outright failure (`failed`,
- `failed-corrupt-source`), else a failed last attempt of kind `no-subs-fallback`. A failed
+- **`sourceFetchFailure(record)`** (`common/ytdlp/downloadOneManaged.ts`) is how a caller that
+ asked for the SOURCE tells whether it got it. When the last attempt is the `no-subs-fallback`
+ media pass, ITS exit code alone answers (a fallback that persisted the container and whose inline
+ whisper then failed ends `failed`, and the source is there — review low 1); otherwise the status
+ (`failed`, `failed-corrupt-source`). Read by `archiveSourceVideo` (throws) and `persistKept`
+ (counts it `failed`, not `persisted`). A failed
`keepTranscript` pass (`:1503`) leaves the status alone and logs `forceMedia: the source download
failed (yt-dlp exit N); the transcript on disk is untouched and the download stays <status>.`,
- naming any `source-media.*.part` it leaves (left on purpose: a retry resumes it). `persistKept`
- does not read it yet (it still counts a returned download as persisted).
+ naming any `source-media.*.part` it leaves (left on purpose: a retry resumes it).
- **`cutReleases` always sets `untouched`** (`common/controller/cutRelease.ts`, `nothingWritten`
`:311`): true = no changelog was written (every refusal); false once any was, including a result
with **`written: true`** — a cut whose file was written and whose commit then failed (`:253`).
diff --git a/plans/release-11.md b/plans/release-11.md
@@ -216,7 +216,7 @@ found, and slice P's review lows L3 + L4. No settings, site or channel key; noth
mocked `state`s, so a new case there could not see the fix; it was re-run (9/9) as the contract
check that `state: "maybe_missing"` still draws "Missing?". The shared transcript pages carry no
`state` (found while writing the test).
-- **The real count, read-only** (`$T/o3-flip-count.ts`, `o3-flip-count.log`: reads
+- **The real count, read-only** (`o3-flip-count.ts` in the job scratch dir, `o3-flip-count.log`: reads
`maybe-missing.json`, `availability.json`, `download-outcome.json`, `metadata.info.json`'s
existence and `sites/*/site.json` through the build's own loaders; opens no LMDB, writes nothing):
1,827 maybe-missing ids in 31 channels; **5 videos across 2 channels move from available to
@@ -260,9 +260,13 @@ found, and slice P's review lows L3 + L4. No settings, site or channel key; noth
optional `onRun` so its card knows), null only while `ids.length === 0 && !ranHere`, and
`disabled={ids.length === 0}` so the surviving panel is not a second Run button. Applied to all
three bucket cards on the Download stage (`NoTranscriptList`, `PartialDownloadsList`,
- `NeedsCookiesList`, same hazard) and to `RetryBucketControl` itself, so the Diagnostics buckets'
- retry controls keep their log too. A reload drops an empty card as before (the reload check at
- the end of the same test still passes); labels and test ids unchanged.
+ `NeedsCookiesList`, same hazard) and to `RetryBucketControl` itself. Beyond the Download stage
+ it reaches one more control: the Transcribe stage's **Fetch audio** (its section stays mounted
+ while `autoSubsCount > 0`, and a fetch moves videos from "Needs audio" to "Ready to transcribe").
+ It does NOT reach Diagnostics: both of its grids drop an emptied bucket's whole card
+ (`DiagnosticsStage.tsx:156` `populated`, `:694` `listed`), control and log with it (corrected in
+ review; see Review fixes). A reload drops an empty card as before (the reload check at the end of
+ the same test still passes); labels and test ids unchanged.
- **Made deterministic.** The spec now waits for the refreshed, EMPTY card (`needs cookies empty`,
which renders only inside a card that survived the refresh) and asserts the log's last line and a
disabled `Download with cookies (0)` in it. On the old code this fails every time, at that line
@@ -282,9 +286,9 @@ found, and slice P's review lows L3 + L4. No settings, site or channel key; noth
400s and carry no `untouched`, as before.
- The CLI (`common/bin/release.ts`) is unchanged: its per-line output already named the earlier cut
and says "failed — Cut release X, but the commit failed" for L3.
-- **The `/sites` form** (`editor/app/sites/lib/cutReleaseAction.ts`) does not yet revalidate on
- `written: true`: it is under `editor/app/sites/**`, which slice O4 owns tonight, so that one-line
- change waits for O4's merge (WAITING_FOR_O4).
+- **The `/sites` form** (`editor/app/sites/lib/cutReleaseAction.ts`) revalidates on `written: true`
+ too. It is under `editor/app/sites/**`, which slice O4 owned tonight, so it landed after O4's
+ merge (Review fixes).
| sha | what |
|---|---|
@@ -332,18 +336,64 @@ found, and slice P's review lows L3 + L4. No settings, site or channel key; noth
marked the download failed). Restored with `git checkout HEAD --`.
**Found and left.**
-- **`persistKept` still counts a returned download as persisted** (pre-existing, N's record). It
- could read `sourceFetchFailure`; not this slice's file.
+- ~~`persistKept` still counts a returned download as persisted~~: done in the review fixes.
- **L3/L4 have no HTTP e2e for the half-way cases.** The fixture server's changelogs are gitignored
copies, so a commit there fails only after the dirty-tree guard has run git in the WORKTREE, and a
failed write needs a read-only directory the server's path cannot be pointed at. The controller's
tests drive both in temp repos (a refusing pre-commit hook; a `0555` directory); the spec pins
`untouched: true` on a refusal.
-- **A used retry control on the Diagnostics buckets** now stays (disabled, with its log) after its
- run empties the bucket, until a reload. Intended, and the same rule; noted because it is visible.
+- **A Diagnostics card after its run.** A Diagnostics retry that empties its bucket still loses
+ its card and its log: `DiagnosticsStage.tsx:156` / `:694` filter emptied buckets out before the
+ control renders. The follow-up is to lift a ran-bucket set into both sections and keep those
+ cards in `populated` / `listed`.
- **The five 410 videos** are published as available today and will read "Missing?" after the next
build; a Full-check unexpected would make them Deleted.
+**Review fixes** (review SHIP AFTER FIXES, `o3-review.md` in the job scratch dir: one should-fix, wording only; three
+lows; one nit; the three questions ruled — keep `archiveSourceVideo`'s failure broad, staying
+mounted is fine as a rule, keep `untouched` optional tonight).
+- **Merge.** `export/.next/dev` removed first (run C's export dev server had left stale types there,
+ so `tsc` failed in `export/` — low 3, environment only), then `git merge main` at `baaa4b47` (O4
+ merged). Conflicts only in `editor/CHANGELOG.md` (one `[Unreleased]`: O4's two bullets, then
+ O3's three) and this file's Record (O4's section, then O3's).
+- **The deferred `/sites` form change.** `cutReleaseAction` revalidates when the result is
+ `written: true`, then returns the error as before. The reviewer read it as correct and complete;
+ no e2e can make the fixture server's commit fail after the write, so tsc and the controller's
+ tests cover it, and `cut-release.spec.ts` re-ran green.
+- **should-fix — the Diagnostics claim was false.** The record and the changelog said the Diagnostics
+ buckets' retry controls keep their log; both Diagnostics grids drop an emptied bucket's card, so
+ they do not. Item 3 above, the editor bullet ("The Transcribe stage's "Fetch audio" button does the
+ same. (A Diagnostics card still disappears…)"), FACTS and found-and-left now say exactly what is
+ covered: the Download stage's three cards and the Transcribe stage's Fetch audio.
+- **low 1 — `sourceFetchFailure`'s order.** A no-subs fallback that fetched and persisted the source
+ and whose inline whisper then failed ends the download `failed`; reading the status first called
+ that source "not downloaded". When the last attempt is the media pass, its exit code alone
+ answers now; otherwise the status. Latent here (`inlineTranscribeOnFallback` is off on the
+ primary).
+- **low 2 — `persistKept`.** It counted every returned download as `persisted`; it now reads
+ `sourceFetchFailure` and counts a failed forced pass as `failed`, with the reason in its log.
+- **nit.** The inert `key="retry"` on the Download stage's three retry controls is gone.
+
+| sha | what |
+|---|---|
+| `0c770285` | merge `main` (`baaa4b47`, O4): the changelog and Record conflicts |
+| `f62d00f1` | the Cut release form revalidates on `written: true` |
+| `e9947421` | low 1: `sourceFetchFailure` reads a media-pass last attempt first; `forceMedia.test.ts`'s table +1 row |
+| `315c76e1` | low 2: `persistKept` counts a failed forced pass as failed; `persistKeptForceMedia.test.ts` +1 (setup made one helper with a process-wide settings file) |
+| `5c9987d2` | nit: the inert keys dropped |
+| _this_ | `plans:` these fixes, the corrected wording (record, changelog, FACTS) |
+
+**Gates on `5c9987d2`:**
+- tsc clean after the merge (`o3-tsc5.log`) and on the fixes (`o3-tsc6.log`); every fix commit is a
+ disjoint set of files from that tree.
+- common **2,051/2,051** (2,041 on `main` after O4, + O3's 10), editor unit **85/85**,
+ `test:scripts` **175 + 1 skip of 176**, mcp **269/269** (`o3-round2.log`).
+- e2e `cut-release ops-cut-release persist-youtube-handling fetch-window saved-videos cookies-mode`
+ (`o3-e2e-specs2.txt`, `o3-e2e2-full.log`): **28 passed, 0 failed, 1.3 min**, no queue wait.
+- **They bite** (`o3-bite3.log`): with the old order in `sourceFetchFailure`, the table test fails on
+ the new row; with `persistKept` counting every return, the new persistKept test fails; the other
+ 8 in the two files pass.
+
## Rollout
Nothing is rolled out tonight. The morning runbook lists what is owed: the :3001 editor restart,