commit cd038193aa42f2030b25774975f457d2426949f0
parent ceadca718377344e68d77a87011000faccbed55f
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 02:44:12 -0400
plans: slice O1 as shipped — the built-ins flag, /ask waits for the hub's list, the three DRAFT copy lines (NO_ARCHIVES_ON_HUB, LIVE_CHAT_MISSING, ASK_SCOPE_LINE), the live-chat Retry that keeps its archive ready, normalizeSocialSvg themes child fills, mcp README env lines; gates, bites, found and left
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
| M | plans/release-11.md | | | 157 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 157 insertions(+), 0 deletions(-)
diff --git a/plans/release-11.md b/plans/release-11.md
@@ -187,6 +187,163 @@ waits on the :3001 restart. After it:
2. **Deploy homepage**, or `pnpm ops deploy-homepage --wait`. The job's log ends with
`[deployed] https://<hash>.archilyzer.pages.dev`.
+### Slice O1, as shipped — hub lows and the DRAFT copy (2026-09-28)
+
+Release 10's hub lows (`release-10.md`, "Slice L1, as shipped", both "Found and left" lists) and
+STATE's x.com icon low, with the three lines that need words shipped as DRAFT constants for a
+morning ruling. Branch `r11/hub-lows` off `main` `2162db92`, merged up to `baaa4b47` (O4) before
+the first commit; worktree `/home/user/Projects/r11-hub-lows` (port block #4). One Opus
+implementer. Scratch files `o1-*` in the job's `tmp/overnight`.
+
+**What shipped.**
+1. **A "built-ins loaded" flag** (`common/components/siteRegistry.ts`). `builtinsLoaded` turns true
+ once `/hub-sites.json` has been answered — a list, `[]`, a 404, a 500, a network error, bad JSON
+ or a non-list. It is set in the same update that adds the built-ins (one `emit`), so no reader
+ sees "loaded" over a list that lacks them. `useRegistry()` returns it, and `getBuiltinsLoaded()`
+ reads it outside React. `loadBuiltins` is exported, and so are the store's `getRegisteredSites` /
+ `subscribeRegistry`. The one-shot guard is now its own `builtinsRequested`: before, a flag named
+ `builtinsLoaded` was set at the START of the fetch. The file is still never retried.
+2. **`/ask` counts as ready only once the hub's list is in.** `useHubSites()` returns
+ `listed = builtinsLoaded && summary settled`, and `useFederatedSites()` passes it through.
+ `MultiSiteDataProvider` takes a `listed` prop (default `true`), and
+ `allSettled = listed && …`. `federation` carries `listed`. `AskHub` passes it; the front page
+ does not, so it is unchanged. The archives a browser added are read from storage at once, and
+ `/hub-sites.json`'s arrive a moment later. Before this, a question sent in between grounded in
+ the added archives alone. Their fetches still start at once; only readiness waits.
+3. **Three DRAFT copy lines** (`export/app/ask/hubScopeCopy.ts`), each marked
+ `// DRAFT (release 11, 2026-09-28) — awaiting the operator's ruling`:
+ - **`NO_ARCHIVES_ON_HUB`** (`:16`). On `/ask`, once the list is in and it is empty (a hub whose
+ `hub-sites.json` is `[]` or failed, with none added here), the composer shows it in
+ `data-testid="ask-blocked"` (`role="status"`), the box and Ask disabled. It replaces
+ "Loading transcripts…" forever. Its [bracketed] words link to `/`, where the add form is.
+ - **`LIVE_CHAT_MISSING`** (`:24`). The note on a scope chip (`data-testid="hub-scope-live-chat"`)
+ for a READY archive whose subs manifest settled in error. Beside it is a Retry, accessible name
+ `Retry live chat from <title>`: `federation.retryLiveChat(origin)` refetches
+ `["subs-manifest", origin]` exactly, and nothing else. The Retry is disabled while it runs.
+ - **`ASK_SCOPE_LINE`** (`:33`). A function of `(inScope, total)` so the plural and the words are
+ one editable constant. `AskScope` renders it (`data-testid="ask-scope"`) between `/ask`'s
+ header and the chat, once the list is in, whenever at least one archive is in scope. Returning
+ `null` hides it: e.g. `inScope === total ? null : …` shows it only when some archives are off.
+ Its [bracketed] words link to `/`.
+ `NO_ARCHIVES_IN_SCOPE` (L1's draft, still unruled) also waits for the list now, so it no longer
+ flashes while the built-ins arrive. `LinkedCopy` (`export/app/ask/LinkedCopy.tsx`) is the one
+ renderer of a copy line with its link. It replaces AskChat's inline split.
+4. **The archive stays ready through a live-chat Retry.** TanStack puts an errored query that has
+ no data back to `pending` while it refetches (`fetchState`, query-core 5.100.5). So
+ `isError && !isFetching`, L1's rule, would have taken the archive and its videos out of the
+ search for the length of the Retry. The subs rule now keys on `errorUpdateCount > 0` with no
+ data: the count moves only once the query's own retry is spent, and it survives a refetch.
+ `FederatedSiteState.liveChatMissing = {retrying, error?}` rides in `statusKey`.
+5. **`normalizeSocialSvg` themes every fill colour** (`common/lib/settingsSchema.ts`).
+ - **What changes.** Every `fill` attribute, and every `fill:` in a `style`, becomes
+ `currentColor`: the root's (a root colour too, not only an absent one) and every child tag's.
+ The exceptions are `none`, `transparent`, `currentColor`, `inherit` and `url(…)` paint (with or
+ without a fallback).
+ - **What is kept.** The tags inside a `<mask>` (white and black there are coverage, not colour),
+ `fill-rule` / `fill-opacity`, strokes and gradient stops.
+ - **The operator's icons.** x.com: the root's `fill="none"` stays, and the path's `white` becomes
+ `currentColor`. Kiwi Farms: the gradient drop and its stops are unchanged, and the `#414042`
+ dot follows the footer's colour.
+ - The function is idempotent.
+6. **`mcp/README.md`.** The `claude mcp add` and `mcp.json` examples carry
+ `ARCHILYZER_EDITOR_URL=http://localhost:3001` and `WORKER_TOKEN=…`, marked optional for
+ `fetch_clip` in AGENTS.md's words (the URL's default and the no-editor answer added). mcp has no
+ changelog.
+
+| sha | what |
+|---|---|
+| `0a2ba7b9` | `common:` siteRegistry `builtinsLoaded` (+ `getBuiltinsLoaded`, exported `loadBuiltins`), set in the same update as the built-ins; +8 unit tests |
+| `615563d1` | `common: normalizeSocialSvg` themes every fill colour, children's too; `none` / `url()` / mask fills kept; +8 unit tests |
+| `2f9b5ab9` | `hub:` `listed` through `useHubSites` → `MultiSiteDataProvider` → `federation`; `liveChatMissing` + `retryLiveChat` (the `errorUpdateCount` rule); the three DRAFT constants; `AskScope`, `LinkedCopy`; the chip note; e2e `ask.spec` (+3), `federated-search.spec` (+2, and the scope line in the chips test) |
+| `2ef9e745` | `mcp:` README — the two `fetch_clip` env lines in both examples |
+| `db555285` | `export: [Unreleased]` above `[0.9.4]` (five bullets) |
+| _this_ | `plans:` this record |
+
+**Gates** (logs `o1-*.log`):
+- **tsc:** clean (`o1-tsc-2.log`, 34 s, on the committed tree). `0a2ba7b9` and `615563d1` are
+ additive subsets of it, with no type dependency on the rest.
+- **Unit and script tests** (`o1-units-1.log`):
+ - common **2,057/2,057** (2,041 + 16);
+ - editor unit **85/85**;
+ - `test:scripts` **175 + 1 skipped**;
+ - mcp **269/269**.
+- **Builds** (`o1-run-1.log`), with `export/public` seeded per path (17 links, none dangling):
+ - export site ok (24 s), `data-accent="signal"`;
+ - export hub ok (24 s), `dark data-base="dark"`, `out/ask/index.html` present.
+ - Editor and homepage builds were not run: neither app changed. The editor's two save paths call
+ `normalizeSocialSvg`, and its output for their e2e inputs is byte-identical (unit test 4).
+- **e2e** on `db555285` (`o1-run-1.log`; no wait in the queue):
+ - `e2e:hub` in full: **31 passed** (26 + 5 new), 1.3 min;
+ - export `site-branding` + `ask-chat` + `ask-workspace`: **36 passed**, 1.9 min. The footer's
+ social row is `site-branding`'s. No export spec reads the icons' colour, and the fixture's icon
+ is already `currentColor`.
+ - `e2e:2origin` (`TWO_ORIGIN_REBUILD=1`, the links in place, no swap): **3 passed**, 36.7 s.
+- **The primary's `export/public`:** all **380 files**' path, size, mtime and md5 were identical
+ before and after the three runs (`o1-primary-before-1.txt` vs `-after-1.txt`, `diff` empty).
+ After 2origin the worktree held its own `hub-sites.json`, `sw.js`, `robots.txt`, `_headers`,
+ `corpus.json` and `llms.txt`. The seed relinked them.
+- **Screens** (`o1-shots/`, from a temporary spec, deleted after): the chip note and its Retry, the
+ scope line under `/ask`'s header, and the zero-archive line under the composer.
+- **Numbers tool:** none. No `settings.json`, `site.json` or `config.json` key changed.
+
+**They bite:**
+- **Unit, against `baaa4b47`'s source** (a scratch copy of the old `common/`, `o1-bite-units.log`):
+ - `siteRegistry.test.ts` fails to load (no `getBuiltinsLoaded`).
+ - `normalizeSocialSvg.test.ts` **4 of 8 fail**: x.com, Kiwi Farms, the colour spellings and the
+ mask.
+ - The other four pin behaviour the old code already had: unchanged icons, the bare icon's exact
+ output, idempotency and the refusals. They are guards and pass on it by design.
+- **Mutations of the new code:**
+ - the flag set at the start of `loadBuiltins`: 7 of 8 fail;
+ - the flag set only when the file listed something (the old early returns): 6 of 8;
+ - the flag announced in its own update before the built-ins join: 7 of 8;
+ - `none` themed too: the x.com and "unchanged" tests fail;
+ - masks not skipped: the mask test fails.
+- **e2e, against `baaa4b47`'s seven source files,** with the new specs and `hubScopeCopy.ts` kept
+ (`o1-bite-e2e.log`, restored by a trap, tree clean after): **6 failed, 10 passed.** The six are
+ every new-behaviour test:
+ - the three zero-archive tests: no `ask-blocked` line;
+ - the chips test: no `ask-scope` line;
+ - the listed test: `Loading transcripts…` gone, because the chat was ready over the added archive
+ alone;
+ - the live-chat test: no note.
+
+**Found and left**
+- **The icon fix applies on save.** `normalizeSocialSvg` runs when a link is saved, not when the
+ footer renders. The stored x.com icon in `settings.json` keeps `fill="white"` until the Settings
+ form is saved once. Any save re-normalizes every link, with no re-paste. Then every site and the
+ hub need a rebuild. A render-side pass (as `sizeSocialSvg` does for sizes) would fix stored
+ icons with no save, but `Footer.tsx` and `sizeSocialSvg` are outside this slice.
+- **The Kiwi Farms dot changes colour.** It was a fixed `#414042`, barely visible on Dark, and now
+ follows the footer's colour. The green gradient drop is unchanged.
+- **Strokes are not themed.** An icon drawn with `stroke="white"` would still vanish. Neither of the
+ operator's icons is stroked.
+- **A failed `/hub-sites.json` reads as "no archives yet"**, the same as an empty one, and the
+ registry still never retries: a reload asks again.
+- **The front page does not wait for the list.** It is progressive and does not take `listed`, so
+ it still settles for the instant before `hub-sites.json` arrives. This is pre-existing, and its
+ chips show the loading.
+- **A live-chat Retry that fails again brings the note back,** with its Retry enabled
+ (`errorUpdateCount` moves, `isFetching` clears). No e2e covers that second failure. TanStack
+ clears `error` during the refetch, so the Retry's title says only "Try again" while it runs. The
+ button is disabled then.
+- **FACTS is stale after merge** (release 10 facts, "L1 — hub lows"): "nothing says so (a new low)"
+ (the subs rule) and "A hub with zero archives therefore reads 'Loading transcripts…' forever"
+ are both closed here, and the subs rule's anchor now keys on `errorUpdateCount`. Not edited here
+ (a shared file).
+
+`[Unreleased]` bullets: `export/CHANGELOG.md` (five: `/ask` with no archives, its wait for the
+hub's list, its scope line, the chip's missing live chat, the social icon). `editor/CHANGELOG.md`
+and `homepage/CHANGELOG.md` are unchanged, because neither app changed.
+
+**DRAFT copy for the morning ruling** (`export/app/ask/hubScopeCopy.ts`):
+- `:16` `NO_ARCHIVES_ON_HUB` = "This hub has no archives yet. Add one on the [hub's front page] to ask."
+- `:24` `LIVE_CHAT_MISSING` = "live chat didn't load"
+- `:33` `ASK_SCOPE_LINE(n, m)` = "Searching {n} of {m} archive(s) — [change on the front page]." (shown
+ whenever at least one archive is in scope, all included)
+- Still owed from release 10: `:7` `NO_ARCHIVES_IN_SCOPE` = "No archives selected. Choose some on the
+ [hub's front page] to ask."
+
## Rollout
Nothing is rolled out tonight. The morning runbook lists what is owed: the :3001 editor restart,