commit dcea35544c8e28c14faf675a3240be8a624060d0
parent cf20cd088e4c7bd62d98be311f96e2609857ec1d
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 16:22:49 -0400
plans: release 8 slice H record — the homepage, rethought; CHANGELOG bullet
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 120 insertions(+), 0 deletions(-)
diff --git a/editor/CHANGELOG.md b/editor/CHANGELOG.md
@@ -1,6 +1,7 @@
# Changelog
## [Unreleased]
+- **The project homepage is rewritten, and it can be deployed as a preview.** The front page of `archilyzer.pages.dev` now opens on one line and a chart. The chart shows the official instances' transcripts by the month each video was published, from 2009 to now, stacked by instance. It is drawn when the site is built, so no chart script loads. Under it is one strip of numbers for the official instances: hours of speech, transcripts, recordings, channels, instances, and recordings gone at the source but still here. The **Official instances** section has a card for each public site, with its channels, recordings, transcripts and hours. **What it does** is now three short paragraphs. The recent-acquisitions list, "How it works" and "What this isn't" are gone from the page. The homepage summary (`homepage/public/homepage-summary.json`) is version 5. It adds `monthly`, `official` and per-site numbers and removes nothing, so `/stats` is unchanged. `archilyzer deploy homepage --preview <branch>` deploys a preview the way `deploy hub --preview` does. Without the flag it still deploys to production (`main`).
- **A channel's video list shows titles, and you can search by them.** On `/channels/<slug>/videos`, each row now shows the video's title, with its id in smaller type underneath. Search matches the title or the id, ignoring case. The title comes from the transcript index for transcribed videos, from the channel's metadata scan for videos that were listed but never downloaded, and otherwise from the video's `metadata.info.json`. A video none of these name shows its id, as before. Each row's selection checkbox and link are named by the id as before, and the order is unchanged (by id).
- **An undownloaded video's page shows its title and details.** The video page used to show a bare id for any video without a `metadata.info.json`. If the channel's metadata scan has read the video, the page now shows its title, upload date and duration from the scan, marked *from the listing scan — not downloaded*. Any video page with a description, from either source, has a **Description** section, collapsed by default.
- **A rate-limit cooldown the download runner recorded is no longer undone by another lane a few minutes later.** The four automatic runners (download, transcription, digest, backfill) are meant to share one copy of `.auto-queue/state.json`, because every save writes the whole file. At startup all four read it at the same moment, and each kept its own copy. After that, whichever runner saved next wrote its startup copy over the others' progress. On 2026-09-25 that silently put a YouTube cooldown back from 13 failures to 12 and dropped a six-hour hold on one rate-limited video, 13 minutes after the download runner had saved them. The runners now wait on one read at startup and share one copy, so a save by any lane keeps every lane's latest state. A cooldown that a manual Sync, a metadata scan or the video page records is no longer erased by a runner's save either. It used to go to the file only, so the next save of any lane wrote over it, and nothing brought it back while the download runner was paused or stopped. It is now written into the copy the runners share. Nothing changes in the file format.
diff --git a/plans/release-8.md b/plans/release-8.md
@@ -179,6 +179,125 @@ format is unchanged.
persist its old object to the same fixture file until it notices its job is gone. That is a
cross-spec clobber, not a two-lanes-in-one-process problem, and this slice did not introduce it.
+### Slice H, as shipped — the homepage, rethought (2026-09-25)
+
+Branch `one-core/r8-homepage` off `main` `07a56106`. **The operator's direction (2026-09-25):** "take a
+look at the homepage and try out a complete refresh, rethink it from the ground up. Primarily describe
+the project, and any of our existing sites should be regarded as the instances that belong to me, maybe
+called something like 'Official instances'." The page had "a bit of 'clunky AI' writing where it could
+be more straight to the point and have less subtitles"; "The whole 'What this isn't' section seems kind
+of bad"; "narrow it down to minimal most impactful copy and show some cool stats about the official
+instances"; and "The 'recent acquisitions' list doesn't feel like it should be the above-the-fold
+showpiece, I think a chart there would be a cooler visual." The hub link stays withheld
+(`HUB_LINK_ENABLED = false`).
+
+**The data.** `buildHomepageSummary` is v5, all additive and optional on the type so a v4 summary on
+disk still renders (its numbers hide):
+- `monthly: {month, bySite}[]` counts transcribed public-universe recordings by **upload month**
+ (`VideoStat.uploadDate`), attributed to the primary public site. It is full history, from the
+ earliest upload month to the build month, zero-filled, and every site is keyed in every month.
+ Upload month rather than `transcribedDate` because the transcription history is six months old
+ (2026-04 to 2026-09, 25,288 of it in May) and would chart the tool's own backlog. Upload month
+ shows how far back each archive reaches: 205 months, 2009-09 to 2026-09.
+- `monthlyUnplaced` counts the transcripts with a missing, malformed or future upload date, so
+ nothing is dropped silently (0 on the current corpus).
+- Each site gains `channels` (with a transcript), `recordings` (with a download date), `hoursArchived`
+ and `gone` (status `deleted`).
+- `official` sums them over the public sites. It is not `totals`, which also counts pool-only
+ channels and the URL-less Jasolyzer: 49,767 official transcripts against 49,769 in total.
+
+**The page, top to bottom, with its final copy:**
+1. **H1** `{official.hoursArchived} hours of speech, searchable to the second.` (no data:
+ `Every word a channel said, searchable to the second.`). It is one line from `lg` up.
+ **Subhead:** "Download a channel's back catalogue, transcribe it on your own hardware, publish a
+ static site you host yourself." The CTAs are unchanged: `Download the source` → `/downloads/`,
+ `Read the setup guide →` → `/docs/install/`, and the hub button behind the switch.
+2. **The chart** (`ArchiveGrowthChart.tsx`), in the hero above the fold at 1280×900. It has a
+ legend row, then stacked strata, then the caption "Transcripts by the month each video was
+ published, all official instances." and a `Numbers by year` details table.
+3. **The stats strip** (`FamilyStats.tsx`): Hours of speech · Transcripts · Recordings · Channels ·
+ Official instances · **Recordings gone at the source, still here**. The last cell is double
+ width, in `--state-gone`. The caption is `Across the official instances · index built <time>`.
+4. **Official instances**, with the standfirst "The archives I run. Anyone can run their own." It
+ has five cards: title linking out (↗), description, and channels · recordings · transcripts ·
+ hours. The stripe is `seriesColor(i)`, the chart's colour for that instance.
+5. **What it does**, as three short paragraphs. Without subtitles, three one-sentence items are a
+ paragraph with bullets in front:
+ "Archilyzer downloads a channel's videos and keeps up with new uploads. It uses the platform's
+ captions where they exist and transcribes the audio on your machine where they don't." / "The
+ result is a static site of plain HTML and JSON that any file host can serve. Search covers every
+ transcript and opens the video at the second the words were said." / "It is a program you run,
+ not a service. There is no account and nothing phones home. The docs cover setup and deployment."
+ (links `/docs/`).
+
+**Dropped from `/`:** the recent-acquisitions rail (`RecentAdditions.tsx` is kept and `recent` stays
+in the summary), "How it works", "What this isn't", the proof-point band (its number is now the strip's
+gone cell), the "This deployment, at last build" eyebrow and every other eyebrow.
+
+**The chart.** It is a server component and renders inline SVG at build time. It ships no client JS
+and no chart library. The plot is an SVG with `preserveAspectRatio="none"` in a fixed-height box
+(200/260/300 px), which avoids layout shift at any width. The hairlines are `non-scaling-stroke`, and
+the axis labels are HTML positioned in percent, so no text stretches. Year ticks run every 4 years on
+a phone and every 2 from `sm`. The value scale sits on its gridlines. Hover is native: one transparent
+column per month with a `<title>` (month, total and per instance) and a CSS tint. The chart has
+`role="img"` with a sentence label (total, span, instance count and busiest month). **Area, not bars:**
+205 months over ~1,100 px is ~5 px a month, and the dataviz spec's 2 px gap would be half of each bar.
+The palette check (`validate_palette.js`) on the family's `--chart-1..5` FAILS normal-vision separation
+for the adjacent teal/blue pair (ΔE 12.4 light, 9.0 dark). The **stack order is therefore interleaved
+(0, 2, 1, 4, 3)**, and every touching pair then passes (worst 21.5 light / 22.5 dark, CVD ≥ 10.2). The
+colours still follow the instance. Cards no longer use a site's `accent`, so a stripe cannot disagree
+with its layer (no site sets one today).
+
+**`deploy homepage [--preview <branch>]`.** `deployHomepage` takes `previewBranch` the way `deployHub`
+does. `previewBranchProblem` refuses `main`/`production` and bad names before the build check. A preview
+deploys with `--branch <name>` and no `--branch main`, and prints the `previewAliasUrl` line.
+`homepageDeployArgs(outDir, previewBranch?)` is the pure argv. **No deploy was run.**
+
+| sha | what |
+|---|---|
+| `6fd98789` | `homepageSummary.ts` v5: `monthly`, `monthlyUnplaced`, per-site `channels`/`recordings`/`hoursArchived`/`gone`, `official`. New `homepageSummary.test.ts` (+3): upload-month placement, zero fill and every site keyed; unplaceable dates counted; per-site and official sums exclude a URL-less site that `totals` still counts; hours rounded once over summed seconds; empty `monthly` when there is nothing to place |
+| `1f93fd10` | `ArchiveGrowthChart.tsx` and the `.growth-hit` hover rule in `globals.css` |
+| `921f534b` | `page.tsx` rewritten. `FamilyStats.tsx` is new. `ArchiveCards.tsx` gets per-site numbers and a `seriesColor(i)` stripe, and the title becomes the link |
+| `8aa17ccb` | `marketing.spec.ts`: "What this isn't" and the rail are replaced by the chart (`role=img`, present iff the summary on disk has `monthly`) and `Official instances` (≥ 1 absolute link iff sites) with the strip's `time[datetime]`. H1 regex, CTA names, the conditional hub link, nav and 404 are kept |
+| `7c01ea86` | `deployHomepage({previewBranch})`, `homepageDeployArgs` and the CLI row. `build.test.ts` +2 (argv for production, blank and preview; refusal order) |
+
+**Gates**, on `7c01ea86`: tsc clean. Common tests **1815/1815** = 1810 + 5. `pnpm --filter homepage
+exec next build` ok, 15 static pages (`out/index.html` is 271 KB, 38 KB gzipped, against 94 KB before;
+most of the growth is the chart's paths and 205 hover columns, repeated in the RSC payload). The
+homepage e2e (`node scripts/worktree.mjs run -- pnpm --filter homepage run e2e`, `h-e2e.log`) passed
+**22, failed 0, in 35 s**, with no queue wait; its 5 specs were run against the copied v5 summary. The
+marketing spec also passed with the summary moved away (no-data path: no chart, no heading, the
+fallback H1). Screenshots at 1280 and 390 wide in light and dark were checked by eye: no horizontal
+overflow (scrollWidth equals the viewport in all four), the chart legible, nothing clipped. The
+editor/export builds, editor unit, test:scripts and mcp were not run, because the slice touches no file
+they build or test (`common/publish/build.ts` is covered by the common suite and tsc).
+
+For a realistic render, the worktree's `homepage/public/{channel-sites.json,stats/}` were copied from
+the primary. `homepage-summary.json` was rebuilt offline by `buildHomepageSummary` over the primary's
+stats pages (`$T/h-mksummary.ts`) and read no corpus. On the current data, official is 5 instances,
+63 channels, 75,785 recordings, 49,767 transcripts, 60,840 h and 480 gone.
+
+**Numbers: none.** No settings, site or channel key changed. The summary's format is additive.
+
+**Pending, the parent's:** `archilyzer build homepage`, then the preview (`deploy homepage --preview
+<branch>`) and production deploys, from the primary. The live summary becomes v5 only when compose
+runs there.
+
+**Found and left.**
+- **The right edge of the chart falls away.** It is by upload month, so the current month is partial
+ (106 by 2026-09-25) and the last few months are thinner while archiving catches up (419 in August
+ against ~600 a month through 2025). That is true, but a reader could take it for fewer uploads. A
+ caption note or trimming the partial month is the operator's call.
+- **The chart cannot show when things were archived.** `transcribedDate` has six months of history
+ and a 25k May spike. The summary's `series.transcribed.month` still carries it for `/stats`.
+- **`--chart-3` is `--state-gone` in the archilyzer family** (Bonnellyzer's layer is the same rust as
+ the gone figure). The family chroma floor also fails the validator (teal and ochre read greyish).
+ Both are in `common/styles/tokens.css`, outside this slice.
+- **`/stats` logs `Received NaN for the width attribute`** once during `stats.spec.ts` (the spec
+ passes). It comes from the `/stats` client charts, not this page, and was not investigated.
+- `KpiHeader.tsx` and `RecentAdditions.tsx` are unchanged. `/stats` still uses `KpiHeader`. The rail is
+ unused on `/` but kept, per the brief.
+
## Rollout 2026-09-25 (late afternoon) — `0e72ef73` live on :3001 (second restart of the day)
The second editor restart of the day. Release 7 (`bb3dbb4c`) had gone live at 13:11. Two things