Archilyzer · Source

archilyzer

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

commit 79ca2b0f4b0d5309ee6b0bb1385ccf9efc6405ad
parent cac99e26d8c7c8917c911623256d21c593bff02c
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sat, 26 Sep 2026 02:29:23 -0400

hub: retry: false on the federated subs manifest — a member with no live chat is ready ~1 s sooner

Readiness waits for the subs query to settle, and a member with no live
chat answers 404; the client default (one retry after ~1 s) held its chip
at "loading" for that second. The e2e counts one subs request per member
when the chip turns ready (two before). Release 9 C2 re-read LOW.

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

Diffstat:
Mcommon/components/SearchDataContext.tsx | 6++++++
Mexport/e2e-hub/federated-search.spec.ts | 31++++++++++++++++++++++++++-----
2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/common/components/SearchDataContext.tsx b/common/components/SearchDataContext.tsx @@ -343,11 +343,17 @@ export function MultiSiteDataProvider({ // merged below only once the archive is ready, so all of an archive's // contributions land in the search in one re-run. A posts 404 (no posts // corpus) resolves to an empty manifest, not a failure. + // + // The subs manifest is asked for ONCE (`retry: false`). A member with no live + // chat has none, and its 404 is its answer: the client default (one retry, + // after ~1 s) only held that archive at "loading" for another second, since + // readiness waits for this query to settle and an error settles it. const subsQueries = useQueries({ queries: sites.map((s) => ({ queryKey: ["subs-manifest", s.origin], queryFn: () => readerFor(s.origin).readSubsSiteManifest(), enabled: inScope(s), + retry: false, })), }); const postsQueries = useQueries({ diff --git a/export/e2e-hub/federated-search.spec.ts b/export/e2e-hub/federated-search.spec.ts @@ -7,6 +7,8 @@ import { expect, test, type Page, type Route } from "@playwright/test"; // says "1 of 2 archives answered" with a Retry, and the healthy member's results // still render), Retry, progressive readiness (a slow member does not hold the // fast one's results back), and a card naming its source archive in text. +// Release 10 (L1): a member with no live chat is ready on its first subs +// answer (a 404 is not retried). const ORIGIN_A = "http://localhost:4598"; const ORIGIN_B = "http://localhost:4599"; @@ -62,16 +64,17 @@ function pageOf(m: Member) { } // Every request to a member origin is counted; `pages` decides how its -// summaries page is answered. +// summaries page is answered, `subs` whether it has a subs manifest at all. type PageMode = "ok" | "abort" | "hold"; type MemberMock = { requests: string[]; pages: PageMode; + subs: "ok" | "missing"; held: Route[]; }; async function mockMember(page: Page, m: Member): Promise<MemberMock> { - const mock: MemberMock = { requests: [], pages: "ok", held: [] }; + const mock: MemberMock = { requests: [], pages: "ok", subs: "ok", held: [] }; await page.route(`${m.origin}/**`, async (route) => { const url = new URL(route.request().url()); mock.requests.push(url.pathname); @@ -86,7 +89,7 @@ async function mockMember(page: Page, m: Member): Promise<MemberMock> { } return fulfillJson(route, pageOf(m)); } - if (url.pathname === "/subs/manifest.json") { + if (url.pathname === "/subs/manifest.json" && mock.subs === "ok") { return fulfillJson(route, { version: 4, channels: [], @@ -95,8 +98,8 @@ async function mockMember(page: Page, m: Member): Promise<MemberMock> { generatedAt: "2026-01-01T00:00:00.000Z", }); } - // No posts, no aliases: a clean 404 (a member without a posts corpus is - // not a failure). + // No posts, no aliases (and, when `subs` is "missing", no live chat): a + // clean 404 (a member without that corpus is not a failure). return route.fulfill({ status: 404, headers: CORS, body: "" }); }); return mock; @@ -272,4 +275,22 @@ test.describe("hub federated search — scope, per-archive state, attribution", await expect(status(page)).toContainText("Origin B did not"); await expect(resultFrom(page, C)).toHaveCount(1); }); + + test("a member with no live chat is ready on its first subs answer: a 404 is not retried", async ({ + page, + }) => { + const mocks = await setup(page); + mocks.b.subs = "missing"; + await page.goto("/"); + + // Ready means every feed of the archive has settled, subs included. With a + // retry, the 404 settled only on the second answer (~1 s later), so the + // chip turned ready with two requests on record; now there is one. + await expect(chip(page, B)).toHaveAttribute("data-status", "ready"); + await expect(resultFrom(page, B)).toHaveCount(1); + const subsAsks = (m: MemberMock) => + m.requests.filter((p) => p === "/subs/manifest.json").length; + expect(subsAsks(mocks.b)).toBe(1); + expect(subsAsks(mocks.a)).toBe(1); + }); });