commit c8ff4c7e7b85427ce5eddd5f2221c896a5420df3
parent 65449da7109a4ab14eb8372cc6da2f8581e98b99
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 29 Sep 2026 21:52:21 -0400
plans: S1's review fixes recorded — the rulings apart from the parent's directions and my decisions (L3); two page-life flags and the second-mount tests (M1); L2 left; the review table and the re-gates; the S1 row of the slices table and three S1 live checks in the Rollout (L4); the plan's as-built note and the export changelog say a stored query stays held under a filter link
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
3 files changed, 86 insertions(+), 21 deletions(-)
diff --git a/export/CHANGELOG.md b/export/CHANGELOG.md
@@ -6,7 +6,7 @@
- **A chart's stacked bars are separated by a 2 px gap in the chart card's colour.** A stacked bar's segments were drawn touching; they now have a 2 px gap in the card's colour between them, and in high-contrast mode the system's background colour. Stacked areas keep their line in each series' colour along the top, charts of one series, line charts and side-by-side bars are unchanged. Needs a rebuild and deploy of each site.
- **Two grounds, Light and Dark, and each site in its own accent.** The third ground, the warm paper one, is gone: the header's toggle cycles System, Light and Dark. A reader who had chosen it gets Light, before the page first paints and with no other ground on the way, and the stored choice becomes Light (the old paper theme's `archive` + `light` too). The theme menu's accent picker is gone from the header and the slide-out menu: every page wears the site's own accent (`site.json` `accent`), and a reader's stored pick from before is not read and is left in storage. Needs a rebuild and deploy of each site.
- **The header carries the operator's social links and one theme toggle, keeps the site's name on a small screen, and links to the Archilyzer home in place of the sites menu.** Every site's header and the hub's end with the social icons (the site's `socialLinks`, else `settings.json`'s) followed by the theme toggle, all 36 px keys (44 px on a touch screen) with a focus ring. From 520 px wide the header shows every link, up to four (with more, the ones marked **Keep in header on small screens** first, then the last of the rest); below 520 px it shows only the marked ones (none marked → none) and keeps the site's name beside them. The switch is 32.5rem, so at a larger text size it comes later. With one marked link, every current site's name shows in full from 360 px wide on a touch screen. The footer keeps every link, in the same keys (its icons were 20 px and turned the accent on hover; they now turn the text colour), and wraps them rather than widen the page. The **Sites** dropdown and the **Hub** link are gone from the header and the slide-out menu: in their place a link, **Archilyzer**, goes to the Archilyzer home's Official Instances, in the same tab (not on the hub, which lists them itself). **Changelog** moved from the header and the menu to the footer, after Use with AI. The nav and the Archilyzer link are inline from 1024 px wide; below that they are in the slide-out menu, which now holds only them. Only as last resorts, for a very long name on a phone, does the name drop (its mark stays; the same before and after the page's font has loaded, and never with its last letter cut off) and do the icons scroll sideways in their own box. A site's `hubUrl` still loads and is no longer shown. Needs a rebuild and deploy of each site.
-- **A clear screen until the first Search.** A plain visit to a site's search page, and to the hub's, shows the search bar, the page's intro and the footer: no count, no listing and no results controls, and the line under the bar, "Press Enter or click Search to apply", says what to do. Search with the box empty lists every video, as before. A link that carries a query or a filter (`qt=`, `q=`, `tg=`, a share link, the older filter keys) still shows its results on load. Within one visit the results stay: going to Ask AI or another page and coming back keeps them. A reload starts over, and shows results at once only when the address carries a query or a filter. A query restored from the last visit waits in the box over the clear screen. Needs a rebuild and deploy of each site and the hub.
+- **A clear screen until the first Search.** A plain visit to a site's search page, and to the hub's, shows the search bar, the page's intro and the footer: no count, no listing and no results controls, and the line under the bar, "Press Enter or click Search to apply", says what to do. Search with the box empty lists every video, as before. A link that carries a query or a filter (`qt=`, `q=`, `tg=`, a share link, the older filter keys) still shows its results on load. Within one visit the results stay: going to Ask AI or another page and coming back keeps them. A reload starts over, and shows results at once only when the address carries a query or a filter. A query restored from the last visit waits in the box until Search, over the clear screen or under a filter link's results, and it still waits after another page and Back. Needs a rebuild and deploy of each site and the hub.
## [0.10.0] - 2026-09-28
- **A video whose recheck failed shows as possibly missing rather than available.** When a video drops out of its channel's listing it is marked "Missing?" until a recheck says why. A recheck that could not reach the video — a blocked request or a network error — used to clear the mark as if the video had been found. It now leaves "Missing?" in place until a recheck actually reaches the video. Needs a rebuild and deploy of every export site.
diff --git a/plans/export-header-first-search.md b/plans/export-header-first-search.md
@@ -332,7 +332,9 @@ Owns `common/components/theme*` (`themeConfig.ts`, `ThemeProvider.tsx`, `ThemeSc
As built (2026-09-29, on the rulings of that day): S1 only, the summaries download not deferred
(S2 stays a candidate). A link carrying only a filter (`tg=`, a share link, the legacy filter keys),
-like one carrying `qt=`/`q=`, runs on load and shows its results. Step 0: the hub renders
+like one carrying `qt=`/`q=`, runs on load and shows its results. It runs no query, so a stored
+one stays held (release 8), on that load and on later mounts in the visit: the gate and the hold
+are two page-life flags (review M1). Step 0: the hub renders
`SearchResults` (`HubHome` → `TranscriptSearch`), so it has the clear screen and `e2e:hub` joined
the gate; Back restores the listing (measured in `release-14.md`, "Slice S1, as shipped"). Beyond
the four specs this section owns, the ones that read the listing at load were `responsive` (two
diff --git a/plans/release-14.md b/plans/release-14.md
@@ -20,7 +20,7 @@ slice HP added to it on the operator's ruling of the same day. Rules:
|---|---|---|---|
| HP | `homepage/social-visible` | The homepage's social row and one theme toggle in the header at every width, the wordmark's text dropped first on a very small screen and the row scrolling only as the last resort, one shared `SocialLinks` component, larger keys with a focus ring; Changelog in the footer only; the instance cards' names as the site's wordmark; the social icon checked by an allowlist on save and at render; a sized SVG with no viewBox gets one; `featured` ("Show in header") on a social link | `common/components/{SocialLinks,SocialScroll,ThemeRadios,ThemeToggle,ThemeScript,ThemeProvider,Wordmark}.tsx` + `themeConfig.ts`, `common/bin/doctor.ts`, the growth chart, `common/lib/{socialSvg,socialLinks}.ts` + tests, `common/lib/settingsSchema.ts` (the social-link type, parser, docs; the normalizer moved to `socialSvg.ts`), `common/lib/normalizeSocialSvg.test.ts`, `common/lib/{settings,site,homepage}.ts` (the save errors), `common/lib/{homepageSummary,siteColor}.ts`, `homepage/app/components/{Header,Footer,ArchiveCards}.tsx`, `homepage/app/lib/{nav,summary}.ts`, `homepage/app/not-found.tsx`, `homepage/e2e/**` (the fixtures, `helpers.ts`, the new and the rewritten specs), `homepage/playwright.config.ts`, `export/app/components/{MobileMenu,Footer}.tsx` (the `ThemeRadios` swap; the footer's read path), `editor/app/components/SocialLinksField.tsx` + `socialLinksJson{,.test}.ts`, `editor/app/{settings,sites}/actions.ts` (the save errors), `editor/e2e/settings.spec.ts`, `SETTINGS.md`, `SITE.md` |
| Lows, chart gap, T1, H1 (H3 folded in), H2 | `r14/two-grounds-headers` | The final review's Lows; the charts' surface gap; two grounds and each site in its own accent; the export and hub headers carry the social row and the toggle as one group, with an Archilyzer link to the homepage's `#instances` in place of the sites dropdown and the hub link; Changelog to the footer; after its review, the narrow header keeps the name and shows only the marked links (every header) | `common/components/{ThemeProvider,ThemeScript,ThemeToggle,SocialScroll}.tsx` + `themeConfig.ts` (and the deleted `ThemeMenu`, `ThemeRadios`), `common/components/charts/{ChartView,CrossSiteChart,surfaceGap}`, `common/styles/tokens.css`, `common/lib/{brand,accent,siteColor,paths,project,socialSvg,siteSchema,settingsSchema}.ts` + tests, `scripts/next-build-trace.test.mjs`, `export/app/components/{Header,MobileMenu,Footer}.tsx` (and the deleted `SiblingSwitcher`), `export/app/{layout.tsx,globals.css,changelog/page.tsx,lib/brand.ts}`, `export/e2e{,-hub}/**` (the theme, header and branding specs), `export/playwright.config.ts`, `editor/app/{layout.tsx,globals.css,sites/components/SiteForm.tsx}`, `editor/e2e/theme.spec.ts`, `homepage/app/{page.tsx,layout.tsx,globals.css,lib/*,changelog/page.tsx,components/{Header,ArchiveGrowthChart,ArchiveCards}.tsx}`, `homepage/e2e/**`, `homepage/content/docs/operate.md`, `SETTINGS.md`, `SITE.md` |
-| S1 | per the plan | per the plan | per the plan |
+| S1 | `r14/first-search` | A clear screen until the first Search, on every site's search page and the hub's: no results area until the visitor asks (a Search, a profile load, or a link that carries a query or a filter), with the bar's "Press Enter or click Search to apply" line meanwhile; two page-life flags, the hold of release 8 unchanged and the gate new | `common/components/{SearchSessionContext,SearchResults,SearchBar}.tsx`, `export/e2e/first-search.spec.ts` (new), `export/e2e/{browse-all,workspace-shell,charts,restore-no-refire,responsive,tag-chips}.spec.ts`, `export/e2e/helpers.ts` (`showAll`), `export/e2e-hub/federated-search.spec.ts`, `export/CHANGELOG.md`, `plans/export-header-first-search.md` |
**Order:** HP → `r14/two-grounds-headers` → S1. The shared files are the three changelogs'
`[Unreleased]` sections and this record.
@@ -1008,24 +1008,34 @@ and nothing overflows. The `md` layout is slice HP's and unchanged here.
Branch `r14/first-search` off `main` `99d4d76a` (`r14/two-grounds-headers` merged), worktree
`~/Projects/plans-export-header-first-search` (block #4: export e2e 3420, hub e2e 3441, editor test
-3411), one Opus implementer. Scratch files `s1-*` in the job's `tmp`. The rulings, all 2026-09-29,
-not re-opened:
+3411), one Opus implementer. Scratch files `s1-*` in the job's `tmp`.
+
+The rulings, 2026-09-29, not re-opened:
1. S1 only. The summaries download is not deferred; S2 stays a candidate.
2. A link carrying only a filter (`tg=`, `fv=`, `ch=`), like one carrying `qt=`/`q=`, runs on
load and shows its results.
-3. The gate is in `SearchResults`, which the hub mounts too, so the hub gets the same behaviour and
- `e2e:hub` joins the gate.
-4. `ranThisPageLife` feeds a reactive `searchedThisPageLife`; the dead `searchExecuted` goes.
-5. A URL with `qt`, `q` or any filter key counts as asked, at hydration.
-6. `SearchResults` renders nothing until the flag is set; `resultGroups` is still computed. The
- page's intro (the transcript count, the Welcome card) stays.
-7. The bar's "Press Enter or click Search to apply" line also shows before the first Search.
+
+The parent's directions for the build (the plan's S1 steps):
+- The gate is in `SearchResults`, which the hub mounts too, so the hub gets the same behaviour and
+ `e2e:hub` joins the gate.
+- The module-level `ranThisPageLife` feeds a reactive `searchedThisPageLife`; the dead
+ `searchExecuted` goes. As built after the review, the flag the view reads is a second module
+ flag, `askedThisPageLife`, and `ranThisPageLife` keeps `main`'s rule (review M1, below).
+- A URL with `qt`, `q` or any filter key counts as asked, at hydration.
+- `SearchResults` renders nothing until the flag is set; `resultGroups` is still computed. The
+ page's intro (the transcript count, the Welcome card) stays.
+- The bar's "Press Enter or click Search to apply" line also shows before the first Search.
+
+My own choices are under "Decisions the operator could overturn".
| sha | what |
|---|---|
| `93d72a80` | `common:` the results area renders nothing until the visitor asks in this page life; the bar's line shows meanwhile; `searchedThisPageLife`; `searchExecuted` deleted |
| `90043b78` | `export:` `first-search.spec.ts`; one `showAll` helper, pressed where the export and hub specs read the listing at load |
-| _this_ | `plans:` this record; the plan's S1 as built; the export changelog |
+| `dbdaa31b` | `plans:` this record; the plan's S1 as built; the export changelog |
+| `9b01068c` | `common:` two page-life flags, one job each; a filter-only link no longer releases a stored query (review M1); the held-query comment (L1) |
+| `41dc1b4b` | `export:` `restore-no-refire` covers a second mount after a filter-only link (M1) |
+| _this_ | `plans:` the review's fixes recorded (L3, M1, L2 left); the S1 row of the slices table and the Rollout's checks (L4) |
- **What a plain visit shows**, on a site's `/` and on the hub's:
- the search bar with its line, "Press Enter or click Search to apply";
@@ -1045,12 +1055,17 @@ not re-opened:
`tag-chips.spec` already asserts.
- A video (`v`, `t`), a chart's shape (`view`, `cs`), `re` and `vm` do not ask. The Share button
always writes `fv=`, so a shared chart does run.
-- **The flag.** `ranThisPageLife` (module) stays the source of truth across remounts.
- - `searchedThisPageLife` starts from it and is set wherever it is set: at hydration, in
- `commitSearch` and in `applySnapshot`.
- - At hydration the URL is read after `holdRestored` is decided, so a query restored beside a
- filter-only link is still held on that first load.
- - On the server the module variable is never set, so the first client render matches the static
+- **Two flags, one job each** (module state: they survive client-side navigation and reset on a
+ reload):
+ - `ranThisPageLife` is the hold, exactly as on `main` (release 8). It is set by a commit, a
+ profile load, or an active URL query. While it is unset, a stored query is held, on this
+ mount and on every later one.
+ - `askedThisPageLife` is the gate: `ranThisPageLife`, or a URL that asks. `searchedThisPageLife`
+ starts from it and is set wherever it is set: at hydration, in `commitSearch` and in
+ `applySnapshot`.
+ - So a filter-only link shows its results and runs no query. A stored query stays held on that
+ load, and on a later mount in the same visit, which shows the listing under the held query.
+ - On the server neither module variable is set, so the first client render matches the static
HTML. The exported `index.html` now has the line and no results section.
- **Step 0, settled:**
- **The hub renders `SearchResults`:** `HubHome.tsx` → `TranscriptSearch` (common) →
@@ -1088,7 +1103,9 @@ not re-opened:
- `browse-all`: the first test rewritten to assert the clear screen, then the listing;
- `charts` (7 tests), `workspace-shell` (1), `responsive` (the filters sheet and the selection
toolbar), `tag-chips` (3; its `?tg=` tests unchanged);
- - `restore-no-refire`: the held query now shows no results area;
+ - `restore-no-refire`: the held query now shows no results area. Two new tests cover a second
+ mount after a filter-only link, and each holds with no shard fetched: `?tg=` → Use with AI →
+ Back, and `?ch=` → Use with AI → the header's Search link;
- the hub's `federated-search` (8): its first test asserts the clear screen with both archives
in, then the listing.
@@ -1125,6 +1142,11 @@ not re-opened:
above). This is as before S1.
- **A card's video opens without a history entry**, so Back from the modal leaves the site. This is
as before S1.
+- **Enter before the session hydrates (review L2).** Until the manifest is in, a `qt=` link shows the
+ line and not yet its results. Enter in that window commits the empty draft, and `commitSearch`
+ strips `qt` before hydration reads it. The race is on `main` too; the line now shows during it.
+ Gating the line's first-Search half on hydration would close it, at the cost of the line in the
+ static HTML. Left for the operator.
#### Decisions the operator could overturn
@@ -1134,10 +1156,42 @@ not re-opened:
| A link with only a chart's shape (`view=chart&cs=…`, copied from the address bar after an empty Search) opens on the clear screen | count `view`/`cs` as asking |
| A video link (`?v=`) opens the video over the clear screen | count `v` as asking |
| On the hub, the "N of M archives answered" line waits for the first Search with the rest of the results; each failed archive's chip says so, with its Retry, before that | show the line above the gate |
-| A filter-only link marks the page life as asked, as a `qt=` link does, so a stored query restored on a later mount (the hub's `/ask`, a page outside the workspace and back) runs rather than being held | a second flag: shown vs. ran |
+| After a filter-only link, a later mount in the same visit (a page outside the workspace and back, the header's Search link) shows the listing, with the stored query held in the box: the gate is once per page life | the clear screen again on that mount |
| A key's presence asks, not a valid value (`?tg=Not%20An%20Id`) | count only the keys the page applied |
| The Search button keeps its outline look before the first Search; only the line says what to do | fill it as for an unapplied edit |
+#### Review (verdict SHIP AFTER FIXES; `$T/s1-review.md`)
+
+The reviewer also ran the editor suite's specs that drive the export's bar, results and `?v=`
+modal (`export-search`, `export-player-platform-cache`): 21 passed, 0 failed.
+
+| Finding | Fix |
+|---|---|
+| M1: a filter-only link set `ranThisPageLife`, so on a later mount in the same visit (a page outside the workspace and Back; the hub's `/` → `/ask`) a query stored by an earlier visit ran and fetched shards, where `main` held it. The reviewer's probe: `?tg=` → Use with AI → Back fetched 1 shard and showed "Matching videos"; `?ch=` → Use with AI → home did the same | `9b01068c`: two module flags. `ranThisPageLife` is set at hydration only by an active URL query, as on `main`, and `askedThisPageLife` (a run, or `urlAsks`) drives the gate. `41dc1b4b`: `restore-no-refire` seeds a stored query and checks both paths: 0 shards, the box filled, Search marked, and "All videos" |
+| L1: the held-query comment still said "the browse listing" | `9b01068c` |
+| L2: Enter before hydration on a `qt=` link commits the empty draft (a race on `main`; the line now shows during it) | left: "Found and left" |
+| L3: the rulings list mixed the rulings with the plan's steps and my choices | this commit: the rulings, the parent's directions, and my decisions, apart |
+| L4: the slices table's S1 row, and the Rollout's live checks | this commit: the row; three S1 checks under "Live checks" |
+
+#### Gates after the fixes (at `41dc1b4b`; logs `$T/s1-*2.log`, `$T/s1-fix-e2e.log`)
+
+The fix changes only which flag each place sets, so the full suites were not re-run.
+- **tsc** clean before each fix commit (182 s under the machine's load).
+- **common** 2,210/2,210.
+- **e2e**, detached and queued (spec lists in `$T/s1-fix-specs.txt`, `$T/s1-fix-hub-specs.txt`):
+
+ | Suite | Specs | Passed | Failed | Time |
+ |---|---|---|---|---|
+ | export | `first-search`, `restore-no-refire`, `browse-all`, `workspace-shell` | 20 | 0 | 5.1 min |
+ | hub | `federated-search` | 12 | 1 | 2.5 min |
+ | hub | `federated-search`, again | — | — | the dev server did not answer in 120 s |
+ | hub | `federated-search`, again | 13 | 0 | 1.7 min |
+
+ The hub's first run failed on its first test's first line, the archive chip still "loading"
+ after 5 s. That line is unchanged from `main` and comes before anything S1 changed; the machine's
+ load average was 33. On the second attempt the dev server did not start within 120 s. The third
+ run passed 13/13.
+
## Rollout
Release 14 is slice HP (merged, `bfa1ff3c`) and `r14/two-grounds-headers` (after the parent's
@@ -1191,3 +1245,12 @@ hub goes before the sites, because in basic mode they share `export/out`. The si
and `localStorage.getItem("ytdlp-tb:base")` now reads `"light"`.
- The homepage's growth chart has no slash in the page colour through any band. `/stats` in
Area mode has coloured top lines.
+- **S1, a clear screen until the first Search**, is in every site's build and the hub's, so steps 3
+ and 4 above (every site and the hub rebuilt and deployed) carry it. On one site:
+ - a plain visit to `/` shows the search bar, the transcript count and the whole footer, with no
+ scrolling and no listing;
+ - a `qt=` link shows its results on load;
+ - on a site that publishes tags (Anilyzer): Search for a word, then open a `tg=` link in the
+ same tab. Its listing shows with the word held in the box. After another page and Back the
+ word is still held: the box is filled, the Search button is marked, and the results say "All
+ videos", not "Matching videos".