Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 1a347f5166dd7c491a66e76611151fbf773ad446
parent 5fa76626b05a765eafdf22a2cbcba2400282654c
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu, 24 Sep 2026 22:39:02 -0400

plans: release 5 slice X — record and changelog after review

The hub key, the config-load self-heal, the spec wording (four mounts, the
hub's homepage.json, /downloads shows its empty state), the gates re-run on
5fa76626, and the changelog bullet now saying the hub follows the switch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Meditor/CHANGELOG.md | 2+-
Mplans/release-5.md | 67++++++++++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 49 insertions(+), 20 deletions(-)

diff --git a/editor/CHANGELOG.md b/editor/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## [Unreleased] -- **A site can turn off its visitors' per-video transcript downloads.** The transcript viewer on a published site has always offered three ways to take a video's text away: a **Download** menu (txt, srt, json), **Copy MD**, and **Copy download command** (a `yt-dlp` line for a marked clip). A site's settings form now has a checkbox for them, *Per-video transcript downloads*, beside the archive zips one. Unticked, the site's next build shows none of the three; **Share** and the clip marks stay. It is on by default, so a site nobody touches is unchanged, and the file stores `"transcriptDownloads": false` only when it is off (`SITE.md` has the key). The site's machine contract (`/corpus.json`, `llms.txt`, the manifests and shards the MCP server and report-to-video read) is published either way. The hub has no site settings of its own and keeps the controls. The editor's own video pages are unaffected. +- **A site can turn off its visitors' per-video transcript downloads.** The transcript viewer on a published site has always offered three ways to take a video's text away: a **Download** menu (txt, srt, json), **Copy MD**, and **Copy download command** (a `yt-dlp` line for a marked clip). A site's settings form now has a checkbox for them, *Per-video transcript downloads*, beside the archive zips one. Unticked, the site's next build shows none of the three; **Share** and the clip marks stay. It is on by default, so a site nobody touches is unchanged, and the file stores `"transcriptDownloads": false` only when it is off (`SITE.md` has the key). The site's machine contract (`/corpus.json`, `llms.txt`, the manifests and shards the MCP server and report-to-video read) is published either way. The hub follows the same switch: the hub form on **Sites** has the same checkbox, stored as `"transcriptDownloads": false` in the hub's `homepage.json`, and it hides the three controls on the hub's Browse and Ask pages. The editor's own video pages are unaffected. - **Channel rows no longer scroll over a group's controls on `/channels`.** Scrolled down and to the right, the pinned Slug column of every row painted over the pinned group header and its five station buttons (Sync, Download, Transcribe, Digest and the speaker lane), and took the clicks. The pinned Slug cell and the group header sat at the same stacking level, and the later rows won. The rack now has one named layer order, kept in one file: the Advanced panel, then the column header, then the group header, then the pinned checkbox and Slug cells. Nothing ties any more. The screenshot audit found four more problems, fixed as well. A group header's name and buttons now stay on screen however far the columns scroll across (they used to scroll off to the left). An Advanced panel opened near the bottom or the right edge scrolls itself into view instead of being cut off. The rule above a pinned group header moves with it instead of leaving a gap the rows showed through. On a phone, the column header no longer paints over the selection bar pinned to the bottom of the screen. - **A group's Transcribe works for YouTube channels, and it counts what it queues.** The station used to be disabled for every `youtube`-handling channel with the message "a youtube-handling channel never runs whisper". That was wrong. A YouTube video that came down with no captions is transcription work like any other, and the automatic runner already treats it that way. Transcribe now counts two kinds of video, after the usual members-only, deleted and private exclusions: downloaded videos with no transcript at all, and downloaded videos whose only transcript is YouTube's auto-captions. Pressing it queues exactly those videos, by id, as the channel page does: up to two jobs per channel on the transcription queue. A video downloaded before it went private, members-only or deleted is no longer transcribed by the group button, because it was never in the figure. Pressing it again while either job runs says *already running*. The wording names no method ("…has downloaded audio to transcribe", "…each takes minutes"). **This figure can now be higher than the Transcription band in the same rack on channels with many auto-caption-only videos.** The band counts videos with no transcript at all, while the station counts everything its button would queue. That is intended. - **The editor's atomic JSON, text and binary writes now go one way, and a failed write no longer leaves a temp file behind.** Nineteen JSON write sites and seven text and binary ones each wrote `<file>.tmp-<pid>` and renamed it over the original — the channel roster, maybe-missing and metadata-scan records, the scheduler and auto-queue state, worker defaults, widget presets, the homepage config, relocation markers, shard configs, the duplicate and media-scan reports and their review decisions, the saved-video backup manifest, both cue normalizers, the playlist, the failed-transcriptions list, the X cookie jar, a site's CHANGELOG cut, a saved video copied into its store across drives, and the video page's VTT promote and remark. They now all go through one writer (`common/lib/jsonFile-server.ts`), which gives every write its own temp name and queues writes to the same file one behind another, so two jobs touching one channel's roster at once cannot trip over each other's temp file. A write that fails now removes its temp: the live `.auto-queue/` holds 175 `state.json.tmp-…` files (173 of them empty) from the day `/home` filled up (2026-09-11), each one a failed write the old code left behind; nothing deletes those old ones for you — `find transcripts -name '*.tmp-*'` lists them. Four temp names stay, on purpose: the export build's two page writers `buildIndex.ts` (a streaming page writer) and `buildStats.ts` (a hand-joined array) — folding them is a restructuring, not a swap — and `transcode.ts` / `transcribeOne.ts` name the output file ffmpeg or the transcription app writes, which is not our write to fold. No file's contents change — every writer puts the same bytes on disk it did before, measured over the live corpus. The cookie jar is still created readable only by you. diff --git a/plans/release-5.md b/plans/release-5.md @@ -91,7 +91,8 @@ Numbers: none (no file format changes). Branch `one-core/r5-exports`. The editor does NOT mount `TranscriptModal`/`PlayerProvider` — the gate is export-only. There is no `pnpm ops site-config`: `site.json` is written only by the -Settings form (`SiteForm.tsx` → `saveSiteAction` → `writeSite`). Five sites mount the modal. +Settings form (`SiteForm.tsx` → `saveSiteAction` → `writeSite`). Four places mount the modal (SiteWorkspace, duplicates, HubHome, AskHub — all +in `export/`). 1. **Schema key `transcriptDownloads`** (boolean, absent = on), the `archives` four-site pattern in `common/lib/siteSchema.ts` (type, docs, zod opt-out, write-if-non-default). Regenerate `SITE.md` @@ -104,16 +105,21 @@ Settings form (`SiteForm.tsx` → `saveSiteAction` → `writeSite`). Five sites off (Copy download command, the Download dropdown, Copy MD). Mount sites pass `features={{ transcriptDownloads: currentSite().transcriptDownloads !== false }}`: `export/app/(workspace)/SiteWorkspace.tsx`, `export/app/duplicates/page.tsx`; hub pages - `HubHome.tsx` and `AskHub.tsx` read the hub's own `_homepage` site.json the same way (the hub is a - published surface and follows the same key; default on). + `HubHome.tsx` and `AskHub.tsx` read the hub's own key the same way — the hub has no site.json, so + it is `transcriptDownloads` in `sites/_homepage/homepage.json`, passed through `hubSite()` and + set by the editor's hub form on `/sites` (the hub is a published surface and follows the same + key; default on). 4. **Tests.** Export e2e: a fixture site with `transcriptDownloads: false` → the three buttons absent (by accessible name), default → present; an `archives: false` fixture → no `/downloads` link, no zip manifest (new coverage); `phase3-files-numbers.ts` still reports zero unknown keys on both sides. Editor e2e: the site form round-trips the new checkbox. 5. **Rollout (after merge and the editor restart):** for each of jeralyzer, rekietalyzer, hasanalyzer, anilyzer, bonnellyzer: Settings form → untick archives and transcript downloads → - save → build-site + deploy-site. Verify per site: `/downloads` absent, transcript modal has no - Download/Copy MD/Copy download command, `/corpus.json` and one `page-0001.json` still 200. + save → build-site + deploy-site; then the hub form on `/sites` → untick transcript downloads → + save → rebuild + deploy the hub. Verify per site: no Downloads link in the header or footer, and + `/downloads` shows its empty state (the route still builds and answers 200); transcript modal has + no Download/Copy MD/Copy download command, `/corpus.json` and one `page-0001.json` still 200. + On the hub: a federated transcript's modal has none of the three either. `use-with-ai` and `llms.txt` already gate their archives prose. 6. Out of scope: gating the machine contract; "Copy share link"; umtool clip fetching; pruning the archives R2 volume. @@ -146,7 +152,8 @@ STATE. umtool: rebuild only if `git diff --stat <live>..<new> -- umtool` is non- Branch `one-core/r5-exports` off `main` `f4da04a9`. One new `site.json` key, `transcriptDownloads` (boolean, absent = on), gating the transcript modal's three per-video export controls on the export -site; the site form writes it. `archives: false` needed no code — it gained test coverage. +site; the site form writes it. The hub, which has no `site.json`, gets the same key in +`homepage.json`, written by the hub form. `archives: false` needed no code — it gained test coverage. | sha | what | |---|---| @@ -154,16 +161,21 @@ site; the site form writes it. `archives: false` needed no code — it gained te | `d301d757` | `PlayerProvider({children, features?})`: `PlayerFeatures = {transcriptDownloads}`, `DEFAULT_PLAYER_FEATURES` all on, exposed as `usePlayer().features` (memoised on the flag, so an inline `features={{…}}` does not rebuild the context). `TranscriptModal` renders none of Copy download command, the Download dropdown and Copy MD when off; Share, the clip marks and every accessible name are unchanged when on. Mounts: `(workspace)/layout.tsx` → `SiteWorkspace` prop, `duplicates/page.tsx` directly, `HubHome`/`AskHub` via a prop from their server pages (`(workspace)/page.tsx`, `(workspace)/ask/page.tsx`) — all `currentSite().transcriptDownloads !== false` | | `a42b1737` | `SiteForm.tsx` checkbox after the archive size cap, label "Per-video transcript downloads (Download menu, Copy Markdown, Copy download command)"; `actions.ts` reads it like `archives`. `editor/e2e/sites-crud.spec.ts`: one new test round-trips BOTH opt-outs (no spec covered the `archives` checkbox before) | | `9958b42b` | export e2e: `transcript-downloads.spec.ts` (2), `archives-off.spec.ts` (2) | -| *(this commit)* | `plans/tools/phase3-files-numbers.ts` literal gains the key; this record; `site-exports-off.md` superseded note; changelog | +| `6950945a` | `plans/tools/phase3-files-numbers.ts` literal gains the key; this record; `site-exports-off.md` superseded note; changelog | +| `c09ef9c7` | review fix: the leftover-key strip moves from the spec's `beforeAll` into `export/playwright.config.ts` (config load, before any spec) | +| `c65c08c2` | review fix, the hub: `homepage.json` `transcriptDownloads` in `common/lib/homepage.ts` (type; read only as `false`; `writeHomepageConfig` writes it only when `false`), `hubSite()` passes it through (`export/app/lib/site.ts`), checkbox on `HomepageConfigForm.tsx` (same label) + `homepageActions.ts`. `common/lib/homepage.test.ts` (new, 2 tests — there was no homepage test), `export/e2e-hub/transcript-downloads.spec.ts` (2, the hub-mode suite), `sites-crud` round-trip of the hub form. `playwright.hub.config.ts` removes a leftover fixture `_homepage/` at load | +| *(this commit)* | record + changelog after review | **Deviations / findings.** -1. *The hub has no `site.json`.* `sites/_homepage/` holds `homepage.json`; in hub mode - `currentSite()` is `hubSite()`, a `Site` synthesised from the homepage config that never sets - the key. The hub pages are wired through `currentSite()` like every other mount, so they follow - the key IF the hub ever gets one — today the hub always shows the three controls. Giving the - hub a switch means a `homepage.json` key and `hubSite()` passing it through - (`common/lib/homepage.ts`, not this slice's file). **Open for the operator:** is the hub - (`archilyzer.pages.dev`'s Browse/Ask) a surface the exports-off decision covers? +1. *The hub has no `site.json`, so its key is in `homepage.json`.* In hub mode `currentSite()` is + `hubSite()`, a `Site` synthesised from `sites/_homepage/homepage.json`. The first cut left the + hub always on; review held the plan's decision (the hub is a published surface — and it + federates exactly the five sites being turned off — so it follows the same key, default on), + and `c65c08c2` adds `transcriptDownloads` to `homepage.json` under the same name and rules. + `writeHomepageConfig` rebuilds the file from named fields, so the hub form is the key's writer. + **`homepage.json` has no generated doc** (`file-schemas-docs.ts` covers `settings.json`, + `site.json` and `config.json` only) and no zod schema; none was invented. The numbers tool does + not read `homepage.json`, so it was not re-run for this commit. 2. *Client mounts cannot call `currentSite()`* (it reads `site.json` off disk). `SiteWorkspace`, `HubHome` and `AskHub` are `"use client"`, so each takes a boolean prop from its server parent; `duplicates/page.tsx` is a server component and passes the features itself. @@ -171,9 +183,14 @@ site; the site form writes it. `archives: false` needed no code — it gained te (`SITE_ID=testsite`) from one `next dev`; no spec varied `site.json` before (the per-site variants in the suite are route mocks of client data). `currentSite()` is uncached and `next dev` re-renders the layout per request, so the off test writes `transcriptDownloads: false` - into `e2e/fixtures/sites/testsite/site.json`, asserts, and `afterEach` restores the bytes; a - run killed mid-test leaves the key, which `beforeAll` strips on the next run. A second fixture - site would need a second `next dev` of the same app dir. + into `e2e/fixtures/sites/testsite/site.json`, asserts, and `afterEach` restores the bytes. A + run killed mid-test leaves the key; `playwright.config.ts` strips it when the config loads, + before any spec runs (`c09ef9c7` — the first cut stripped it in the spec's own `beforeAll`, + which runs 36th of 37 files, after `player-control-bar` had already failed on it). The strip + re-serialises the file, so after a killed run the fixture can differ from git in whitespace + only. A second fixture site would need a second `next dev` of the same app dir. The hub-mode + spec does the same with a `_homepage/homepage.json` that the fixture tree never commits; the + hub config deletes a leftover. 4. *`archives: false` is a build-time switch, so its coverage is a compose run, not a page.* The Downloads page and its Header/Footer links key off `public/archives/manifest.json` (`hasArchives()`), which `compose-site` writes; the dev server reads the checked-out @@ -201,6 +218,16 @@ site; the site form writes it. `archives: false` needed no code — it gained te settings`, all six exist; `sites-crud` is now the spec covering the `archives` checkbox): **50 passed, 0 failed, 1.7 min** (after ~7.5 min behind slice R's run on the queue). +**Gates after review** (tip `c65c08c2`, same commands). tsc clean. common **1740/1740** (+2, +`homepage.test.ts`). `file-schemas-docs.ts --check` green. editor unit **72/72**. editor `next +build` ok (39 s), export `exec next build` ok (24 s), no dangling `export/public` links. EXPORT e2e, +full suite: **192 passed, 0 failed, 7.5 min**. Export HUB suite (`… run e2e:hub`, the existing +hub-mode run, `playwright.hub.config.ts`): **8 passed, 0 failed, 17 s** (6 + the 2 new). EDITOR e2e +`sites-crud`: **13 passed, 0 failed, 41 s** (the spec closest to the hub form; no spec covered that +form before). Fixture tree clean after all three (`git status`; no `_homepage/` left). +test:scripts and mcp are untouched by the fixes (no file under their globs changed) and were not +re-run. + **Numbers** (`plans/tools/phase3-files-numbers.ts`, `TMPDIR` = the job's scratch). Inputs frozen once (`FREEZE_TO`) from the live corpus: 6 `site.json`, 71 `config.json`, 1,763 sidecars. Before = the tool on `f4da04a9` code (branch changes stashed); after = the branch with the literal updated @@ -211,5 +238,7 @@ the key on disk). A third run over a scratch copy with jeralyzer's `site.json` c `transcriptDownloads: false` + `archives: false`: unknown keys still empty, both parse `false`, and the written file keeps both keys. -**Left.** Rollout item 5 (untick both on the five published sites, build + deploy each) is the -operator's, after merge. The hub question in finding 1. +**Left.** Rollout item 5 is the operator's, after merge: untick both on the five published sites +and build + deploy each, then untick transcript downloads on the hub form and rebuild + deploy the +hub. Verify per site as item 5 now says: no Downloads link in the header or footer, and +`/downloads` shows its empty state (it still answers 200).