Archilyzer · Source

archilyzer

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

commit e34d473336a9c0c5dd10da54ed9a38c411192bda
parent 4495d56a049dd94c04716bdf4f5b65d79a8fe6d9
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue, 22 Sep 2026 17:03:55 -0400

plans: STATE for 2026-09-22 — the release, and two notes that expired

Rewritten head per README: the curated-tags rollout (including the three seed
patterns that were wrong against the real corpus, which is the part worth
carrying forward), `storage/debts-1`, `feat/filtered-channel-followups`, and
this release's three branches with per-item shas, the measured gates, the
housekeeping, and the four post-release operator steps in order.

Two standing notes are retired because following them would now waste time:

- The `editor/content` e2e blocker is deleted, not annotated. The symlink is
  gone and the path is gitignored, so `pnpm e2e` runs in the primary checkout
  and the "add by path" discipline is no longer load-bearing. Leaving a
  blocker in place after it lifts costs every future session a worktree.
- The four-legacy-pause-fields bullet moves to done. Its warning was heeded —
  `defaultHeldFor` makes backfill default `true`, so the lane gate B fences is
  off twice over — and the invariant is asserted through `sanitizeAutoQueue`,
  where a reader hits it, rather than through the default in isolation.

Decisions added: s0-pause ships now rather than waiting on the live file,
2origin was fixed rather than skipped (a red run there is a regression now),
hub tag federation is out, and only merged branches were deleted. Commands
updated to the measured numbers, with the `run e2e*` scripts named instead of
`exec playwright test` — the scripts are what take the machine-global lock.

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

Diffstat:
Mplans/STATE.md | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 200 insertions(+), 21 deletions(-)

diff --git a/plans/STATE.md b/plans/STATE.md @@ -3,9 +3,148 @@ The working memory for the local-AI derived-corpus work. Rewritten at the end of every session, before context is cleared. See [`README.md`](README.md) for the protocol. -**Last updated:** 2026-09-20 — **storage locations S5 + S6 shipped** on branch +**Last updated:** 2026-09-22 — **the release *curated-tags follow-ups + debts sweep* is on +`main` @ `766e0873`.** Base `4ac8ceda`; three branches merged in the order +`1a011d96` alone → `tags/rules-and-ops` (`9837f066`) → `export/tags-followups` (`cb254919`) +→ `editor/debts` (`766e0873`). Anchors for every seam are in +[`FACTS.md`](FACTS.md#the-2026-09-22-release--curated-tags-follow-ups--debts-sweep-main--766e0873). + +**Final full-suite run on 766e0873: PENDING-FINAL-GATES** + +### Curated per-video tags — shipped, reviewed, deployed to Anilyzer + +The plan is [`curated-tags.md`](curated-tags.md); it is now a shipped design, not work in +flight. `transcripts/tags.json` is the authoritative vocabulary AND every assignment, written +through the ONE path `applyTagAssignments` (`common/lib/curatedTagsStore.ts`) by all three +writers (editor UI, `pnpm ops`, umtool); the MCP gained a read-only `list_tags` and a `tags` +filter and no write tool. The record field is **`curatedTags`**, never `tags`. S1.1 froze the +model (`d8c9d252`), then S1 ‖ S2 ‖ S3; review fixes `86cbb616`, `468f3a31`; the two +re-apply bugs `edf0e204` (it held the event loop for minutes) and `8db8a3be` (the +assignment-only pass destructured a bare key). + +**Rollout, 2026-09-21 → 09-22.** Seeded through `pnpm ops tags --file` (never by hand — the +file carries provenance). First derivation: examined 77,224, re-derived 831 in 623 s; 103 +transcript pages rewritten, 906 unchanged. **All three seed patterns were then wrong against +the real corpus and were corrected** — the full before/after is in `curated-tags.md`'s rollout +log, and the shape of the error is worth carrying: a bare `elfpire` matched a friends-list +boilerplate on MommaOcco (200 false hits), `@PapaElfpire` in chat, and "Jason Angelfire" / +"channelfireball" / "RekietaLawLive" in ASR. **ASR never spells "Elfpire"**, so the caption +rule is a misspelling alternation with `\b` on both ends. + +**Anilyzer is live with Eva tags** (`4ac8ceda`), reviewed, with 21 suppressions. Two bugs the +live data found were fixed there. Every other site stays at corpus spec 3 until the operator +chooses to rebuild it — a pre-spec-4 site serves no `/tags.json`, which the MCP reports as +empty-with-a-warning rather than as a silent miss. + +**And a rule can exclude channels** (`1a011d96`): `channelsExclude` is applied AFTER +`channels` and **the exclusion wins**. It is part of `hashCuratedRules`, which is the trap — +see the owed steps below. + +### `storage/debts-1` — merged 2026-09-21 (`7589c50c` → `e109b6ac`, fast-forward) + +The three things S5/S6 named and left out; two of them are now done and the plan file says so. +`assertRelocationRootPresent` (`1fccabac`) — a move can no longer `mkdir` its destination +under an absent mount, checked from the preview AND immediately before each copy phase, on the +channel mover and the saved-video mover both. A `/review` section for auto-paused channels +(`bbc1899e`) — `common/views/review.ts` `autoPausedRows`, and a channel a PERSON paused is not +a row. **Only the per-unit reachability re-check in the two lane runners is still open**, still +deliberately deferred to the five-minute storage watch. +Around them: `clips/` counted in `totalMediaBytes` (`7589c50c`) so the platter's bytes stop +lying, clip-window eviction BY AGE (`4416bb58`, `e4f9fa1f`), the relocate space check freed +from how much RAM is free (`1271e21f`), a fan-out that says which jobs it started +(`94b24fdf`), the full-source path (`fab61a49`), and the umtool suite SKIPPING rather than +going red without the song data (`1ab07623` → `007c12e5`). +Anchors: [`FACTS.md`](FACTS.md#storage-debts-verified-2026-09-21-branch-storagedebts-1). + +### `feat/filtered-channel-followups` — merged 2026-09-21 (`337477d5`) + +Nine commits, `f142f7d9` → `c442cb60`, behind the per-channel download filter. A rejection +takes its own prefetch directory with it; the metadata scan is DISPATCHED by the download +lane, not waited for, and goes in the pick log like any other unit; a filtered-out livestream +gets a third answer rather than settling forever; `videoHasAnyArtifact` turned out to be +`isVideoDownloaded` under a second name. Review (`5c02e7da`) found the discard was a +deny-list and four things it let through. +Anchors: [`FACTS.md`](FACTS.md#filtered-channel-follow-ups-verified-2026-09-21-branch-featfiltered-channel-followups). + +### The release itself — what each branch landed + +**A · `tags/rules-and-ops` → `9837f066`.** `1a011d96` (merged alone, first) `channelsExclude`. +`dcd065e7` a site layer carries NO rules — dropped on read AND on write, so a hand-written +site rule is removed from the file on the next save. `d653b504` `--wait` survives a failed +poll: the log route is POLLED (`{content, nextOffset, status, …}`), a poll failure is not a +job failure, there is an `/api/jobs/active` fallback, probe failures are bounded, a +`--wait-timeout`, and `followJob` is importable with injected `fetch`/`sleep`. `52f5c64c` +both build routes take `siteId` OR `siteIds` via `reqSiteIds`, and `build-deploy` fans out +returning `{ok, jobs, skipped, jobId?}`. `6214eeb9` `wt rm` finds the directory `wt add` made +(`worktreeDirFor`, one function for both verbs). `729f64ef` a measure-nav comment. `bad9ea43` +the four-lane status poll reads the corpus ONCE. `d0df901b` the `typeof process` guard in +`contract.ts`. `94750a1b` review fixes — a bad site id is 400 *before* any job, and a +recovered poll answering `queued`/`running` is not an outcome. + +**B · `export/tags-followups` → `cb254919`.** `e942dc7f` tag chip groups fold (`<details>` +per group, local state, `"n selected"` on the summary). `ce5379f2` `SearchResults`' leaf +sections read `SCOPE_LABELS` — one scope-label table, no second copy anywhere. +`f40c4257` + `7a38b60b` hub `/ask` prerenders, and the fix is that `AskHub` needs the WHOLE +provider stack (`PlayerProvider` → `MultiSiteDataProvider` → `SearchSessionProvider`), never +an opt-out; `2e13c0ab` its spec, `export/e2e-hub/ask.spec.ts`. `befd2fbf` MCP: a tag filter +takes posts OUT of the search, reported in `postsScanned.skippedForTagFilter` and in a footer +sentence, because posts carry no curated tags and the export UI does the same. + +**C · `editor/debts` → `766e0873`.** `94c965d7` + `674c976b` the channel Storage panel evicts +that channel's clip windows (`ClipWindowsCard` with a `slug`, disabled by the panel's +`blockedReason`, by-age caveat on screen). `d23883f7` rename and delete refuse with +`channelMediaBusyReason`. `1b3d4837` Sync all skips a channel whose media drive is not +mounted. `1cace934` worker auth's 503-when-unset asserted. `1e0b10a9` `.gitignore` +`/editor/content`. **S0-pause landed** — `98fa001a` the four legacy pause fields and their +migration deleted (`migrateHeldToLanes` gone), `27b6be0f` `held` defaults per lane via +`defaultHeldFor` (backfill `true`, the others `false`), `ad20ffd8` the editor readers gone, +`7d2e889a` `settingsWrite.test.ts`. Review fixes `4d2fa0ca` + `70c2443a` (**every** +`/api/test/*` route 404s unless `EDITOR_TEST_ROUTES=1`), `8ad4ea77` (the `held` default +asserted through `sanitizeAutoQueue`), `67f17b30`, `307c9b81`. + +### Gates measured for this release + +`tsc --noEmit` clean in `common`, `editor`, `export`, `mcp`. Unit: common **1604** (was +1592), mcp **219** (was 216), `pnpm run test:scripts` **151 passed + 1 deliberate LIVE skip** +(was 138). Per-branch e2e: A `tags.spec.ts ops-api.spec.ts` **24/24**; B export **188/188**, +hub **6/6**, `e2e:2origin` **3/3**; C 20 specs **184/184**. + +**`e2e:2origin` is GREEN for the first time since hub `/ask` existed.** The FACTS entry +calling it known-red on the base is closed — a red run there is a regression now, not a +known failure to skip past. + +### Housekeeping + +Worktrees `storage-locations-s0..s4` and `s0-pause` removed. Branches deleted: +`storage/locations-s0..s4`, `storage/debts-1`, `feat/filtered-channel-followups`, +`one-core/phase-1`, the seven `umtool/*`, `one-core/s0-pause`. **Kept:** +`parked/download-filter-preview`, `p1-e2e`, `worktree-duplicates-page`, `diet-series`. The +three release worktrees and branches (`tags/rules-and-ops`, `export/tags-followups`, +`editor/debts`) go once the final gates are in. Nothing unmerged was deleted. + +### Owed — the post-release operator steps, in order + +1. **Re-send each Eva tag definition** via `pnpm ops tags --file`, with + `rules[].channels: []` and `channelsExclude: ["elfpire-eva", "elfpire-eva-x"]`. +2. **`pnpm ops build-index --wait`.** Expect ONE full re-derive of the whole corpus, ~80 s, + and the log line `curated tags: rules <hash8> (changed)`. This is not the exclusion doing + work — `channelsExclude` is part of `hashCuratedRules`, so merely shipping the field + changes the hash for every existing rule set and the first build after the deploy walks + every record. It happens once. Do not go looking for a bug. +3. **`pnpm ops build-deploy --json '{"siteId":"anilyzer"}' --wait`**, then verify: + `curl https://anilyzer.pages.dev/tags.json | jq '.tags[].count'` and + `list_tags {source:"remote:https://anilyzer.pages.dev"}` against the allow-list build + (**96 / 282 / 56**). Suppress any new false positive in `/tags` → Preview. +4. **Rebuild and restart the editor and umtool on this `main`.** Other sites stay at corpus + spec 3 until the operator chooses. + +**Out of scope, decided:** hub tag federation. A tag filter works per site; the hub does not +aggregate `/tags.json` across members. + +**Previously:** 2026-09-20 — **storage locations S5 + S6 shipped** on branch `storage/locations-s5-s6` (worktree `/home/user/Projects/storage-locations-s5-s6`, off `main` -@ `60183f0`, unmerged). The operator's two asks that evening — *"disk space and current storage +@ `60183f0`; **merged since — the "unmerged" below is stale, and the three things it names +as left out are down to one, see the 2026-09-22 block above**). The operator's two asks that evening — *"disk space and current storage volume as columns on the channels menu, and let me filter by volume"* and *"progress on relocate jobs since rsync gives progress"* — plus the four things around them: per-channel media bytes in the snapshot (`totalMediaBytes`), the corpus volume as a first-class `/storage` @@ -106,20 +245,17 @@ scopes migrating into lane trees, both `enabled: false`. `backfill.allowRedownload` and the channel scope. As configured today it would re-fetch audio for ~66,540 videos. The migration left the lane disabled; the tree's channel leaves are the scope from now on. -- **The four legacy pause fields go only once the live `settings.json` carries all four - `held` keys** — which happens on its first write through the editor. And when - `sanitizePolicy` finally defaults `held`, **backfill's must default `true`**: - `sanitizeBackfill` defaults `enabled: false` and `legacyGateHeld` reads that field - INVERTED, so a `held: false` default would silently un-hold the very lane Gate B fences. - -**BLOCKER FOR ANY EDITOR e2e IN THE PRIMARY CHECKOUT, and it is not ours.** -`editor/content` is an untracked **symlink to `/home/user/Projects/recipe-content`** (made -2026-08-31). Tailwind's source detection follows it and Turbopack **panics** compiling -`editor/app/globals.css`, so `pnpm e2e` dies before the first spec. Reproduced on `main` at -`5ffb0e1`, so it is environmental. Every Phase 1 slice ran its suite from a `git worktree` -instead — which needs one extra step, a composed fixture site copied into its `export/public` -so the export webServer answers 200 instead of 500. **`git add -A` stages that symlink**, and -a commit made that way carries the panic into every worktree of it: add by path. +- ~~**The four legacy pause fields go only once the live `settings.json` carries all four + `held` keys**~~ — **DONE 2026-09-22 (S0-pause, in `editor/debts`).** The four fields + (`transcriptionsPaused`, `downloadsPaused`, `digest.digestsPaused`, and the inverted + `backfill.enabled`) are deleted from `SiteSettings`, from every sanitizer and from + `writeSettings`' merge literal, so a file that still spells one loses it on the next write; + `legacyGateHeld` and `migrateHeldToLanes` are gone. The warning this bullet carried was + heeded: `defaultHeldFor(lane)` is `lane === "backfill"`, so **backfill defaults `true`**, + and the invariant is asserted where a reader actually hits it — through `sanitizeAutoQueue` + in `common/jobs/autoQueuePolicy.test.ts`, not through a unit test of the default itself. + The backfill lane is now off twice over on a fresh install, unarmed AND held, which is gate + B kept as two deliberate acts. **All four e2e flakes are fixed** (`plans/deflake-e2e.md`, `a32612a` → `fae99f1`), and the first three's class is worth remembering: **a debounced write survives a test boundary @@ -1300,6 +1436,35 @@ parallel task), Phase 0 transcription benchmark (a separate bottleneck). ## Decisions log +**S0-pause ships in this release rather than waiting for the live file (2026-09-22).** The +standing condition was "the four legacy pause fields go only once the live `settings.json` +carries all four `held` keys". Waiting bought nothing a default could not: `sanitizeAutoQueue` +supplies `held` from `defaultHeldFor(lane)`, which reproduces exactly the reading the retired +fields gave a file that named no gate — false for the three paused-flags, **true for backfill**, +because `backfill.enabled` was inverted. A settings.json written before this release is +therefore read correctly on the first load, and loses the dead keys on its first write. The +alternative — a fifth release carrying a migration for a field nobody may ever have written — +was the more expensive of the two. + +**`e2e:2origin` was FIXED, not skipped (2026-09-22).** It had been known-red since the export +responsive redesign, and the cheap option each time was to keep the note and move on. The +cause was `AskHub` prerendering outside its providers, and the fix is the WHOLE provider stack +(`PlayerProvider` → `MultiSiteDataProvider` → `SearchSessionProvider`), never +`force-dynamic` or `ssr: false`: opting the page out of prerendering would have hidden a real +missing-provider bug behind a build flag and cost the hub its static `/ask`. The suite is 3/3 +and the FACTS entry calling it known-red is closed — **a red run there is a regression now.** + +**Hub tag federation is out of scope (2026-09-22).** A tag filter works per site. The hub +does not aggregate members' `/tags.json`, and a chip row is built from the site's own +published file. Revisit only if someone actually wants to filter across members; the shape of +the answer (whose counts, whose suppressions) is not obvious and nothing needs it today. + +**Only merged branches were deleted in the housekeeping pass (2026-09-22).** Every branch +removed is an ancestor of `main`. `parked/download-filter-preview`, `p1-e2e`, +`worktree-duplicates-page` and `diet-series` are kept precisely because they are not, and the +three release branches wait for the final gates. A deleted unmerged branch is the one cleanup +mistake that is not recoverable from the repo alone. + **Diarization is 2–3× SLOWER than the transcription it follows, so it does not run inline (2026-08-07).** Measured, not assumed. GPU transcription came free from the corpus's own `transcribe-outcome.json` records — **3,602 videos, 10,790 audio-hours, @@ -1656,21 +1821,35 @@ playwright `webServer` with `OLLAMA_URL` pointed at it in **both** `dev:test` an ## Commands known to pass -Established against this branch: +Numbers measured on `main` @ `766e0873` (2026-09-22) unless noted: ```bash -pnpm --filter yt-dlp-transcript-common run test # 356 tests (was 344) +pnpm --filter yt-dlp-transcript-common run test # 1604 tests (was 1592) +pnpm --filter yt-dlp-transcript-mcp run test # 219 tests (was 216) +pnpm run test:scripts # 151 passed + 1 deliberate LIVE skip (was 138) pnpm --filter editor build -pnpm e2e # default dev mode; E2E_MODE=start serves a stale build -pnpm --filter export exec playwright test # 150 tests, separate suite on :3020 +pnpm e2e # THE EDITOR SUITE, whatever spec you append — ~24 min +pnpm --filter export run e2e # 188 tests, separate suite on :3020 +pnpm --filter export run e2e:hub # 6/6, :3041 +pnpm --filter export run e2e:2origin # 3/3 — GREEN since 2026-09-22, no longer known-red ``` -Run `tsc` per package — the brace form is mangled by fish: +Use the `run e2e*` scripts, not `exec playwright test`: each one wraps `queue-lock.mjs`, which +takes the machine-global lock and declares its ports. Bypassing it is how two suites end up +driving each other's servers. + +**`pnpm e2e` runs in the primary checkout again** (2026-09-22): the `editor/content` symlink +that panicked Turbopack is gone and `/editor/content` is in `.gitignore`, so `git add -A` +cannot re-stage it. A fresh worktree still needs a composed fixture site copied into its +`export/public` or the export webServer 500s — that was always a separate problem. + +Run `tsc` per package — the brace form is mangled by fish. All four are clean: ```bash cd common && pnpm exec tsc --noEmit cd editor && pnpm exec tsc --noEmit cd export && pnpm exec tsc --noEmit +cd mcp && pnpm exec tsc --noEmit ``` A `SCHEMA_VERSION` bump means a FULL index rebuild: 77k videos, **~35 min** and it re-reads