Archilyzer · Source

archilyzer

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

commit d4998ac63b2ca741f3acf1503e3230bfe20fda30
parent d2170bfb377377ccc4fba23afd7c83a28c593707
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sat, 12 Sep 2026 02:20:07 -0400

plans: S1 recorded — sha range, the four divergences, and every gate number

`plans/one-core-phase-2.md` §Record now carries what S2a, S2b, S2c, S3 and
S0-pause need before they branch off this tip.

The four divergences, each because the code said so rather than the plan:
`contract.ts` OWNS `CONTRACT` and `pageFileName` instead of re-exporting them
(the alternative is a TDZ ReferenceError through `manifest.ts`'s module-scope
read, and the note says so with the trace); the four hub spellings collapse to
two types plus a `Pick`, because `corpus.json` spells a published member
`title`/`url` and the input spells it `siteTitle`/`siteUrl` and the wire is
frozen; `shipsPwa` reads `process.env.INSTANCE_MODE` bare, because a `typeof
process` guard would defeat Next's build-time inlining and silently turn the hub
PWA off in the browser; and `stats/` gets URLs through a wider `ArchiveTree`
rather than joining the published `CONTRACT.layers`.

Gate numbers as measured, not paraphrased: common 1077/1077 (from 1051), mcp
205/205, test:scripts 71+1 skipped, tsc clean in six packages, `next build`
compiled, the bench's eight structural rows identical byte for byte on a 1.3 GB
corpus, and compose-site identical modulo the build clock.

`e2e:2origin` gets its own subsection because it is RED AND NOT OURS: it dies in
globalSetup's `pnpm run build:hub`, prerendering `/ask` with "useSearchSession
must be used within a SearchSessionProvider". Re-run on `8fd36c5` itself in this
worktree — same error, same page, same chunks. Export e2e was 172/172 and hub
5/5. Recorded with what the failure still proves: the hub-mode build compiled
and type-checked before the prerender, so `lib/archive/*` resolves in both modes
and `reader-fs.ts` reaches no client chunk.

Also corrected in place: four file paths in §Corrections point at directories
that do not exist (`SearchSessionContext.tsx` / `SearchDataContext.tsx` are under
`common/components/`, `useAskChat.ts` is `export/app/ask/`, `siteRegistry.ts` is
`common/components/`, and the deliberate worker copy is
`common/components/searchIndex.worker.ts`). S2a and S3 would each have burned a
grep on that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mplans/one-core-phase-2.md | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 140 insertions(+), 0 deletions(-)

diff --git a/plans/one-core-phase-2.md b/plans/one-core-phase-2.md @@ -238,3 +238,143 @@ Monitor, commit incrementally, add by path, never `next build` in the primary ch ## Record Filled in as slices ship: sha range, actual gate numbers, every divergence from this plan. + +### S1 — shipped 2026-09-12 + +Branch `one-core/phase-2-s1`, off `c7f7b90` (the `integrate/2026-09-storage-priority` +tip). Five commits, `8d60ad9` → `f085667`, unmerged. **No URL shape moved, no +`corpus.json` byte moved, no CONTRACT version moved, no architecture allow-list +entry added or burned.** + +| commit | what | +|---|---| +| `8d60ad9` | `archive/contract.ts` + `archive/io-stats.ts`; `buildSiteCorpus` calls the URL builders | +| `3c631a2` | the reader: `reader.ts` / `reader-fs.ts` / `reader-hub.ts` + `reader.test.ts` | +| `43be5f9` | `mcp/src/source.ts`: 1,386 lines → 47, a re-export | +| `f2f688d` | the five `lib/search/*` stubs S3 fills | +| `f085667` | `plans/tools/compose-fixture-one-youtube-channel/` | + +#### Four divergences from the plan above, each because the code said so + +**1. `contract.ts` OWNS `CONTRACT` and `pageFileName`; it does not re-export +them.** The plan (and the umbrella) said re-export from `lib/corpus.ts` / +`lib/manifest.ts`. That is a hard ESM failure, not a style question: `corpus.ts` +must import the URL builders (that is the point — one definition of the shape it +emits), `contract.ts` needs `CONTRACT.pagePad` for `pageFileName`, and +`manifest.ts` reads `CONTRACT.manifest` **at module scope**. Any arrangement that +leaves `CONTRACT` in `corpus.ts` closes the loop `corpus → archive/contract → +manifest → corpus`, and the first module to be imported gets +`ReferenceError: Cannot access 'CONTRACT' before initialization`. So the contract +module is the BOTTOM of the stack — it imports only `lib/duplicates.ts` — and +`corpus.ts` / `manifest.ts` re-export the names. Every existing import site +(`from "./corpus"`, `from "./manifest"`, the three `*PageFileName` aliases) is +byte-identical. Verified by importing each of the ten modules in the cycle under +`tsx`, entry-point by entry-point. + +**2. The four hub spellings collapse to TWO types plus one `Pick`, not one.** +The reason is on the wire. `HubMemberInput` and compose-hub's `HubSiteEntry` are +the same direction and the same vocabulary (`siteTitle`/`siteUrl`) — those +genuinely become one, and S2c deletes the local copy. But `HubCorpusSite` is the +entry **published** in a hub `corpus.json`, and it spells the same member +`title`/`url` with two derived pointers beside it. `corpus.json` is frozen, so +the emitted shape cannot be renamed to match the input shape. What the slice does +instead is make mcp's `HubSite` a `Pick<HubCorpusSite, "siteId"|"title"|"url">`, +so the read-back spelling can no longer drift from the published one. + +**3. `shipsPwa` reads `process.env.INSTANCE_MODE` bare, with no `typeof process` +guard** — unlike `io-stats.ts` and the page-cache knob, which are guarded. That +is deliberate and the guard would be a BUG: Next inlines the literal member +expression `process.env.INSTANCE_MODE` into the client bundle at build time, but +a browser has no `process` global, so `typeof process !== "undefined" && +process.env.INSTANCE_MODE === "hub"` evaluates to `false` in a hub build's +browser and silently turns the PWA off. The two knobs that ARE guarded use the +dynamic `process.env?.[name]` form, which Next does not inline — correct for a +server-only setting whose browser answer is "take the default". + +**4. `stats/` gets URLs without joining `CONTRACT.layers`.** It follows the same +manifest → page walk, but `corpus.json`'s `shardScheme` does not document it, so +adding it to the published layer list would be a wire change. The builders take a +wider `ArchiveTree = ContractLayer | "stats"` instead and the layer list stays +frozen. + +One thing the plan did not mention and that had to change: **common's test glob +only reached one directory deep**, so `lib/archive/*.test.ts` would have been +collected by nobody. `common/package.json`'s `test` script now also globs +`{lib,…}/*/*.test.ts`. Nothing else lives two deep today, so no existing test +moved in or out. + +#### Gates + +- `pnpm -r exec tsc --noEmit` — clean in all six packages, after every commit. +- `pnpm --filter yt-dlp-transcript-common test` — **1077 passed / 0 failed** + (baseline 1051 at `c7f7b90`, + 9 `contract.test.ts`, + 17 `reader.test.ts`; + none lost). The architecture test passes with its allow-list untouched. +- `pnpm --filter yt-dlp-transcript-mcp test` — **205 passed / 0 failed**, + unchanged. +- `pnpm test:scripts` — **71 passed / 1 skipped**, unchanged. +- `pnpm --filter export exec next build` — **compiled successfully**, 11 static + pages. This is the only test that `reader-fs.ts` is unreachable from a client + module. (The one warning is the pre-existing NFT trace on + `next.config.ts → channelMedia.ts → controller/channels.ts → app/offline/page.tsx` + — the documented reason this app does not use `output: "standalone"`.) +- `pnpm --filter yt-dlp-transcript-mcp bench --repeat 1 --force`, before at + `c7f7b90` and after, both `--local` the same composed 1.3 GB site, identical + fingerprint (3,358 summaries videos; stats, duplicates and digests all + present). **Every structural counter identical, byte for byte:** + + | case | reads | bytes parsed | + |---|---|---| + | cold-channels | 0 → 0 | 0 → 0 | + | rare, whole corpus | 170 → 170 | 1,335,885,512 → 1,335,885,512 | + | common, whole corpus | 170 → 170 | 1,364,679,296 → 1,364,679,296 | + | channel-scoped | 4 → 4 | 28,847,054 → 28,847,054 | + | date-scoped (filter-first) | 32 → 32 | 213,451,503 → 213,451,503 | + | state-scoped (filter-first) | 9 → 9 | 73,399,988 → 73,399,988 | + | enumerate, whole corpus | 170 → 170 | 1,364,679,296 → 1,364,679,296 | + | get_transcripts × 20 ids | 4 → 4 | 28,847,054 → 28,847,054 | + + The per-case scan notes match too, `pruned` flags included (date-scoped 28 + pages pruned, state-scoped 9). Wall ms is noise and is not quoted. +- **compose-site byte-identity** over the FACTS.md fixture recipe, at `c7f7b90` + and at the tip: 16 files under `public/`, 7 under `index/`, identical modulo + the build clock. The artifact and the normalising diff are committed at + `plans/tools/compose-fixture-one-youtube-channel/`; re-running the README's own + commands into two fresh temp dirs reproduces it. +- **e2e**, behind the queue lock from the worktree (port block #9 — + 3901/3911/3920): export `e2e` **172 passed / 0 failed**, `e2e:hub` + **5 passed / 0 failed**. `e2e:2origin` **could not run**, and the reason is + not this slice — see below. + +#### `e2e:2origin` is red on the base commit, for a reason S1 does not touch + +It never reaches a spec. `playwright.2origin.config.ts:16` calls +`e2e-2origin/globalSetup.ts:142`, which shells `pnpm run build:hub`, and the hub +build dies prerendering `/ask`: + +``` +Error: useSearchSession must be used within a SearchSessionProvider +Export encountered an error on /(workspace)/ask/page: /ask, exiting the build. +``` + +**Verified on `c7f7b90` itself** — `git switch --detach c7f7b90` in this +worktree, `pnpm --filter export run build:hub`: the same error, on the same +page, from the same two chunks (`SearchSessionContext`, `AskChat`). This is the +"hub `/ask` broken on main blocks `e2e:2origin`" failure already recorded with +the export responsive redesign, and nothing in S1 is in that path: the reader +never enters a React tree, and the provider is +`common/components/SearchSessionContext.tsx` — S2a/S3's file, untouched here. +(Note for those slices: four paths in §Corrections above are wrong, checked +2026-09-12 — `SearchSessionContext.tsx` and `SearchDataContext.tsx` are under +`common/components/`, not `export/app/lib/`; `useAskChat.ts` is +`export/app/ask/useAskChat.ts`; `siteRegistry.ts` is `common/components/`, not +`common/lib/`; and the deliberate worker copy is +`common/components/searchIndex.worker.ts`, not `export/app/lib/`. The files all +exist; only the directories in the plan are wrong.) + +Worth noting what it DOES prove: the site-mode `next build` passed here and the +HUB-mode one compiled and type-checked before the prerender — so the bundle +resolves `lib/archive/*` in both modes and never pulls `reader-fs.ts` into a +client chunk. The prerender is the only step that fails, and it fails at base. + +`plans/tools/jeralyzer-corpus-2026-09-12.json` was not re-fetched; the live diff +is S3's gate, after the search pipeline lands.