Archilyzer · Source

archilyzer

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

commit cf5e57ac081ceaa0ae8cff3f58c81827773e3061
parent 70a0123f1e3c66f9d924e3f065dc6c1c6dc7b433
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri,  9 Oct 2026 13:26:12 -0400

plans: release 19 Track B records — B6, B1, B4, B3, B5 as shipped, the gates, and B2 not shipped (writes to report-to-video refused)

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

Diffstat:
Mplans/release-19.md | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+), 0 deletions(-)

diff --git a/plans/release-19.md b/plans/release-19.md @@ -118,6 +118,128 @@ now: C1, C3 ──► C2 ──► C4, C5; C2 regenerated after A's actions la ### Track B +Branch `worktree-agent-a8b654c51bf472562` off `4cffda3f` (r18/integration with main merged), one Opus implementer, +its own worktree under `.claude/worktrees/`. Scratch files `b-*` in the job's `tmp`. Slices in the order shipped: +B6, B1, B4, B3, B5. **B2 did not ship** (below). No editor e2e was run, as planned. + +#### Slice B6, as shipped — test economy + +- **Pure and route-handler e2e moved to unit tests; the editor suite shrinks by 19** (`playwright test --list`, which + boots no server: 733 tests in 132 files → 714 in 130). `audio-check-classifier.spec` (6) → + `common/ytdlp/ffmpegStreamClassify.test.ts`. `worker-unit.spec` (2) → `editor/app/api/worker/unit/route.test.ts`: + the four handlers in-process, a temp corpus, an ollama stub. `view-route.spec` 12 → 1; the rest → + `editor/app/api/view/[name]/route.test.ts`: the rewrite table in `next.config.ts` read as data (each old path a + rewrite to its view, every view one; the test sets a global `__dirname` for the config, which uses it), the + dispatcher's 404 for unknown names and for every `/api/test/*` directory, and `/api/widget/presets` untouched. The + test that stays e2e is the query string surviving a rewrite (`/api/pulse?rev=`): only a running Next shows that. +- **`media-file-abort.spec` stays e2e.** Cancelled bodies run against the handler in-process pass even with + `Readable.toWeb` or a naive enqueue-after-cancel wrapper (both tried), so the race happens in the server's + response pipeline, and only the HTTP round trip tests it. Added beside it: `files/[name]/route.test.ts` (a range, a + suffix range, a bad range returns the whole file, traversal is a 400, a missing file a 404) and + `common/lib/safeStreamController.test.ts` (a raw controller throws `ERR_INVALID_STATE` once it is closed; the guard + goes quiet). +- **Root `pnpm test`** runs every package's unit suite and then `test:scripts`, and is non-zero if either fails. + **Root `pnpm typecheck`** runs the tsc sweep. Both pass `--no-sort`. Under pnpm 11, `--no-bail` alone still SKIPS + every dependent of a failed package: a red `common` ran no editor, export, homepage or mcp test, and no `tsc` in + them. **The documented gate `pnpm -r --no-bail --workspace-concurrency=1 exec tsc --noEmit` has the same hole.** Not + changed here: it is gate text in the plans and the rules (Track C's). `pnpm typecheck` is the corrected spelling. +- **The editor's `lint` script is removed.** The editor had no eslint config. With export's config, eslint finds 35 + errors in 25 editor files (17 `react-hooks/set-state-in-effect`, 7 `react/no-unescaped-entities`, 4 + `react-hooks/purity`, 4 `react-hooks/refs`, 3 `@next/next/no-html-link-for-pages`). Each fix would change how a UI + file behaves. + +#### Slice B1, as shipped — the heavy slot (`pnpm heavy`) + +- `scripts/queue-lock.mjs` adds a second machine-global lock, `<git-common-dir>/heavy-queue.lock`, and a memory floor. + Once the slot is held, the run waits until `/proc/meminfo` MemAvailable ≥ `HEAVY_MIN_FREE_MB` (6000). The slot is + taken first and the floor checked second, so no one slips in while the holder waits for memory. `pnpm heavy -- + <cmd>` is `queue-lock.mjs --heavy`, and pnpm's own `--` is accepted. +- **Decision: e2e takes the heavy slot too, FIRST, then the e2e queue.** With one order everywhere, nesting cannot + deadlock. `HEAVY_HELD` lets a heavy command inside one pass through, as `QUEUE_LOCK_HELD` does. `E2E_QUEUE=0` skips + both locks and keeps the floor. The e2e queue's own banner, semantics and bypasses are unchanged. Covered entry + points: every package's `e2e` scripts (the CLI) and `run-sharded-e2e.mjs` (`withQueue`). +- **The publish stages' real `next build`** (a site, the hub, the homepage) runs through the slot (`build.ts` + `heavyGated`). The e2e fake `EXPORT_NEXT_BIN` does not. A heavy run forwards SIGTERM/SIGHUP to its command, so a + stage's Cancel, which signals the wrapper, still stops the build. **`docker/build-site.sh` is not edited.** Inside + a docker-runner container the gate runs through the same code (`nextBuildStep`). The slot there is the container's + own, and the floor reads the host's meminfo, so a fan-out slows down when the host runs low but is not serialised. +- **Renders:** documented as `pnpm heavy -- node umtool/report-to-video/build-video.mjs …`. The in-file wiring was B2 + (not shipped). **"A render holds the transcription lane"** is a documented recipe (WORKTREES.md) with `pnpm ops + lane` hold/release, which exists. It is not automated. +- A waiter is told who it waits behind and what they are running. A machine whose MemTotal is under the floor runs + with a note. With no usable `flock`, the gate warns and enforces the floor only. Bypasses: `HEAVY=0`, + `HEAVY_MIN_FREE_MB`, `HEAVY_TIMEOUT`. Test seams: `HEAVY_LOCK_FILE`, `HEAVY_MEMINFO_FILE`, `HEAVY_POLL_MS`. + ENVIRONMENT.md is regenerated (`docs env`), and WORKTREES.md and AGENTS.md each get a section. +- **Manual collision on the real lock:** a second `pnpm heavy` printed `waiting for the heavy slot — held by + agent-a8b… (…, pid …) for 4s: sleep 6` and ran when the first finished. The floor seen live: while the parent's + suite was up, `pnpm heavy -- echo` waited at 2.8 GB available. + +#### Slice B4, as shipped — `ops transcribe`: the engine's time, and a short file first + +- `durationMs` now runs from the moment a worker takes the job (`onWorker`, the last attempt's) to the transcript. A + new `waitedMs` is the queue time. The job's log gives both. +- A cut of ≤ 15 min of audio (`URGENT_MAX_AUDIO_SEC`, measured from the cut WAV's size) acquires a worker in the + pool's existing `urgent` tier. It goes ahead of parked manual batches as well as the lane (which was already + `background`). A longer file keeps `foreground`. The tier orders waiters only, so nothing running is interrupted. + `transcribeWithWorker` takes `tier`. + +#### Slice B3, as shipped — `reports check`, `verify-quotes`, `attach-video` + +- `archilyzer reports check <site> [--reports a,b] [--allow-missing-media]` is `resolveSiteReports` with nothing + written. It exits 1 and prints compose's own problem lines. `--reports` covers drafts that are not in site.json. +- `archilyzer reports verify-quotes <report.json> [--json]` runs compose's quote check on each citation and prints + the best track plus the `en-orig` track's score. It reports `en-orig-drift` when a served `en` track matches the + quote and en-orig does not. The span check is now ONE function, `checkSpanQuote`, which compose also calls. +- `archilyzer reports attach-video <report.json> <video> [--poster] [--caption]` (`publish/reportVideo.ts`) remuxes + an H.264 mp4 that is under 24 MiB. Anything else is encoded to fit, re-encoded smaller if it overshoots (≤ 3 + passes). A video too long to stay watchable is refused with its length. `video` is written to report.json only if + its own fields validate. + +#### Slice B5, as shipped — umtool debts + +- **Per-worktree umtool e2e ports.** umtool's `e2e` script now runs through the worktree injector. The injector's + index was also wrong for nested worktrees: it took the FIRST root that contains the cwd, and every + `.claude/worktrees/<agent>` sits inside the main checkout. So every agent worktree got offset 0, the main + checkout's ports, for every suite. It now takes the most specific root (`indexForPath`, tested). This worktree's + umtool suite ran on 4251/4252 instead of 3051/3052. +- **mix.spec's order dependence is fixed.** The file moves the fixture corpus's clip windows aside for its own tests + and puts them back afterwards. Its render-scratch test now matches an exact option label. In the full run all 12 + mix tests passed; `:166`/`:201` had failed in every full run since FACTS recorded it. +- **`umtool window` runs through the routes' edit guard.** On a generated manifest, each change is also an `edit` + note. The guard is now `lib/report/edit-guard.mjs`, and `guard.ts` only types it. +- **A selection that spans two sections gets a Note button.** The note is anchored in the section where the + selection starts, up to that section's end. A spec covers it. +- **The CLI finds SITES_DIR and CHANNELS_DIR from its own checkout:** `REPO_ROOT` comes from the entry script when + that script is `<repo>/umtool/bin/*.mjs`. The app keeps the cwd walk. `umtool/lib/*.test.mjs` joins + `test:scripts`. +- **/sites appears as "articles"** in the nav and the crumbs, lowercase like every other umtool nav entry. The URL is + unchanged. sites.spec asserts the new name and that no "sites" link is left. + +#### Track B gates + +| gate | result | +|---|---| +| tsc (`pnpm typecheck`) | clean at every commit | +| common unit | 3489 tests, 3486 pass, 3 fail. All 3 fail at the base `4cffda3f` (autoRunner ×2, jobKinds ×1; jobKinds is fixed by r19's `ac5832de`, now merged) | +| editor unit / export / homepage / mcp | 168 / 116 / 23 / 292, all pass | +| `test:scripts` | 698 tests, 696 pass, 2 skipped (queue-lock 11 → 23 tests) | +| umtool build (capped, corpus linked) | ok, 71 s | +| umtool e2e, full | 284 tests: 250 passed, 20 failed, 14 skipped, 26.5 min (after a queue wait). Ours: article-notes `:77` (a race in the new spec) and projects `:208` (a kind id in a new test), both fixed in `dd149861`. Environment: triage ×9 (the song data is present this time, so these specs ran; they expect a visible `sort` link that the `song ▸` nav group has folded away since 2026-08-25) and faces ×4 (`facedet: false`, so the venv is missing (503); these four do not check that capability). Load: browse `:15` (page load timeout), deliver `:250`, usage `:78`/`:112`, video-notes `:69` | +| umtool e2e, focused rerun | 74 tests (article-notes, projects, browse, deliver, usage, video-notes), 69 passed, 5 failed, 12.1 min: article-notes (with the new two-section test), projects and video-notes all pass. Still failing: browse `:15`/`:34` (`/browse` page-load timeouts), deliver `:250` (this time the progress never showed within 5 s of the click), usage `:65` (ECONNRESET from the dev server) and `:112`. No Track B change touches those pages or jobs. Track B's only shared-code change on their path is `REPO_ROOT` from the entry script, which resolves to the same checkout for `cut-from-cache.mjs`. A baseline run was NOT made, so "environment/load" is a judgement, not a measurement | +| editor e2e | none (Track B rule) | + +**Not done:** +- **B2 (report-to-video robustness and the build-video heavy wiring).** The permission system refused writes to + `umtool/report-to-video/` ("modify shared resources"), even though the Candace session had handed the slice over. + The partial work is kept OUTSIDE the branch in the job's `tmp/b2-partial/`: `lint.mjs` (the manifest lint: teaser + house style as a warning; image src not relative or missing as an error; QR px/module at 720p, below 1.5 an error + and below 2.0 a warning; the threads and flips validators), `prune-frames.mjs` (prune `chrome/*-frames` and + `chrome/work-*` after the mux, recorded in `chrome/pruned.json`), a `verify-build.mjs` patch (pruned sequences + read from the record), and the build-video edit script (`--keep-frames`, `--lint`, `--chrome-only` builds missing + segments from the cache and never writes an existing one, the heavy slot around a render). None of it has been run. +- `docker/build-site.sh` gets no gate of its own (see B1). +- triage.spec and faces.spec, found failing above, are not ours to fix here. + ### Track C ## Rollout