commit 58569da848f69b3b3ba75c0c6b4a0239d687f3b8
parent 92cc9d6d94a6ff1c069d9109091b6b7e8a6e1750
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sat, 26 Sep 2026 14:23:45 -0400
plans: release 10 integration (S4 + L1 + L2) as merged — the combined tree's gates (tsc; common 1,954, editor unit 85, scripts 173 + 1, mcp 219; five builds with the icon checks; e2e export 204, hub 24, 2origin 3 with the export/public links in place and the primary byte-identical, homepage 27, umtool 175 + the known mix.spec pair, editor 634 + 6 load flakes, 18/18 alone), the Jeralyzer early deploy, STATE's "Merged to main, NOT rolled out" block, the Rollout re-cut and the optional umtool step
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
3 files changed, 322 insertions(+), 100 deletions(-)
diff --git a/plans/STATE.md b/plans/STATE.md
@@ -3,90 +3,109 @@
The working memory for the local-AI derived-corpus work. Rewritten at the end of every
session, before context is cleared. See [`README.md`](README.md) for the protocol.
-**Now (2026-09-26, just after midnight): the brand is merged to `main` and NOT rolled out.** `main` =
-`85cf6e81` + the S3 review-fix `plans:` record (review: SHIP AFTER FIXES, the fixes were in the
-runbook). It is release 10's first content, the Found-line mark and base × accent reader themes: S0
-`7c9e3bdf`, S1 `575ae1d4`, S2 `b9772e53`, then S3 (integrate: review carry-overs, a coherence pass, the
-full gates, the records, the runbook) fast-forwarded. Record:
-[`release-10.md`](release-10.md); the plan and the per-slice records:
-[`brand-and-themes.md`](brand-and-themes.md).
-- **The live :3001 editor still runs the pre-brand build**, `0213f6c8` / `BUILD_ID`
- `P0VMdKX7gbdsaiS5GvorF` (umtool `a9YAe_dwhuM6DiCPzIwMD`, untouched; the brand does not touch
- umtool). The five sites, the hub and the homepage still serve pre-brand builds (below).
-- **Next action: the operator's rollout, per `~/reports/release-10/RUNBOOK.html`** (`make-runbook.py`
- beside it; the rollout scripts in `scripts/`). In order: cut the export `[Unreleased]` notes (they
- are public on every site's `/changelog`); restart :3001 on the new `main` (one editor restart, md5
- before / pre-restart / after-boot, smoke); set each site's accent + wordmark lead in the site form;
- preview Anilyzer (`https://brand.anilyzer.pages.dev`) and check it on a phone; then the five sites
- by name, the hub, the homepage.
-- **Archilyzer Media (the YouTube channel) awaits the operator's picks** on the canvas
- (https://claude.ai/artifact/UsUxwgRkP5a3m4jZXucAvG): mark M1 bone / **M2 Signal**, lockup inline /
- **imprint** (recommended in bold). Then slice S4 (`brand-and-themes.md` "Proposed"); it needs
- `ttf-ibm-plex` and Archivo installed as system fonts.
-- Worktrees: `brand-mark` and `brand-themes` are merged and can go; `brand-found-line` once `main`
- carries its tip. Removing any of them re-sorts the port blocks (`diet-series` moves up).
-
-**Parked for after the brand rollout (2026-09-26, all Opus-reviewed to SHIP; none merged):**
-- **Brand S4 (Archilyzer Media)**, `brand/media` @ `333d2826`, worktree `../brand-media`:
- - `common/lib/brandMedia.ts` builds the lockup with its text as glyph outlines;
- - `common/bin/brand-media.ts` writes the YouTube assets. They are already generated in
- `~/reports/archilyzer-media/brand/` and can be uploaded now; see `INDEX.html` there
- (Customization → Profile);
- - the opt-in umtool report-to-video preset `render.brand: "archilyzer-media"`, with its fonts
- vendored (OFL) under `umtool/report-to-video/fonts/`. Manifests that don't opt in render
- byte-identical (334 files checked).
-- **L1, hub lows**, `r10/hub-lows` @ `b6b47ec1`, worktree `../r10-hub-lows`:
- - subs 404 = an empty manifest, with one retry that never fails the archive;
- - `/ask` honours the scope chips;
- - the official-instance accent fallback, and one card order (the homepage's);
- - the playwright config and `compose-hub`/`compose-site` never write through `export/public`
- links (`common/bin/_publicFile.ts`).
-- **L2, runner lows**, `r10/runner-lows` @ `14e96739`, worktree `../r10-runner-lows`:
- - YouTube's "try again later" soft block reads as `rate_limit` and backs off: download batch,
- runner, scan, and a prefetch early exit;
- - the availability check stops on it, and `verifyBeforeClean` marks every unprobed suspect
- `unverified`;
- - the boot pass waits at most 60 s for the storage pass;
- - `/jobs` shows `cancelReason`;
- - the safeRevalidate warning is counted.
-- **Merge plan.** Merge in the order S4 → L2 → L1. The code merges clean. `plans/release-10.md`
- conflicts on the record sections only: keep every section. At that merge:
- - update FACTS to retire the `sw.js`-copy and compose-output-swap workarounds; the per-path
- seed of the `export/public` links stays;
- - L1 adds public `export/CHANGELOG.md` `[Unreleased]` bullets, so cut before the next site
- deploy;
- - the live umtool needs a rebuild and restart before its new-project form offers the brand.
-- **Copy rulings owed by the operator:**
- - `NO_ARCHIVES_IN_SCOPE` (`export/app/ask/hubScopeCopy.ts`, draft "No archives selected.
- Choose some on the [hub's front page] to ask.");
- - whether `/ask` shows a "Searching N of M archives" scope line.
-- **New lows:**
- - `/ask` waits forever on a hub with zero archives;
+**Now (2026-09-26, afternoon): release 10 is merged to `main` in full and NOT rolled out.** It is
+the brand (S0–S3: the Found-line mark and base × accent reader themes; its own final commit
+`bb6f378f`, then cut as editor + export 0.9.0 at 01:37, `0e0d8f59` + `31798769`), plus brand S4 and
+the lows L1 + L2. Those three merged 2026-09-26 12:52 on the operator's word, before the rollout,
+and the integration pass gated them together. Record: [`release-10.md`](release-10.md) (the L2
+and L1 records, then "Integration (S4 + L1 + L2), as merged"); the brand's plan and per-slice
+records, S4's included: [`brand-and-themes.md`](brand-and-themes.md).
+
+**Merged to main (2026-09-26), NOT rolled out:**
+- **The merges**, in the planned order: S4 `dcb04f61` (`brand/media` @ `333d2826`) → L2 `41ddc382`
+ (`r10/runner-lows` @ `14e96739`) → L1 `5c0a6ef9` (`r10/hub-lows` @ `b6b47ec1`). The code merged
+ clean; the conflicts were `plans/release-10.md` (the record sections, all kept) and one
+ `editor/CHANGELOG.md` `[Unreleased]` join (S4's bullet and L2's four in one section above
+ `[0.9.0]`).
+- **Integration tip:** `brand/found-line` = `main` `4ac32a2e` + the integration `plans:` commits
+ (FACTS `9e3047e3`, then this STATE and the record). `4ac32a2e` is `5c0a6ef9` plus the plans-only
+ `plans/mcp-fetch-clip.md`, committed to `main` during the pass; the integration commits were
+ rebased onto it. No code changed at integration. The parent fast-forwards `main` to the tip, and
+ the runbook's `FINAL` is that tip.
+- **What is in it:**
+ - **S4, Archilyzer Media:** `common/lib/brandMedia.ts` (the lockup with its letters as glyph
+ outlines); `common/bin/brand-media.ts` (the YouTube assets, already generated in
+ `~/reports/archilyzer-media/brand/`, with `INDEX.html` there; upload any time); umtool
+ report-to-video's opt-in `render.brand: "archilyzer-media"` with vendored OFL fonts (a manifest
+ without it renders byte-identical, 334 files checked); and the brand choice in umtool's
+ new-project form.
+ - **L1, hub:** subs 404 = an empty manifest, one retry, never fails an archive; `/ask` honours
+ the scope chips (none in scope: disabled + one line); the official instances in the homepage's
+ order and colours; the playwright config and the compose scripts never write through
+ `export/public` links (`common/bin/_publicFile.ts`; the FACTS workarounds are retired).
+ - **L2, runner:** YouTube's "try again later" soft block backs off (batch, runner, scan, a
+ prefetch early exit; the availability check stops); `verifyBeforeClean` never judges an
+ unprobed suspect; the boot pass waits at most 60 s for the storage pass; `/jobs` shows
+ `cancelReason`; the safeRevalidate warning is counted.
+- **Gates on the merged tree** (`5c0a6ef9`; every number in the record):
+ - tsc clean;
+ - common 1,954, editor unit 85, `test:scripts` 173 + 1 skip (the report-to-video tests
+ included), mcp 219;
+ - editor, export site + hub, homepage and umtool builds ok, the icon checks green;
+ - e2e: export 204, `e2e:hub` 24, `e2e:2origin` 3 (the `export/public` links left in place: the
+ primary's `export/public` and a `sw.js` sentinel byte-identical throughout), homepage 27;
+ - umtool 175 + 2 failed + 45 skipped (the known order-dependent `mix.spec` pair; alone 6/6);
+ - editor full 634 + 6 failed + 12 skipped in 54.6 min, under the live editor's load. All six are
+ flakes (fixture EEXIST, the pulse timing tripwire, two sync-deep stamp races, a 7 s task timer,
+ the diarization snapshot poll); alone ×3: 18/18. No integration regression.
+- **Next action: the operator re-cuts the editor + export `[Unreleased]` notes, then the rollout
+ per `~/reports/release-10/RUNBOOK.html`** (`make-runbook.py` beside it; the scripts in
+ `scripts/`). Both changelogs have a new `[Unreleased]` above the 0.9.0 cut. Export's (L1's three
+ hub bullets) is public on every site's `/changelog` from the next site build; the editor's holds
+ S4's and L2's. Cut on `/sites` → Release notes with "Commit changelog" unticked whenever the
+ primary's tree is dirty (it refuses one; at the 0.9.0 cut an untracked
+ `settings.json.pre-priority-*` made it so, and the tree was clean at this pass), then commit both
+ files by hand.
+ Then, per the runbook:
+ 1. restart :3001 on the new `main` (one editor restart; md5 before / pre-restart / after-boot;
+ smoke);
+ 2. optionally, rebuild and restart umtool on :3050 with `r10-umtool.sh`, for the form's brand
+ choice;
+ 3. set each site's accent and wordmark lead;
+ 4. preview Anilyzer;
+ 5. deploy the five sites by name, then the hub, then the homepage.
+- **Jeralyzer already serves the brand, in Signal.** A build-deploy on the live editor built
+ Jeralyzer from `main` @ `386ac995` and deployed it to production (job `01M3F36N7SCKGC5JV27EBDFYPN`,
+ 2026-09-26 10:51–11:06, no agent recorded). That tree was the brand plus the 0.9.0 notes, before
+ S4/L1/L2. Its accent is unset, so it wears Signal until the runbook's step 4 (every site by name) rebuilds
+ it in Brass.
+ The same build left the primary's `export/public` as a Jeralyzer site compose (no hub `sw.js`,
+ `hub-sites.json` or `hub-summary.json`).
+- **umtool is no longer untouched.** Its report-to-video scripts changed on disk, and the live :3050
+ already spawns them (it still runs release 8's build, `a9YAe_dwhuM6DiCPzIwMD`). That is safe: a
+ render that does not opt in is byte-identical. Its app changed only for the new-project form's
+ brand choice, which appears after the optional `r10-umtool.sh` rebuild and restart.
+- **Copy rulings still owed by the operator:**
+ - `NO_ARCHIVES_IN_SCOPE` (`export/app/ask/hubScopeCopy.ts`). The draft is "No archives selected.
+ Choose some on the [hub's front page] to ask.", with the bracketed words a link to `/`. It
+ ships as drafted unless changed.
+ - Whether `/ask` shows a "Searching N of M archives" scope line.
+- **New lows (not started):**
+ - `/ask` waits forever ("Loading transcripts…") on a hub with zero archives;
- the chip doesn't show a member's missing live chat (it needs a subs-only Retry);
- `resolveMaybeMissingState` reads an `error` probe newer than the scan as `available`
- (display only);
- - umtool's rail, ledger and chart are still on Fira Sans, and a bold Plex Mono is not vendored.
+ (display only: the published presence badge);
+ - umtool's rail, ledger and chart text stays Fira Sans under the brand;
+ - no bold IBM Plex Mono is vendored (the HyperFrames band's `fontBold`).
+- **Worktrees that can go:** `brand-mark`, `brand-themes`, `brand-media`, `r10-hub-lows` and
+ `r10-runner-lows` are all merged. `brand-found-line` can go once `main` carries its tip. Removing
+ any of them re-sorts the port blocks (`diet-series` moves up).
**Live on :3001 — unchanged since 2026-09-25, 19:40:** `0213f6c8` (release 9 slice F + hub C1),
-`BUILD_ID` `P0VMdKX7gbdsaiS5GvorF`; umtool untouched (`a9YAe_dwhuM6DiCPzIwMD`). Live sites:
+`BUILD_ID` `P0VMdKX7gbdsaiS5GvorF`; umtool's build untouched (`a9YAe_dwhuM6DiCPzIwMD`; its
+report-to-video scripts on disk are `main`'s, above). Live sites:
- https://archilyzer-hub.pages.dev — C1 + C1b + C2 (archilyzer theme, "Official Instances" with
the homepage's figures, scope chips, per-archive state, "N of M archives answered", archive named
on every card, bare Ko-fi footer link), deploy `6ef7f472` (20:50);
- https://archilyzer.pages.dev — the refresh with the copy rulings and the hub link ON (C3),
deploy `e09900af` (20:51);
-- the five official sites at release 8's slices Z + E (17:45 / 18:10 deploys); they carry no Ko-fi.
-
-**Release 10 candidates (found 2026-09-25 evening, none started; any may join release 10 before its
-rollout):** `retry: false` on the hub's subs query (a member with no subs corpus is ready ~1 s late);
-`/ask` on the hub honours the scope chips (`useHubScope().isOn` in `AskHub`); official-site accents
-on the hub (shared `seriesColor` fallback for result stripes + chip dots, needs `hub-summary.json` —
-setting the accents at rollout already fixes the cards' stripes); the hub orders its cards
-alphabetically, the homepage by transcripts; `export/playwright.config.ts` must unlink
-`public/sw.js` before copying (a worktree run overwrites the primary's hub SW); YouTube's soft block
-"try again later" classifies `deleted`/per_video so it never backs off; the boot pass's wait on the
-storage pass has no timeout; `/jobs` does not show `cancelReason`; the safeRevalidate warning is once
-per bundle. Then Phase 4 slice 3 (`plans/one-core.md`), the LM chat-only tier config, the
-`Dockerfile.build` image refresh.
+- https://jeralyzer.pages.dev — **the brand, in Signal**, deploy `35593886` (2026-09-26 11:06, above);
+- the other four official sites at release 8's slices Z + E (17:45 / 18:10 deploys); they carry no
+ Ko-fi.
+
+**The release 10 candidates of 2026-09-25 evening are all done** (L1 and L2 above). Next after
+release 10's rollout: Phase 4 slice 3 (`plans/one-core.md`), the LM chat-only tier config, the
+`Dockerfile.build` image refresh, and the new lows above.
**The 2026-09-25 evening plan is complete** (steps A–C: release 9 F + C1 live on :3001; C1b, C2, C3
on the hub and the homepage). The operator's rulings (C2's federated search as proposed; a bare Ko-fi
diff --git a/plans/brand-and-themes.md b/plans/brand-and-themes.md
@@ -1549,3 +1549,7 @@ rail/ledger/chart on Fira, no bold Plex Mono yet — stand).
different**. The branded fixture rebuilt (same 3,631,785-byte cut). The real assets were
regenerated: every PNG and SVG byte-identical to the first run, only `INDEX.html` changed. No e2e:
the one umtool app line is a `neededBy` label no spec reads.
+
+**Merged** to `main` 2026-09-26 as `dcb04f61` (S4, then L2, then L1), and gated with L1 and L2 on the
+merged tree: [`release-10.md`](release-10.md), "Integration (S4 + L1 + L2), as merged". Its rollout step
+is the optional umtool rebuild (`~/reports/release-10/scripts/r10-umtool.sh`).
diff --git a/plans/release-10.md b/plans/release-10.md
@@ -9,11 +9,10 @@ families retire. The plan, the design decisions and the per-slice records are
[`brand-and-themes.md`](brand-and-themes.md); this file records the release as a whole. Rules:
`plans/tools/implementer-rules.md`.
-**The release-10 "lows" in `STATE.md` are still unstarted** (`retry: false` on the hub's subs
-query, `/ask` honouring the hub's scope chips, the shared `shelfAccent` fallback, the hub's card
-order, `playwright.config.ts` unlinking `public/sw.js` before its copy, the YouTube soft-block
-classification, the boot pass's storage wait, `cancelReason` on `/jobs`, the safeRevalidate
-warning). Any of them may join this release before it is rolled out.
+**The release-10 "lows" joined the release** (2026-09-26): they shipped as slices L1 (hub) and L2
+(runner), and merged to `main` with brand S4 before the rollout, on the operator's word. Their
+records and the integration pass that gated the three together are below ("Slice L2", "Slice L1",
+"Integration (S4 + L1 + L2), as merged").
## Record
@@ -222,7 +221,11 @@ safeRevalidate helper.
- **L2:** the editor `next build`, and the editor e2e specs for `/jobs`, boot and downloads (the
full editor suite runs at integration). Unit tests for the classification (6) and the timeout (7).
-**S4 — Archilyzer Media** (the YouTube channel's assets and umtool's opt-in `render.brand` preset) merges after the release-10 cut, from `brand/media`, and is **not part of the site rollout**: it touches only `common/lib` + `common/bin` and umtool; see `brand-and-themes.md`, "Slice S4, as shipped".
+**S4 — Archilyzer Media** (the YouTube channel's assets and umtool's opt-in `render.brand` preset)
+merges after the release-10 cut, from `brand/media`, and is **not part of the site rollout**: it
+touches only `common/lib` + `common/bin` and umtool; see `brand-and-themes.md`, "Slice S4, as
+shipped". (Merged 2026-09-26 as `dcb04f61`; its one rollout step is the optional umtool rebuild,
+"Rollout" 2b.)
### Slice L2, as shipped — runner lows (2026-09-26)
@@ -761,27 +764,215 @@ its own file, and the next seed relinks it.
`ready` with no sign that its live chat is missing. A chip note with a subs-only Retry would show
it.
+### Integration (S4 + L1 + L2), as merged (2026-09-26)
+
+The operator asked for S4, L1 and L2 on `main` now, before the brand rollout, and will re-cut the
+editor and export release notes before deploying. Each slice was reviewed to SHIP on its own branch;
+they had never run together. The parent merged them in the planned order, and this pass gated the
+combined tree, wrote the records and updated the runbook. Worktree `/home/user/Projects/brand-found-line`,
+branch `brand/found-line` fast-forwarded to `main` `5c0a6ef9`, port block #1. **No code changed at
+integration**: every gate below ran on `5c0a6ef9`, and the commits after it are `plans:` only. During
+the pass `main` gained `4ac32a2e` (`plans/mcp-fetch-clip.md`, plans-only), and the integration commits
+were rebased onto it so `main` can fast-forward.
+
+| merge | branch → tip | review | on `main` |
+|---|---|---|---|
+| **S4** Archilyzer Media | `brand/media` → `333d2826` | SHIP AFTER FIXES → re-read SHIP | `dcb04f61` |
+| **L2** runner lows | `r10/runner-lows` → `14e96739` | SHIP AFTER FIXES → re-read fix `52bb00ef` | `41ddc382` |
+| **L1** hub lows | `r10/hub-lows` → `b6b47ec1` | SHIP AFTER FIXES, twice (re-read `d0fa13ae`, `0fc63ed7`) | `5c0a6ef9` |
+
+**Conflicts resolved at merge** (`git show --remerge-diff`): `plans/release-10.md` in all three (the
+record sections; every section kept, S4's line, then L2's record, then L1's), and one
+`editor/CHANGELOG.md` `[Unreleased]` join in L2's merge (S4's bullet and L2's four in the one new
+section). No code file conflicted.
+
+**Changelogs, checked as one set.** `editor/CHANGELOG.md` and `export/CHANGELOG.md` each have exactly
+one `[Unreleased]`, and everything from `[0.9.0]` down is byte-identical to the 0.9.0 cut
+(`31798769`; md5 of the released part `aeb7fd45…` editor, `ae97cb39…` export). The editor's five
+bullets (S4's umtool brand; L2's soft block, boot wait, `cancelReason`, safeRevalidate count) and
+export's three (L1's order and colour, `/ask` scope, subs) neither repeat nor contradict each other
+or the code as merged (the subs bullet states the final rule: a failure after its one retry leaves
+the archive searched without its live chat). No rewrite was needed. `homepage/CHANGELOG.md` is
+unchanged by all three.
+
+**Gates**, all from the worktree root on `5c0a6ef9` (`$T/i10-gates.log`, `$T/i10-e2e.log`).
+- **tsc** (`pnpm -r --no-bail --workspace-concurrency=1 exec tsc --noEmit`): clean, 0 errors, 80 s.
+- **Unit and script tests:**
+ - common **1,954/1,954** (1,913 + S4's 10 + L1's 10 + L2's 21), 59 s; the glyph-parity test ran,
+ not skipped (fontTools 4.65.0 installed);
+ - editor unit **85/85** (79 + L2's 6);
+ - `test:scripts` **173 passed + 1 skip of 174** (162 + S4's 11 `brand.test.mjs`; the report-to-video
+ tests are in this script; the skip is the `LIVE=1` network test);
+ - mcp **219/219**;
+ - `file-schemas-docs.ts --check` clean.
+- **Builds**, all ok, `export/public` seeded per path from the primary (links, no copies), `homepage/public`
+ copied (60 MB):
+ - editor 64 s (`BUILD_ID` `OjVWgLuZA1ucPntDKDx8C`);
+ - export site 41 s: `<html … data-accent="signal">`, theme-color `#f3f6f7` / `#0c0a08`;
+ - export hub 44 s: `… dark" data-base="dark" data-accent="signal"`, `out/ask/index.html` present;
+ - homepage 20 s: `dark data-base="dark" data-accent="signal"`;
+ - umtool 29 s (`pnpm --filter umtool run build`, `BUILD_ID` `BktSP58PkTab_aLIBJ1Vc`).
+ - Every export/homepage build: `out/icons` = the seven files; PNG magic `89504e470d0a1a0a` and IHDR
+ 180/192/32/512/512 (apple-touch, 192, 32, 512, maskable-512); `favicon.ico` `00 00 01 00 03 00`
+ (1,804 B site, 1,832 B hub/homepage); the site's `icon.svg` lit Signal `#5fa8a0` on `#0c0a08`, the
+ hub's and homepage's the parent mark `#151b20 #3f4c56 #e7edf1`.
+- **e2e**, one detached sequence (13:04 → 14:19), every run through the machine-wide queue:
+
+ | suite | passed | failed | skipped | time |
+ |---|---|---|---|---|
+ | export full | **204** | 0 | 0 | 9.7 min |
+ | `e2e:hub` | **24** | 0 | 0 | 1.1 min |
+ | `e2e:2origin` (`TWO_ORIGIN_REBUILD=1`, links in place, no swap) | **3** | 0 | 0 | 1.4 min (2.2 min with `build:hub`) |
+ | homepage full | **27** | 0 | 0 | 49.7 s |
+ | umtool full (`SONG_DIR=~/reports/quartering-uh-song/data`) | **175** | 2 | 45 | 5.8 min |
+ | **editor full** (`pnpm e2e`, 652 tests) | **634** | 6 | 12 | 54.6 min |
+
+ The 45 umtool skips are the song-data capabilities this machine lacks (FACTS "The umtool suite skips
+ instead of going red…"); S4's full run had the same 175 / 2 / 45.
+- **Failures, classified** (each re-run alone ×3: umtool `$T/i10-rerun.log`, editor `$T/i10-rerun2.log`):
+ - **umtool `mix.spec.ts:166` and `:201`: known flake, not the integration.** The pair FACTS records
+ as failing in every full umtool run on `main` (`FACTS.md:5013`, "`mix.spec.ts` IS
+ ORDER-DEPENDENT"): an earlier spec leaves a clip window in the fixture's
+ `channels/testchan/data/vid1/clips/`, and the row links to it (`…/clips/0.00-14.00.mp4`) instead of
+ `/out/clips-raw/vid1_0.00-9.00.mp4`. S4's full run failed on exactly this pair. Alone,
+ `--repeat-each 3`: **6 passed**, 24.4 s; the whole `mix.spec.ts` alone: **12 passed**, 23.0 s.
+ None of the three slices touches `umtool/app/mix`, `umtool/lib/projects/report.mjs`, `mix.spec.ts` or the fixture: in umtool's app
+ and lib, S4 changed only `bin/umtool.mjs` (`--brand`), `components/NewProjectMenu.tsx`,
+ `lib/projects/{kinds,scaffold}.mjs`, `scaffold.ts` and `lib/tools.mjs`'s doctor label (`git diff
+ --stat 386ac995 5c0a6ef9 -- umtool/app umtool/lib umtool/components umtool/bin`).
+ - **Editor: six failures, all flakes under machine load; none is the integration.** Each alone,
+ `--repeat-each 3`, straight after the suite (`$T/i10-e2e-editor-x3.log`): **18 passed, 0 failed,
+ 1.8 min**. The suite ran while the live :3001 editor was busy with its own auto-queue work (load
+ average 10–13 on 8 cores; 7.4 during the re-runs), and took 54.6 min against S3's 38.2. The six:
+ - `queues.spec.ts:144` (281 ms): `EEXIST: file already exists, mkdir
+ '…/test-transcripts/channels/slow-b'` in the fixture setup, before the test touched the page.
+ This is the fixture-reset race S3 met in `pipeline.spec.ts:164` and L2 in `channel-work.spec.ts:208`.
+ - `pulse.spec.ts:18` (3.6 s): the idle `/api/pulse` averaged 499.2 ms a call against its 250 ms
+ tripwire. The spec calls that budget "a tripwire … not a benchmark, and it runs on a shared
+ machine". The idle path (in-memory state plus two `stat`s) is untouched by all three slices; the
+ one `listJobs` change adds a field only to cancelled metas, and pulse does not list jobs.
+ - `sync-deep.spec.ts:256` (ENOENT reading `viddeleted1/availability.json`) and `:442`
+ (`lastFullSweepAt` undefined). Both read something the sync writes AFTER the `lastSyncedAt`
+ stamp their `runSync` helper waits on: `runYtdlp.ts:1853` `touchLastSync`, then `:1858`
+ `touchLastFullSweep` and `:1859` `safeBackfillAvailability`. That is a pre-existing race in the
+ spec's wait, which load widened. The merges change `runYtdlp.ts` by one comment (`:904-909`) and
+ `backfillAvailability.ts` not at all.
+ - `jobs-batch-tasks-drain.spec.ts:54` (24.6 s): the task row's elapsed timer read `0:06` and did
+ not pass it within 10 s. The fake whisper's slow task lives 7 s (`fake-whisper.mjs`, ten 700 ms
+ lines), so a bar that renders late under load leaves about a second before the task ends. L2's
+ `JobRow.tsx` change draws only on `cancelled` rows (`cancelReasonOf`), and the running task's
+ timer (`JobProgressBars.tsx`) is unchanged.
+ - `diarization.spec.ts:185` (1.3 min): the channel snapshot's `transcribedWithAudio` bucket read
+ `[]` for the whole 60 s poll (expected `["vidA"]`). The same poll failed in an earlier full run
+ (the storage-locations slice 4 worktree, job `4cc6ed24`: `["vidC"]`, left by the previous test's
+ batch) and passed on its re-run. `channelSnapshot.ts` is untouched by the merges, and the file's
+ other four tests passed, including the backfill test that drives the same cleanup.
+- **The `export/public` links stayed in place, and the primary was never written.** The rules' per-path
+ seed (`$T/i10-seed-public.sh`): every gitignored entry of the primary's `export/public` linked into
+ the worktree, and no `sw.js` or compose-output copies (the workarounds L1 retired, FACTS updated).
+ - The primary's `export/public` had changed since L1's proof: the live editor's Jeralyzer
+ build-deploy (below) re-composed it at 11:03 as a SITE, so it holds `site.json` and no hub `sw.js`,
+ `hub-sites.json` or `hub-summary.json`. Of L1's eight files, five exist, plus
+ `site.json`. The worktree's stale links to the three absent ones were dropped before the seed, so
+ none dangled (the rules' check before every export build).
+ - With no primary `sw.js` to link, the worktree's `sw.js` was linked to a sentinel in the job's
+ scratch (`$T/i10-sentinel/sw.js`), so a write-through by the export config would change it.
+ - The snapshot (`$T/i10-snap.sh`): every entry of the primary's `export/public` (614 entries, 543
+ files, 2.9 GB) by type, size and mtime, and the md5 of every file, plus the sentinel's md5. It was
+ taken before the gates, after them, before the e2e, after the export run, after `e2e:hub`, after
+ `e2e:2origin`, and after the editor suite: **identical every time** (tree md5 `2f7f3bb9be9d`,
+ content md5 `e3661fb98468`, sentinel `fc1bd566…`), and the primary still had no `sw.js`,
+ `hub-sites.json` or `hub-summary.json`. The five named files throughout:
+
+ | file | size | mtime (2026-09-26) | md5 |
+ |---|---|---|---|
+ | `corpus.json` | 13,702 | 11:03:23.547761504 | `f89e603b…` |
+ | `llms.txt` | 4,245 | 11:03:23.574761030 | `5b6be721…` |
+ | `robots.txt` | 125 | 11:03:23.574761030 | `17b7cc13…` |
+ | `sitemap.xml` | 352 | 11:03:23.575761012 | `4bd0cd6b…` |
+ | `_headers` | 736 | 11:03:23.513762100 | `302b4216…` |
+ | `site.json` | 5,858 | 11:03:23.541761609 | `9394b81b…` |
+ | `sw.js`, `hub-sites.json`, `hub-summary.json` | absent | | |
+
+ - **What the runs did in the worktree:** the export suite replaced the `sw.js` LINK with a plain
+ 11,172 B file whose md5 equals `service-worker/site-sw.js` (`15b4689b…`), and the sentinel kept its
+ md5 — `playwright.config.ts:54`'s `rmSync` removed the link. `e2e:2origin`'s `build:hub` replaced
+ the `corpus.json`, `llms.txt`, `robots.txt` and `_headers` LINKS with the worktree's own plain
+ files (468 / 494 / 76 / 459 B), wrote a plain `hub-sites.json` (2 B, `[]`) where none existed,
+ removed the `site.json` link and left `sitemap.xml` (which it does not write) a link. The next
+ seed relinked them for the homepage and later suites.
+- **The live processes were never touched:** :3001 still serves `BUILD_ID` `P0VMdKX7gbdsaiS5GvorF` and
+ :3050 `a9YAe_dwhuM6DiCPzIwMD`; nothing was deployed, pushed, restarted or written under
+ `transcripts/`.
+- Numbers tools: none.
+
+**Found during the pass.**
+- **Jeralyzer is already on the brand, in Signal.** Job `01M3F36N7SCKGC5JV27EBDFYPN` (`build-deploy`,
+ queue `deploy`, no agent recorded) ran on the live :3001 editor 2026-09-26 10:51–11:06. It built
+ Jeralyzer from the primary's working tree, then `main` @ `386ac995` (the brand plus the 0.9.0 cut,
+ before S4/L1/L2), and deployed it to production (`https://35593886.jeralyzer.pages.dev`). The old
+ editor runs the on-disk build scripts, so the site got the brand, but its `site.json` has no
+ `accent` (the pre-brand editor cannot set one), so it wears Signal. `jeralyzer.pages.dev` serves
+ `data-accent="signal"`; the other four sites, the hub and the homepage are pre-brand. The runbook
+ now says so. The sites step (the runbook's 4, "Rollout" 5 below) rebuilds Jeralyzer in Brass,
+ and its readers' stored themes have already migrated.
+- **umtool is not "untouched" any more.** The live :3050 already spawns `main`'s report-to-video
+ scripts from disk. That is safe (a render that does not opt in is byte-identical), and the form's
+ brand choice needs a rebuild. The runbook adds an optional step for it (`r10-umtool.sh`).
+
+**Runbook** (`~/reports/release-10/make-runbook.py`, regenerated `RUNBOOK.html`):
+- `FINAL` is this pass's tip, and every script's ancestor guard checks it.
+- It says what the rollout contains: the brand, then S4, L1 and L2, in plain words.
+- Step 0 is a **re-cut**: both `[Unreleased]` sections are shown, since export's is public. The editor
+ cut, the hand commit of both files and the untracked-file caveat are unchanged.
+- The new **step 1b** is an OPTIONAL `r10-umtool.sh`: fail-stop, with the ancestor guard, and it
+ refuses while a umtool job runs. It builds into the live `umtool/.next` and never restarts after a
+ failed build. Then one restart on :3050 and a smoke: `/` and `/api/jobs` 200, the form offers
+ "Archilyzer Media", no `⨯` in the start log. `r10-rollback-umtool.sh` is its rollback twin.
+- It adds what readers and the operator will notice.
+- The YouTube assets point to `~/reports/archilyzer-media/brand/INDEX.html`.
+- It adds these gates and the link-safety proof.
+- The copy rulings and new lows are listed.
+- `bash -n` and `sh -n` pass on all nine scripts. No script was run.
+
+| sha | what |
+|---|---|
+| `9e3047e3` | `plans:` FACTS: the two worktree workarounds superseded (L1), and the release 10 facts for S4, L1 and L2 |
+| _this_ | `plans:` this record, STATE ("Merged to main (2026-09-26), NOT rolled out"), the Rollout steps |
+
## Rollout
-Nothing is rolled out. The live :3001 editor still runs `0213f6c8` (the pre-brand build); the five
-official sites are at release 8's Z + E; the hub and homepage at release 9's C1b/C2/C3. The
-operator's runbook is `~/reports/release-10/RUNBOOK.html` (generated by `make-runbook.py` beside it,
-with the rollout scripts in `scripts/`). The steps:
-
-0. **Check the primary contains the brand's final commit** (this release's `plans:` review-fix
- commit): `git -C /home/user/Projects/yt-dlp-transcript-browser merge-base --is-ancestor <final>
- HEAD`. The runbook names the sha, and every rollout script refuses to run without it.
-1. **Cut the export release notes first.** `export/CHANGELOG.md`'s `[Unreleased]` is rendered on
- every site's public `/changelog` until it is cut: `/sites` → Release notes → Cut release (the
- form suggests `0.8.8`). With "Commit changelog" ticked the form refuses a dirty tree, and the
- primary has an untracked `settings.json.pre-priority-*`, so untick it and commit the changelog by
- hand. The
- editor's own notes (`/changelog`) can be cut the same way; they are not public.
+Nothing is rolled out, except that **Jeralyzer is already on the brand, in Signal** (a build-deploy
+on the live editor, 2026-09-26 10:51–11:06; "Integration (S4 + L1 + L2), as merged", above). The
+live :3001 editor still runs `0213f6c8` (the pre-brand build); the other four official sites are at
+release 8's Z + E; the hub and homepage at release 9's C1b/C2/C3. The operator's runbook is
+`~/reports/release-10/RUNBOOK.html` (generated by `make-runbook.py` beside it, with the rollout
+scripts in `scripts/`). The steps:
+
+0. **Check the primary contains release 10's final commit** (the integration pass's `plans:`
+ record, on top of the brand, S4, L2 and L1; it was the brand's S3 review-fix commit before):
+ `git -C /home/user/Projects/yt-dlp-transcript-browser merge-base --is-ancestor <final> HEAD`. The
+ runbook names the sha, and every rollout script refuses to run without it.
+1. **Re-cut the release notes first.** Editor and export 0.9.0 were cut for the brand (2026-09-26
+ 01:37); S4, L1 and L2 then added a new `[Unreleased]` to both (export: L1's three hub bullets;
+ editor: S4's and L2's). `export/CHANGELOG.md`'s `[Unreleased]` is rendered on every site's public
+ `/changelog` until it is cut: `/sites` → Release notes → Cut release (the form suggests
+ `0.9.1`). With "Commit changelog" ticked the form refuses a dirty tree; the primary had an
+ untracked `settings.json.pre-priority-*` at the 0.9.0 cut (its tree was clean at the integration
+ pass), so untick it whenever the tree is dirty and commit the changelog by hand. The
+ editor's own notes (`/changelog`) can be cut the same way; they are not public. Commit both files
+ in one commit.
2. **Restart the live :3001 editor on the new `main`** (release 9's procedure): check `/jobs` for an
- operator-started job; md5 `before`; the editor build into the live `.next` (umtool untouched, not
- rebuilt or restarted); md5 `pre-restart`; ONE editor restart; md5 `after-boot` — the three must
- match; the smoke (`SMOKE_FAIL=0`, or every flag explained). The editor itself changes look: the
- parent mark and a favicon, Archivo/Plex, and the operator's stored theme migrates once.
+ operator-started job; md5 `before`; the editor build into the live `.next` (umtool is not
+ rebuilt or restarted in this step; see 2b); md5 `pre-restart`; ONE editor restart; md5
+ `after-boot` — the three must match; the smoke (`SMOKE_FAIL=0`, or every flag explained). The
+ editor itself changes look: the parent mark and a favicon, Archivo/Plex, and the operator's
+ stored theme migrates once.
+ - **2b. Optional: rebuild and restart umtool on :3050** (`r10-umtool.sh`). Only for the
+ new-project form's brand choice (S4). The live :3050 already spawns `main`'s report-to-video
+ scripts from disk, which is safe because a render that does not opt in is byte-identical. The
+ script refuses while a umtool job runs, never restarts after a failed build, restarts once, and
+ checks that `/` offers "Archilyzer Media".
3. **Set each site's accent and wordmark lead** in the site form (`/sites/<id>`, the one writer; never
hand-edit `transcripts/`):
@@ -814,4 +1005,12 @@ with the rollout scripts in `scripts/`). The steps:
**What readers will notice:** a stored theme migrates once (light stays light, Archive light becomes
Sepia, dark stays dark, system stays system; the family is dropped). After the service-worker update
an installed app opens offline only after one more online visit (`shell-v2` drops the old shell);
-offline channel downloads are kept.
+offline channel downloads are kept. From L1 (on the hub): the official instances are listed in
+the homepage's order, each in one colour everywhere; `/ask` follows the scope chips and, with every
+archive off, is disabled with one line saying why; an archive whose live chat fails is still searched
+(without its live chat, its chip ready).
+
+**What the operator will notice:** a YouTube soft block ("…isn't available, try again later") now
+pauses the platform like a 429 (the batch stops, the runner defers the video 6 h, a scan stops,
+the availability check stops) instead of excluding the video as deleted; cleanup keeps the audio of
+every video its confirmation check did not reach; `/jobs` says why a job was cancelled at boot.