commit 59c6e4f4ab475ee8f911e33be9f0a6a5eda1fcd6
parent 663e13ff65ac23b25fb123600af2d5c89123fa4e
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Wed, 30 Sep 2026 23:44:17 -0400
plans: the on-screen deck, as shipped — the slice table, gates on 663e13ff, what was found and left, the rollout; two [Unreleased] changelog bullets (the deck; a hard-cut cut with a relative --out)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 53 insertions(+), 0 deletions(-)
diff --git a/editor/CHANGELOG.md b/editor/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+- **umtool's report videos can wear an on-screen deck: one panel under the footage for the whole cut, with a pip timeline, a title per clip, its source and date, and its QR.** A report manifest whose `render` says `"chrome": { "engine": "hyperframes", "layout": "deck" }` scales the footage into a box above a 190 px panel (both sizes are settings) and draws, over the whole cut, one unlabelled pip per clip on a track that fills as the cut plays, the clip's own title from `onscreen.title`, a subtitle naming the recording and its date (the channel too when the cut spans more than one; `onscreen.subtitle` replaces it), and the clip's QR. At each clip change the marker travels to the next pip and the title, subtitle and QR hand over; over a card the panel slides away and comes back after. The citation header, the corner QR and the section footer are not drawn on such a cut, and chapters take the clip's on-screen title. Every setting (sizes, spacing, date format, what the subtitle names, whether cards keep the panel, the motion's timings) is in `render.chrome.deck` and checked when it is saved; an unknown or out-of-range one is refused with a sentence saying why. The panel is rendered once per cut by HyperFrames (pinned to 0.8.24; `HYPERFRAMES_PKG` or `HYPERFRAMES_BIN` override it) and reused until its text or settings change. `build-video.mjs --chrome-only` redraws it over the built segments without rebuilding or fetching anything, `--no-chrome` builds the framed cut without it, and `--chrome-preview <at> <dur>` renders a short window. In umtool, the report page has an **On-screen** section — a switch, the settings, a table of every entry's title and subtitle with the automatic subtitle as its placeholder and a character counter, a live preview with a scrubber, a true still, **Re-render on-screen** and the built video — and the clip bench has on-screen title and subtitle fields with the panel previewed over the clip. A manifest without `render.chrome` builds exactly as before, byte for byte.
+- **A report cut with `transition: 0` builds when its output folder was given as a relative path.** The hard-cut concat listed its segments relative to the working directory, and ffmpeg reads that list relative to the list file's own folder, so every hard-cut build with a relative `--out` failed at the concat. The list now names each segment by its full path.
- **`archilyzer doctor` checks the image Build all builds sites in.** When a container engine answers, a new **build image** section says whether the image named under **Settings → Build pipeline** is there, when it was built and how big it is. It warns when the image is missing, or older than the last change to its Dockerfile, and prints the one command that rebuilds it. Build all still builds or refreshes the image itself before it builds any site; the warning tells you ahead of time that the next Build all will spend that time. With no container engine the check is skipped in one line, and with no corpus it is only a note. It never fails the doctor.
- **The site build image runs Node 22 and pnpm 11**, the versions the rest of the workspace runs on, instead of Node 20 and pnpm 9, which did not read the workspace's install rules. The next Build all rebuilds the image from its first step, reinstalling every dependency, before it builds any site.
- **Build all sites works in containers again.** Every site's container build had been failing while it prerendered `/favicon.ico`. Each site now builds from the data composed for it, never from files baked into the build image. A bundle whose `site.json` and `corpus.json` do not both name its site is refused before it is handed back or deployed. The image carries no corpus data, and its build context is about 7 MB from any checkout.
diff --git a/plans/onscreen-deck.md b/plans/onscreen-deck.md
@@ -290,3 +290,54 @@ they are the things a reader of this file alone would not know.
full cut's `deck`/`deck-frames`, so a preview window never overwrites or
races the whole-cut sequence's cache. The contract names `--from`/`--duration`
as composeChrome arguments but not this naming rule.
+
+## As shipped
+
+Branch `deck/integration`, every slice merged `--no-ff` after review; `main` 3edeb4d0 merged in
+at 0d1e7d69 before the final gates.
+
+| Commit | What |
+|---|---|
+| 2cd13c9a, d1e8439e | S0 — this contract, `deck.mjs` + tests, `attributionParts`/`deckSubtitle`/`formatDeckDate`, `segmentOffsets` on `scheduleFrom`, the `./deck` export |
+| e95f208e | S4 — umtool writers (`updateOnscreen`, `updateChrome`, `updateClip` `onscreen`), the chrome/onscreen/preview/files/still/video routes, `rangeResponse`, `buildSteps` `chromeOnly` |
+| 0be86732 | S2 — `chrome-deck.mjs`, the compose-chrome port (dispatch, `--still`, windows, workers, vendored GSAP, pinned renderer, render cache, `qrPng`) |
+| d3bb2804 | S1 — deck framing for clip/image/shown-card segments, chrome skipped under the deck, `writeChromeSchedule`, chapters prefer `onscreen.title` |
+| f936f799 | S5 — the On-screen section and the clip bench's on-screen fields |
+| c653e119 | S3 — one command composes, renders and overlays; `applyChrome` for transition 0; `--chrome-only` / `--no-chrome` / `--chrome-preview`; `-reinit_filter 0` + `format=rgba`; verify-build deck checks; absolute concat list |
+| 89fd4cfa | S7 — README, report-video/build/clip-bench docs, quirks, "As built" |
+| 66e60c65 | S6 — `onscreen.spec.ts`, deck fixtures and a HyperFrames stub; the 409 reload keeps only real edits; timeline-row locators scoped past the table's `data-entry` |
+
+### Gates on 66e60c65
+
+- `pnpm -r --no-bail --workspace-concurrency=1 exec tsc --noEmit` clean (81 s).
+- common 2,404/2,404; editor unit 101/101; `test:scripts` 267 pass, 2 skipped (the
+ `queue-lock.test.mjs` timing cases fail intermittently under machine load and pass alone —
+ a known load flake, not touched here); mcp 271/271.
+- `pnpm --filter editor exec next build` exit 0 (50 s); `pnpm --filter export exec next build`
+ exit 0 (28 s).
+- umtool `next build` with the corpus linked, under the 5 GB cap: exit 0, 22 s; link removed.
+- umtool e2e `onscreen clip-bench build projects report-fetch-via-editor`: 87 passed (3.6 min);
+ on S6's branch 87/87 twice (7.8 and 3.3 min).
+- Byte-identical without `render.chrome`: `--only c07` and `--only t00` segment md5s equal to
+ the base's (S1), and `c07` again after S3.
+- First application, built from this branch with `--skip-fetch`: 17 clips, 350.2 s, deck
+ 10,506/10,506 frames (rendered in 211 s), `verify-build` ok, 17/17 mid-clip QRs decode to the
+ clip's `citeUrl`, chapters equal the on-screen titles in order. An edit-one-title
+ `--chrome-only` round trip (S3, scratch) re-rendered the deck only; an unchanged re-run was
+ `cached` in 0.6 s.
+
+### Found and left
+
+- `verify-build` cannot tell whether the overlay was laid: a `--no-chrome` final passes while
+ frames from an earlier render are on disk.
+- `--chrome-only` writes over the final while it encodes, as `--rail-only` does.
+- The umtool preview shows saved settings; unsaved title/subtitle drafts preview live, unsaved
+ settings do not (save recomposes).
+- e2e runs the renderer through a stub; the real renderer is proven by the gates above, not by e2e.
+- The legacy chart band still refuses `transition: 0`; `applyChrome` could serve it, untested.
+
+### Rollout
+
+Umtool-only: a umtool rebuild (`pnpm --filter umtool exec next build`, capped, corpus linked)
+and a umtool restart. Nothing under `export/`, `homepage/`, `editor/` (beyond the changelog) or
+`common/` changed, so no site, hub or homepage rebuild is owed for it.