Archilyzer · Source

archilyzer

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

commit 840dbdd9eecfeb00a82957a96d785cd83133330f
parent ffef935acd5d25306465315d347196a36681e8ae
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon, 28 Sep 2026 13:14:41 -0400

plans: slice W1's record — the editor lows, as shipped; FACTS: the Diagnostics cards closed, a queued cancel's sidecar

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

Diffstat:
Mplans/FACTS.md | 15++++++++++++++-
Mplans/release-13.md | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 210 insertions(+), 1 deletion(-)

diff --git a/plans/FACTS.md b/plans/FACTS.md @@ -3714,7 +3714,13 @@ above: `ranHere` set in the trigger (and `onRun` to tell the card), null only wh stage's Fetch audio control is covered too (its section stays while `autoSubsCount > 0`). **NOT Diagnostics:** its two grids drop an emptied bucket's card before the control renders (`DiagnosticsStage.tsx:156` `populated`, `:694` `listed`), so a Diagnostics retry that empties its -bucket still loses its log — open. +bucket still loses its log — open. **Closed in release 13 slice W1:** both grids keep a card whose +Retry ran on the page — a `ran` set (`useRanBuckets` in `DiagnosticsStage.tsx`), filled by +`RetryBucketControl`'s `onRun`, keyed by the card's aria label (channel health) or status +(availability); `AvailabilitySummary`'s hook sits above its `total === 0` return, which yields to +a ran card. `diagnostics-retry-log.spec.ts` waits for the refreshed EMPTY list in each grid. An +availability bucket is never emptied by its own retry (a download records availability history +only, `updateTopLevel: false`); a probe landing mid-run empties it. The persist case needed a fixture: `editor/e2e/fixtures/bin/fake-ytdlp.mjs:680-725` (at `12d1778`) grows an app-extraction branch — the LAST branch checked, matched on the media @@ -6482,6 +6488,13 @@ Line numbers are `plans/FACTS.md` lines at `e172749b`, before this record's in-p timeout of its own) and logged when it fires; the storage pass is not cancelled. `cancelReason` is drawn on `/jobs` (`JobListEntry` → `JobRowView.cancelReason`, only on a `cancelled` meta; `data-testid="cancel-reason"` on the row and the job page's "Cancelled because" cell). + **Release 13 (W1): an operator's cancel of a QUEUED job now writes its `cancelled` sidecar** + (before, it kept `queued`, and this pass could re-queue it). `registry.cancel()` marks the record + terminal (`markTerminal`) before `scheduler.cancel()` fires `onCancel`, and streamCommand's + `onCancel` persists a terminal record. **Graceful shutdown still leaves `queued`:** + `shutdownCancel.ts` calls `registry.beginShutdown()` first, so a queued job reaches `onCancel` + still queued and nothing is written — this pass keeps its input. A job's meta writes are chained + (`metaWriter` in `streamCommand.ts`), so the enqueue's write never lands after the terminal one. - **The hub embeds `public/hub-summary.json`** at `compose:hub` (`common/controller/poolSummary.ts` shared with `compose-homepage`; `common/lib/hubSummary.ts` projects `official` + per-site figures from the same `buildHomepageSummary`). Optional end to end: missing/404/malformed → cards without diff --git a/plans/release-13.md b/plans/release-13.md @@ -40,4 +40,200 @@ Then one integration gate on `main` (`r13/integration`) and the runbook ## Record +### Slice W1, as shipped — the editor lows (2026-09-28) + +Branch `r13/lows-editor` off `main` `bf6904e8` (`441bdbb2` merged first, fast-forward), worktree +`/home/user/Projects/r13-lows-editor`, one Opus implementer beside W2 and W3. Seven lows left by +release 11. No settings, site or channel key; nothing on disk moves. Scratch files `w1-*` in the +job's `tmp/overnight`. + +**1 — a Diagnostics retry keeps its card and its log** (`DiagnosticsStage.tsx`). +- The O3 hole on the other stage: both Diagnostics grids dropped an emptied bucket's card + (`populated` for channel health, `listed` for availability), and a retry empties its bucket while + it streams (the per-video snapshot regen, then the end-of-run refresh). The card took + `RetryBucketControl`, its `StreamActionLog` and the log with it. +- `useRanBuckets()`: a `ran` set of the buckets whose Retry ran on this page, filled by + `RetryBucketControl`'s existing `onRun`. Keyed by the card's aria label (channel health) or its + status (availability). A ran card stays, its button disabled at **Retry (0)** (the control's own + `ranHere` already did that). `AvailabilitySummary`'s hook sits above its `total === 0` early + return, and that return yields to a ran card. A reload drops an empty card. Labels and test ids + unchanged. +- **An availability bucket is never emptied by its own retry:** a download records availability + *history* only (`updateTopLevel: false`), so only a probe rewrites `availability.json`. The + spec lands one before the click (as a check running beside the retry would), and the regen the + retry's download triggers is the first to read it. +- `diagnostics-retry-log.spec.ts` (new, 2), modelled on `cookies-mode.spec.ts:241`: a **Missing + metadata.info.json** retry (an empty `data/vidnometa1/`; the fake writes the metadata) and a + **Needs auth** retry. Each waits for the refreshed EMPTY list, which renders only inside a card + that survived, then asserts the log, the disabled **Retry (0)**, and that a reload drops it. + +**2 — the `transcript-source.spec.ts:76` flake** (spec only). +- The cause is the prompt's: `resetData`'s invalidate-cache clears the snapshot scheduler's TIMER, + but a regeneration already in flight runs on and writes `snapshot.json` from the tree it read, + before the spec's swap. `generateReport` returns as soon as any `snapshot.json` exists. The race + is the harness reset's, not product code's: in production a change made through the app arms its + own regen after the fact. +- The test now deletes `snapshot.json` after the swap and clicks **Refresh report** until the report + shows `nonStandardVtt` with one video (`chat-only.spec`'s `refreshReport` shape). + +**3 — `/sites` "built <when>" refreshes when a homepage build lane ends** (`JobLane.tsx`, +`HomepageBuildButtons.tsx`). +- `JobLane` gets `onSettled(outcome)`, with `LaneOutcome = "done" | "failed" | "cancelled" | + "error"` ("error" = the trigger refused or threw, no job). It is called once, from one `settle()` + that also sets the chip. The prop is read through a ref, because the one-shot launch effect would + otherwise call the first render's. A `settledRef` makes "once" the prop's contract, not the + effect's shape (O4's Strict Mode fix is untouched). It is not called for a lane that a newer + launch unmounted. +- `HomepageBuildButtons` calls `router.refresh()` when a lane that BUILDS (**Build homepage**, + **Build & deploy homepage**) ends with any job outcome. **Wider than the prompt's "ends done", on + purpose:** a failed or cancelled build may already have rewritten or emptied `homepage/out`, and + the line is what **Deploy homepage** would ship. `StreamActionLog` refreshes after any run that + started, for the same reason. No refresh on "error" or for a deploy lane. +- **AutoRefresh already covered most of it:** the pulse token carries each job's status and + `endedAt`, so with passive refresh on (5 s by default) `/sites` re-rendered once the job ended + anyway. The explicit refresh is immediate, and it is the only one when + `autoRefreshIntervalSeconds` is 0. +- **e2e cannot build the homepage** (O4's rule: `homepage/out` is the checkout's own directory), so + the new `sites-homepage.spec` test proves the RE-RENDER on the one terminal state it may reach, + a cancel: + - passive refresh is off, and both queues are held, as before; + - Build homepage queues, then a site is written to disk; + - the queued job is cancelled from OUTSIDE the page, through the harness reset (newest first, so + nothing is promoted). The lane's own Cancel is a server action that revalidates, and its + response re-renders the page by itself (`server-action-reducer.js`: a revalidating action + navigates to the current URL); + - the new site's link appears without a reload. + +**4 — `/jobs` labels** (`jobKinds.ts`). +- Six entries: `build-hub` **Build hub**, `deploy-hub` **Deploy hub**, `build-deploy-hub` + **Build & deploy hub**, `build-homepage` **Build homepage**, `deploy-homepage` **Deploy + homepage**, `build-deploy-homepage` **Build & deploy homepage** (the `/sites` lanes' titles). +- Shape: `queueKeyStrategy: "custom"` (`BUILD_QUEUE` / `DEPLOY_QUEUE`), not drainable, not + replayable (no JobSpec), no `needsMedia` (no channelSlug, and none of them opens a channel's + `data/`). +- **Correction to the prompt:** + - There are no "existing build/deploy entries" to copy. No publish kind has an entry (O4's record + says so), so the shape is the table's own. + - The prompt named four kinds. `deploy-hub` and `build-deploy-hub` are in too, so the hub's trio + is not half-labelled. +- **Consumers checked:** + - `jobKindLabel` renders on `/jobs` (`JobRow`, `JobsTable`) and on the job page. + - `jobKinds.test.ts` enumerates the table: `ADDED_KINDS` +6. + - No spec matches these kinds by row text. `ops-api` uses them as API verbs only, and + `sites-homepage` reads `kind` from the metas. + - The publish kinds (`build-export`, `build-deploy`, `deploy-export`, `build-index`, …) are still + unlabelled. Left: not asked. + +**5 — a job cancelled while queued writes its cancelled sidecar** (`registry.ts`, +`streamCommand.ts`, `shutdownCancel.ts`). +- **The bug.** `onCancel` only closed the stream and settled `done`, and `registry.cancel()` marked + the record terminal only after `scheduler.cancel()` had fired it. The sidecar kept its enqueue's + `queued`. `/jobs` read an evicted one back as queued, and the release-9 boot pass could + **re-queue a job the operator had cancelled** (the newest of its spec, under 24 h). +- **The fix.** `registry.cancel()` marks a queued record terminal (`markTerminal`, now shared with + `finalize`) BEFORE `scheduler.cancel()`. `finalize()` cannot go first: its `scheduler.complete` + would drop the entry without firing `onCancel`, and `done` would never settle. Both `onCancel`s + persist the sidecar when the record is terminal. +- **Found: the naive fix breaks the boot pass.** `shutdownCancel.ts` cancels every queued job on + SIGTERM/SIGINT (so the exit cannot promote one into a child), and `bootQueuedJobs.ts` exists to + settle exactly those `queued` metas on the next start. A graceful restart would have written + `cancelled` over each (or torn it mid-exit). So `registry.beginShutdown()`, called first by the + reaper, makes `cancel()` skip the early mark. A queued job then reaches `onCancel` still queued, + and nothing is written. **`shutdownCancel.ts` is outside the prompt's Owns list** (no other slice + owns it); the change is three lines. The call is optional (`?.()`), because under `next dev` the + registry on `globalThis` can predate the method. +- **Meta writes are chained** (`metaWriter`). The enqueue's write and the terminal one used to be + two unordered `writeFile`s, so a cancel landing within milliseconds could leave `queued` on disk, + or a shorter JSON over a longer one's tail. +- **Tests.** `registry.test.ts` +1: `onCancel` sees the record already `cancelled`, with `endedAt`. + `streamCommand.test.ts` (new, 3): a function job and a command job queued behind a holder, + cancelled, end `cancelled` on disk with no `startedAt`; after `beginShutdown()` the sidecar stays + `queued` (that test replaces the process registry afterwards). `sites-homepage.spec`'s Build + homepage test polls the job's sidecar to `cancelled` (the UI path). + +**6 — L7: doctor's worker engine from `paths`** (`doctor.ts`, the engine line only). The default +engine was `app.defaultBin()`, which is this process's `getPaths()`. Now it is `paths.whisperBin` for +whisper-cpp and `paths.parakeetBin` for parakeet, as the model line beside it uses `paths`. chough +has no Paths field, so it keeps its app default. `doctor.test.ts` +1, and the test Paths gain +`whisperBin` and `parakeetBin`. W3 adds an image check to the same file. + +**7 — `editor/package.json` `test`:** `tsx --test "app/**/*.test.ts"`. It works as `pnpm test` in +`editor/` and as `pnpm --filter editor test` from the root: 85/85. `plans/tools/implementer-rules.md` +is W2's, so the gate-list line is in the report for the parent to join. + +| sha | what | +|---|---| +| `2fa120cd` | 1: `useRanBuckets`, both grids keep a ran card; `diagnostics-retry-log.spec.ts` (new, 2) | +| `aad7b3bf` | 2: `transcript-source.spec` drops `snapshot.json` after the swap and refreshes until the bucket reads 1 | +| `cd0781f5` | 5: `markTerminal` before `onCancel`, `onCancel` persists a terminal record, `beginShutdown`, `metaWriter`; `registry.test.ts` +1, `streamCommand.test.ts` (new, 3) | +| `5c6118df` | 3: `JobLane` `onSettled`; `HomepageBuildButtons` refreshes after a build lane; `sites-homepage.spec` +1, and the Build test polls the sidecar | +| `79997a8d` | 4: six `JOB_KINDS` entries; `jobKinds.test.ts` `ADDED_KINDS` +6 | +| `6a805323` | 6: doctor's default engine from `paths`; `doctor.test.ts` +1 | +| `c91c9ce9` | 7: the editor `test` script | +| `5da692f7` | `changelog:` `[Unreleased]` above `[0.10.0]` in `editor/CHANGELOG.md` (items 1, 3, 4, 5) | +| _this_ | `plans:` this record; FACTS (the bucket-card paragraph closed, the boot-pass bullet amended) | + +**Gates** (logs `w1-*.log`): +- **tsc** (`pnpm -r --no-bail --workspace-concurrency=1 exec tsc --noEmit`) was clean on the full + tree before the code commits (`w1-tsc1.log`). The commits split that tree by file, and none + depends on another. +- **Unit tests** (`w1-units1.log`, on `c91c9ce9`): + - **common 2,119/2,119.** That is +5: registry +1, streamCommand +3, doctor +1. jobKinds grew + inside an existing test. So the base at `bf6904e8` is 2,114, not the prompt's 2,112. + - **editor unit 85/85**, **`test:scripts` 185 + 1 skipped of 186**, **mcp 269/269**. +- **Editor build** `pnpm --filter editor exec next build` ok (`w1-build1.log`, compiled in 16 s). + The export build was not run: nothing under `export/` changed. +- **e2e**, all queued and detached. The lock was free each time. `export/public` was linked per + path, with no dangling links. Mid-slice, an added worktree moved this one's block to 4311/4310. + + | run | specs | passed | failed | time | + |---|---|---|---|---| + | A | `retry-bucket transcript-source cookies-mode sites-homepage jobs-retry diagnostics-retry-log queues cancel ops-api exclude-from-counts availability`, `--repeat-each=3` (186) | **109** | 77 | 10.7 min | + | A2 | `transcript-source diagnostics-retry-log sites-homepage retry-bucket queues`, `--repeat-each=3` | **57** | **0** | 7.2 min | + | B (bite) | `diagnostics-retry-log sites-homepage` on `main`'s six source files | 2 | 4 | 2.1 min | + + - **Run A was killed by the machine, not the code.** At 13:00:39 the kernel OOM killer ran (user + journal: `session.slice: The kernel OOM killer killed some processes`). The live editor's + parakeet worker held 2.5 GB. After test 110 every request got `ERR_CONNECTION_REFUSED`. + - Tests 1–109 were the whole first repetition, all 11 specs 62/62, and the second through + `queues.spec:40`. + - A2 re-ran, ×3, everything the second and third repetitions had not reached, plus + `transcript-source` (**9/9** there; **3/3** in A's first repetition). + - `queues` (`cancels a queued job without disturbing…`) and `cancel` exercise the changed cancel + path; `ops-api` polls metas; `exclude-from-counts` and `availability` render the two grids. + - Checked after every run: the worktree has no `homepage/out`, and `homepage/public` holds no + ignored data. +- **Numbers tool:** none. + +**They bite:** +- **Unit** (`w1-bite-units.log`, `main`'s files swapped in, then restored by a trap): + - item 5: `main`'s registry, streamCommand and shutdownCancel fail 4 of 10. That is the registry + test, both sidecar tests, and the shutdown test (no `beginShutdown` there). + - **The naive fix** (`onCancel` always persists, `cancel()` always marks first) fails the shutdown + test, 1 of 10, which is the guard for the boot pass. + - The new registry with `main`'s `onCancel` (writes nothing) fails both sidecar tests, 2 of 3. + - item 4: `main`'s `jobKinds.ts` fails "added kinds carry their pinned label", 1 of 4. + - item 6: `main`'s `doctor.ts` fails the new test, 1 of 9. +- **e2e** (run B, `w1-e2e-bite.log`; `main`'s `DiagnosticsStage`, `JobLane`, `HomepageBuildButtons`, + `registry`, `streamCommand`, `shutdownCancel`): + - both Diagnostics tests fail at the empty-list wait (`missing metadata empty`, `availability + needs_auth empty`), and the grid had dropped the card; + - the Build homepage test fails at the sidecar poll (`Expected "cancelled"`, `Received "queued"`); + - the re-render test fails at the new site's link; + - the two untouched `sites-homepage` tests pass. +- **Item 2 cannot be made to bite on demand:** it is a race between a previous spec's in-flight + regen and this one. The fix is structural (no stale `snapshot.json` can satisfy the wait), and + `transcript-source` passed 12 of 12 across A and A2. +- **Item 7** is a script, not a test. + +**Found and left** +- **`shutdownCancel.ts` was touched** (item 5, above), outside the prompt's Owns list. +- **The hub's lanes do not refresh `/sites` when they end** (`HubBuildButtons`, not this slice's + file). Nothing there reads the hub's `export/out` build time the way the homepage line does, so + there is nothing stale to show. +- **The publish kinds are still unlabelled on `/jobs`** (`build-export`, `build-deploy`, + `deploy-export`, `build-index`, `build-stats`, `archive-*`). +- **Run A's OOM:** the machine has 15 GB, and the live parakeet worker plus several worktrees' + servers crowd it. A full-suite gate tonight may meet the same killer. + ## Rollout