# Release 6 — follow-ups + Phase 4 slice 1 `main` at `93dcb532` (release 5 merged: slice R, Rumble; slice X, visitor exports off). Release 5 is live. This release clears release 5's "found and left" items and Phase 3's deferred items, then lands Phase 4 slice 1. ## Record ### Follow-ups slice, as shipped — 410 is removed, umtool's Rumble args, one atomic write, a comment, measure-nav (2026-09-25) Branch `one-core/r6-followups` off `main` `93dcb532` (`main` had not moved). Five small items, one commit each. Four came from release 5's "found and left" list and Phase 3's deferred list. The fifth came from a live sidecar. | sha | what | |---|---| | `6d5cdbc3` | `common/lib/availability.ts` `parseUnavailableFromStderr`: `/http error 410/` joins the `deleted` group. There was no `404` pattern to copy, so it sits with "video unavailable" / "no longer available". The evidence is `rekietalaw-rumble` `v7e07us`'s sidecar (read only, not re-checked). It has read `error` since 2026-08-21, with `ERROR: [Rumble] v7e07us: Unable to download webpage: HTTP Error 410: Gone (caused by )`. The classifier now returns `deleted`, so `classifyDownloadFailure` makes a download that hits it `per_video` (skip) where it used to be `unknown` (batch abort). `availability.test.ts` +1 test: that exact line, the per-video consequence, and 403/500 still `error` | | `9a88ee4b` | umtool's yt-dlp spawns carry the platform args. `report-to-video/*.mjs` run under bare `node`, and the runtime image is `node:20` (no type stripping), so they cannot import common's TS. **The shim moves the table rather than copying it.** `PLATFORM_ARGS` / `platformArgs` (+ a new `platformArgsForUrl`) now live in `common/ytdlp/platformArgs.mjs`, and `detectPlatform` in `common/lib/detectPlatform.mjs`. Both are plain JS with JSDoc types. `channelArgs.ts` and `platform.ts` re-export them, so no TS caller changed (the base tsconfig already has `allowJs`). common's `package.json` exports the two files by exact path; the `./lib/*` / `./ytdlp/*` patterns map to `.ts`. `umtool-report-to-video` gains a `yt-dlp-transcript-common` dependency. The lockfile gets that importer entry only: a plain `pnpm install` re-resolved ~280 lines of peer suffixes, so the entry was applied by hand and verified with `pnpm install --frozen-lockfile`. `build-video.mjs` exports `clipFetchArgs` (platform args before a retry's `extra`), and `check-availability.mjs` exports `simulateArgs`. New `platform-args.test.mjs` (3): Rumble gets the table's args in position, YouTube gets none | | `947f07aa` | `common/controller/transcribeOne.ts`: the remote upload path's `writeFile(transcriptPath, bytes)` is now `writeFileAtomic` (`lib/jsonFile-server.ts` — the brief's `writeJsonAtomic.ts` does not exist; slice W's idiom lives there). The shared-fs path is unchanged. No new test: the path needs a remote worker, and the repo does not module-mock. `transcribeOne.test.ts` (3) is green | | `71e2bb41` | `common/controller/channelSnapshot.ts` above `foldBucketLaneEntry`: the comment named the retired `editor/app/components/pipelines/buildBands.ts`. It now names `common/views/pipeline/buildBands.ts`. It also said the band's `blocked` "is this entry's missingInput"; it is `noTranscript` less the untranscribable and downloaded ones (`buildBands.ts:171-175`), so the sentence says that. Comment only | | `b4bd58a3` | `editor/scripts/measure-nav.mjs` (the brief said `plans/tools/`; it has always been under `editor/scripts/`). The six retired paths are `/api/pulse`, `/api/widget/{cleanable,actionable,sync}`, `/api/jobs/active` and `/api/workers`, which only reach a handler through `next.config.ts`'s rewrites. They are replaced by the `/api/view/` paths the pages poll (`pulse`, `cleanable`, `widgetActionable`, `widgetSync`, `activeJobs`, `workers`), and the idle-pulse check uses `/api/view/pulse`. The 13 page routes were already current. Run once against the live :3001 (`--runs 1`, GETs only): no route or endpoint flagged, `/api/view/pulse` idle 8 ms | | *(this commit)* | this record, `[Unreleased]` bullets | **Gates** (worktree root). tsc (`pnpm -r --no-bail --workspace-concurrency=1 exec tsc --noEmit`) clean before every commit. common **1751/1751** (1750 + 1). test:scripts **159 pass + 1 skip** (156 + 1 skip + the 3 new). editor unit **72/72**. mcp **219/219**. `pnpm --filter editor exec next build` ok (50 s). `pnpm --filter export exec next build` ok (34 s). Before the builds, the worktree's `export/public/archives` link was dangling, because the primary has no `archives/` at the moment; it was removed, and no dangling links were left. EDITOR e2e `availability availability-backfill maybe-missing` (from `$T/f-specs.txt`; Playwright's substring match also picked up `pre-clean-availability`): **15 passed, 0 failed, 2.0 min**, with no wait in the queue. Numbers: **none**. No file format changed; `availability.json` keeps its shape, and a 410 video's value changes only when it is next checked. **Found and left.** - **`v7e07us` still reads `error` on disk.** Nothing here re-checks it (the brief forbids live checks). The next availability check or backfill that reaches it records `deleted`, and the `history` gains that entry. - **umtool's `check-availability.mjs` has its own classifier** (`classify`, which already matches a bare `410` as deleted). It differs from common's `parseUnavailableFromStderr`, and its states are different ones (`maybe_missing`, `geo-blocked`, `restricted`). It was left alone because it is umtool's editorial vocabulary, not a copy of the args table. - **`--sleep-requests 1` now applies to Rumble clip fetches too.** It comes with the table and adds about a second per request on a clip fetch; that is harmless, and one table is the point. - **measure-nav's `nav`/`prefetch` columns read ~0.1 kb on every route of the live editor.** That is the same for routes this commit did not touch. The state-tree encoding may have drifted with Next 16.2; this commit only replaced the paths, so it was not investigated. - **Commit trailers** name `Claude Opus 5.5 (1M context)`, as in release 5. ### Phase 4 slice 1, as shipped — `buildDeployCore` to the core as `common/publish/build.ts` (2026-09-25) Branch `one-core/phase-4-s1` off `main` `93dcb532`; `main` moved to `4d97049f` (the follow-ups slice) mid-slice and was merged before the final gates. `editor/app/sites/lib/buildDeployCore.ts` (578 lines) imported nothing from `editor/**`, so it moved **unchanged** to `common/publish/build.ts`, the path `one-core.md` Phase 4 item 1 names. Only its header comment and its three `yt-dlp-transcript-common/*` imports (now relative, `../jobs`, `../lib`, as the rest of common writes them) changed. Every export, log line, exit code and path is the same. No helper had to come down from the editor with it. | sha | what | |---|---| | `9cb35c37` | `git mv` to `common/publish/build.ts`. `sites/lib/buildAction.ts` and `deployAction.ts` import `yt-dlp-transcript-common/publish/build`. **No re-export is left at the old path**: no spec, script or tool imports it (`git grep buildDeployCore` finds only docs and plans). `@aws-sdk/client-s3` and `@aws-sdk/lib-storage` (`^3.1080.0`) move from `editor/package.json` to common's `dependencies`. As in the follow-ups slice, a plain `pnpm install` re-resolved unrelated peer suffixes (`supports-color`), so the lockfile change was applied by hand (the two importer entries move from `editor:` to `common:`, 12 lines) and verified with `pnpm install --frozen-lockfile`. `common/package.json` also gains `"./publish/*": "./publish/*.ts"` in `exports` (the editor resolves common through `exports`, so the new directory needs its own pattern) and `publish` in the `test` glob. `architecture.test.ts` learns the layer: `publish/` may not import `views/` or `components/`, and `lib/` and `components/` may not import `publish/`. No back-edge was found and `ALLOWED` did not grow. New `publish/build.test.ts` (3) pins `resolveOutDir`, `dockerSiteOutDir` and `dockerSiteStagingDir`. `DEPLOY_CLOUDFLARE.md`'s pointer for `ARCHIVE_CACHE_CONTROL` (it still named the pre-IA `editor/app/deploy/` path) names the new home | | `2a31a863` | merge `main` `4d97049f` (release 6 follow-ups). Clean: that slice's `exports` lines sit between `./components/*` and `./lib/*`, and this one sits after `./views/*`. Then `pnpm install --frozen-lockfile` to create the new umtool → common link (`--offline` failed for want of cached metadata for `@next/env`; the online frozen install changed nothing on disk in git) | | `c8af2165` | this record, the `[Unreleased]` bullet, `plans/FACTS.md`'s `PREVIEW_SHARES_ARCHIVES_NOTICE` path | | `f2507e89` | (review fix) `architecture.test.ts`: the `jobs` and `controller` rows forbid `publish/` too, since dispatch sits below publish, and the failure message says so. Nothing imports that way, so it stays green | | *(this commit)* | (review fix) this record: how the editor build loads the SDK, `Dockerfile.build`'s extra install, the commit table | **Gates** on the merged tree (`2a31a863`, worktree root). After the review fix: tsc clean, common **1754/1754**, editor unit **72/72**. No e2e was rerun, since the fix changes no behaviour. tsc (`pnpm -r --no-bail --workspace-concurrency=1 exec tsc --noEmit`) clean, and clean before `9cb35c37` too. common **1754/1754**: 1750 + 1 (follow-ups) + 3 (`publish/build.test.ts`); it was 1753 before the merge. Editor unit **72/72**. test:scripts **159 pass + 1 skip** (the follow-ups count). mcp **219/219**. `pnpm --filter editor exec next build` ok (compiled in 18.6 s). With editor no longer depending on the SDK, `@aws-sdk/lib-storage` is bundled, and `@aws-sdk/client-s3` stays external: it loads through the symlink Next creates at `editor/.next/node_modules/@aws-sdk/client-s3-`, which points into `common/node_modules`. `pnpm --filter export exec next build` ok (7.8 s). The worktree's `export/public/archives` link was dangling (the primary has no `archives/` at the moment), so it was removed before the builds. EDITOR e2e `build deploy-page site-publish-preview sites-crud cut-release channel-build-toggle` (all six exist; `$T/p4-specs.txt`): **29 passed, 0 failed, 1.8 min**, with no wait in the queue. Numbers: `plans/tools/phase3-files-numbers.ts` over one frozen copy of the corpus (`FREEZE_TO`, 71 configs, 1,763 sidecars), `main` `93dcb532` against this branch (both before and after the merge): **diff empty** (3,858 lines each). `pnpm ops build-site` / `build-deploy` / `deploy-site` post to `editor/app/api/ops/*`, which call the same actions. They compile in the editor build and were not run, because a real build against the corpus is forbidden. **Left from Phase 4 item 1, by name.** The brief scoped this slice to the move. What `one-core.md` item 1 also lists is still to do, and most of it needs item 2's CLI first: - the named entry points `buildSite(id, opts)`, `deploySite`, `buildAll(mode)`, `composeHub` and `composeHomepage`. The orchestration that would become them (`runManagedFunction` jobs, the docker fallback, per-site queues) still lives in the editor's `"use server"` `sites/lib/buildAction.ts` / `deployAction.ts`; - `docker/build-site.sh` and `publish-site.sh` calling the CLI; - export's `build` / `build:nodata` twins becoming one script with `--nodata`; - `build:hub` getting a CLI and an editor entry. **Found and left.** - **`common/package.json` `exports` was on the list of files owned by the follow-ups slice.** The one-line `./publish/*` addition cannot be avoided if the spec's `common/publish/` path is kept: the editor resolves common through `exports`. It was made anyway and is its own line. `git merge-tree` against `one-core/r6-followups` was clean before that slice landed, and the real merge was clean too. - **`Dockerfile.build`'s export image now installs the AWS SDK too**, because it installs common's dependencies. That adds weight to the image and breaks nothing; the export never imports `publish/`. - `plans/STATE.md` still says Phase 4 is next and that `buildDeployCore.ts` imports nothing from the editor (`:73`, `:1485`). Status is the parent's to write, so those lines were not edited. - **Commit trailers** name `Claude Opus 5.5 (1M context)`, as in release 5.