commit 79bef42cb078cf903a38285d855607e37d7e0e07
parent 91a7fe71d36af0f267842f5ae48ebe085f5d6bc9
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sat, 26 Sep 2026 03:11:27 -0400
plans: release 10 slice L1 as shipped — hub lows: subs retry: false, /ask honours the scope chips, one order and one colour for the official instances (the homepage's), playwright unlinks sw.js before its copy (proved: the primary's sw.js kept size, mtime and md5 through a worktree export run)
Gates on 91a7fe71: tsc clean per commit; common 1,917, editor unit 79,
scripts 162 + 1 skip, mcp 219; editor, export site, export hub and homepage
builds ok; e2e export 204, hub 22, 2origin 3, homepage 27; the three new
hub tests red on the base source.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
| M | plans/release-10.md | | | 120 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 120 insertions(+), 0 deletions(-)
diff --git a/plans/release-10.md b/plans/release-10.md
@@ -222,6 +222,126 @@ 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).
+### Slice L1, as shipped — hub lows (2026-09-26)
+
+Branch `r10/hub-lows` off `main` `5dfc9c3a`, worktree `/home/user/Projects/r10-hub-lows` (port
+block #6). All five items are done. Item 4 follows the homepage: the code gave no reason to keep
+alphabetical (below).
+
+1. **`retry: false` on the federated subs manifest** (`common/components/SearchDataContext.tsx`,
+ the `subsQueries` of `MultiSiteDataProvider`). Readiness waits for the subs query to settle. A
+ member with no live chat answers 404, and the client default (`retry: 1`, `QueryProvider.tsx`)
+ held its chip at `loading` for one more second. The single-site `useSubsManifest("")` shares the
+ query key shape but never an origin the hub uses, so it is unchanged. **Cost, accepted in the C2
+ re-read:** a transient network error on a member's subs manifest is also not retried. Its chip
+ reads `ready` without that member's live chat, and no Retry is offered, because the chip is not
+ `failed`.
+2. **`/ask` honours the scope chips.** `AskHub` builds its list with `useFederatedSites()`, the same
+ hook `HubHome` uses, so an archive switched off on the front page is not fetched on `/ask`. It is
+ still not `progressive`: the chat waits for every archive in scope to settle.
+3. **One colour per archive, on every surface.** `officialInstances(members, summary)`
+ (`common/lib/hubSummary.ts`, pure) resolves each official instance's colour: the site's own
+ accent, else the summary's, else `seriesColor()` at its index in the summary, the colour of its
+ homepage card and chart layer. It replaces `ArchiveShelf`'s private fallback (C1). A member the
+ summary does not name takes the colours after the summary's, so it cannot share a colour with a
+ named instance. Before, an unnamed member took its card index, which could collide.
+ `useHubSites()` (`export/app/components/hub/useHubSites.ts`) is the one list every hub surface
+ reads, each official entry wearing its resolved colour: the shelf's cards, the scope chips' dots,
+ the result cards' left edge (`accentOf`), and the channel groups' dots (`FiltersPanel`,
+ `HubOfflineManager`). An added archive with no accent still has no stripe in the results. Its
+ shelf card keeps `var(--brand)`.
+4. **One order.** `officialInstances` also orders the official instances by the summary's `sites`,
+ the homepage's order (transcripts, most first: `homepageSummary.ts:160`). A member the summary
+ does not name follows, in `hub-sites.json` order, and with no summary that order stands. The
+ cards, the chips, the filter panel's groups and `/ask` all follow it.
+ **Why not alphabetical:** nothing in the code depends on `hub-sites.json`'s order. The channel
+ list sorts by origin itself, `corpus.json`/`llms.txt` keep their own (compose-time, unchanged),
+ and the merge sorts records by date. The one cost is that the order moves when two instances'
+ transcript counts cross. The homepage already does that, and the hub now does it on the same
+ build.
+ **No reorder flash:** `useHubSites` lists the official instances only once `/hub-summary.json`
+ has settled (`useHubSummary` now returns `{summary, settled}`), whether it was found, missing or
+ unreadable. Both files are small, same-origin and requested together (the hub service worker
+ passes both straight through), so the wait is the gap between two requests already in flight.
+ `HubStats` now counts the provider's list (`federation.sites`) rather than the registry. It
+ therefore never reads "Searching 0 archives" while that list waits.
+5. **`export/playwright.config.ts` unlinks `public/sw.js` before copying `site-sw.js`**
+ (`fs.rmSync(SW_DEST, {force: true})`). In a worktree that path is a link into the primary, and
+ `copyFileSync` writes through a link. Proof below.
+
+| sha | what |
+|---|---|
+| `1f852979` | `export: playwright.config.ts` unlinks `public/sw.js` before copying the site worker (item 5) |
+| `6fa6bd3b` | `common: officialInstances`: the summary's order and one colour each, plus 4 unit tests (items 3–4) |
+| `cf5455c0` | `hub:` `retry: false` on the federated subs manifest; e2e counts one subs request at `ready` (item 1) |
+| `90ec10a1` | `hub:` `useHubSites` / `useFederatedSites`; `ArchiveShelf`, `HubHome`, `HubStats`, `AskHub` read it; `useHubSummary` `settled`; e2e for order + colour (items 3–4) |
+| `21be8700` | `hub: /ask` honours the scope chips; e2e (item 2) |
+| `86d686fb` | `export: [Unreleased]` (a new section above `[0.9.0]`) |
+| _this_ | `plans:` this record |
+
+**Gates**
+- **tsc:** clean before every code commit. Five runs, 37–91 s (`l1-tsc-1..5.log`). `cf5455c0` was
+ staged as a subset of a tree that had passed tsc (`l1-tsc-3`), with no type dependency on the
+ unstaged rest.
+- **Unit and script tests** on `86d686fb`: common **1,917/1,917** (1,913 + 4, all `officialInstances`),
+ editor unit **79/79**, `test:scripts` **162 + 1 skip**, mcp **219/219**.
+- **Builds** on `86d686fb` (`l1-gates.log`), with `export/public` seeded under sh (no dangling links)
+ and `homepage/public` data copied (60 MB):
+ - editor ok (44 s);
+ - export site ok (30 s), `data-accent="signal"`;
+ - export hub ok (31 s), `dark data-base="dark"`, `out/ask/index.html` present;
+ - homepage ok (22 s).
+- **e2e on `86d686fb`** (`l1-e2e.log`):
+ - export full **204 passed**, 9.6 min;
+ - `e2e:hub` **22 passed** (19 + the 3 new tests), 46.1 s, after 18.4 min in the queue behind L2;
+ - `e2e:2origin` (`TWO_ORIGIN_REBUILD=1`; the seven composed entries + `sw.js` swapped for copies,
+ relinked after) **3 passed**, 35.7 s;
+ - homepage full **27 passed**, 42.6 s.
+ - During development: `e2e:hub` 21/21 (56.8 s, with the item-1 and items-3–4 tests). Then
+ `federated-search` + `ask` + `ask-grounding` went 9 passed, 1 failed, and 10/10 after a fix to
+ the new `/ask` test's own wait. `AskChat`'s composer reads ready for the instant before the
+ registry loads, so the test now waits for Origin A's fetch first (below).
+- **Red on the base source** (`l1-redgreen.log`). The three new tests were run with the six source
+ files checked out at `5dfc9c3a` and the specs kept. Each failed for the reason it exists:
+ - the subs test made **2** subs requests by `ready` (expected 1);
+ - the order test's first card read "Origin A" (expected "Origin B"), so its colour assertions
+ never ran;
+ - `/ask` made **7** requests to the switched-off Origin B (expected none).
+
+ The source was restored and the tree left clean.
+- **Item 5, proved.** Before the export run the worktree's `export/public/sw.js` was a LINK to the
+ primary's. The config loaded, and the worktree's became a plain 11,172 B file (md5 = `site-sw.js`).
+ The primary's `export/public/sw.js` read **10,027 B, mtime 2026-09-25 20:47:37.183119759, md5
+ `55cbf381…`** before the run, during it (02:38), after it, and after every later run. The other
+ seven composed files kept their sizes and mtimes throughout (`l1-primary-public-before.txt`,
+ `l1-gates.log`, `l1-e2e.log`).
+- Numbers tools: none. No `settings.json`, `site.json` or `config.json` key changed. `hub-sites.json`
+ and `hub-summary.json` are unchanged on disk: the order is applied in the browser.
+
+**Found and left**
+- **The `/ask` composer reads ready before the hub's list loads** (pre-existing). With no archives
+ listed yet, the provider has nothing in scope, so it counts as settled and `summariesReady` is
+ true for that instant. A question sent then would ground in nothing. `ask.spec` relies on the
+ empty-hub case being ready. The window used to end when `hub-sites.json` arrived. It now ends
+ when both it and `hub-summary.json` have arrived: same-origin, and requested together. A fix would
+ be for `AskHub` to count as ready only once the registry's built-ins have loaded. That needs a
+ "loaded" signal from `siteRegistry.ts`, which does not expose one.
+- **`/ask` does not show which archives are in scope.** The front page has the chips; `/ask` silently
+ follows them. A visitor who switched an archive off weeks ago gets answers from the rest. A
+ one-line "Searching N of M archives — change on the front page" would need a copy ruling.
+- **After the rollout the hub's cards and the homepage's cards differ in colour.** The hub prefers a
+ site's own accent (C1, kept); the homepage's `ArchiveCards` deliberately draws only
+ `seriesColor(i)` so a card matches its chart layer. The two sites now list the instances in the
+ same order.
+- **FACTS is stale after merge:** "Replace `export/public/sw.js` with a copy before any export e2e in
+ a worktree" (Release 9 facts, "Worktree e2e can write INTO the primary") no longer holds for
+ `sw.js`. It still holds for the compose outputs `e2e:2origin` writes. Not edited here (a shared
+ file).
+
+`[Unreleased]` bullets: `export/CHANGELOG.md` (three: order and colour, `/ask` scope, subs).
+`editor/CHANGELOG.md` and `homepage/CHANGELOG.md` are unchanged, because neither app changed. The
+item-5 harness fix is not in the public changelog.
+
## Rollout
Nothing is rolled out. The live :3001 editor still runs `0213f6c8` (the pre-brand build); the five