# The slice-5 leftovers, then the transcode operation is removed: it never fired in production ## Context **Verified read-only against `f3b187a` (clean) on 2026-08-30/31**, the commit that closed slice 5. Every file:line was re-read at this sha by the design pass; where the initial brief's anchors or wording were off, a finding says so. The `duplicates-page` worktree exists (HEAD `7baef40`, merge-base `43bb519`) — another session commits into this checkout. The IA docs' remaining editor item was "the transcode band" (`plans/editor-operations-ia.md:60`, `STATE.md:328-331`): teach the snapshot writer a transcode population so `EXTERNAL_BAND_IDS` could go to three. The operator's instinct — that the transcode feature was built for a bug that never occurred — is confirmed by the census, and **the band question closes by deletion, not by a snapshot-shape change**. **The production census** (read-only `jq` over `transcripts/channels/*/{snapshot,config}.json`; nothing booted, nothing written): | fact | figure | |---|---| | channels / snapshots | **68 / 68** — every snapshot carries a `buckets.untranscoded` key | | `generatedAt` | 44 on 2026-08-27, 19 on 08-30, 5 on 08-31 (UTC) | | `buckets.untranscoded` non-empty | **0 of 68** | | `buckets.multipleAudioFormats` non-empty | **0 of 68** | | `cleanupBytes.foreignAudio > 0` | **0 of 68** | | `failed-transcodings` files on disk | **2** (`cornbreadman` mtime Jun 29, `the-quartering-rumble` mtime May 16), **both 0 bytes** | | channels passing `handling==="transcribe" && !!audioFormat` | **4**: cornbreadman, hasanabi, omnimirror, the-quartering-rumble (all mp3) | | `keepLatest > 0` (forces `extractionMode: "app"`, `persistencePlan.ts:92-94`) | **2**: nuxanor-kick 1, quarteringvlogs 5 | **Transcode is three layers wearing one name.** (1) *Download-time extraction*: the app runs ffmpeg itself (`transcodeAudio`, `common/controller/transcode.ts`) when `extractionMode === "app"` — two callers, `audioCheckedDownload.ts:44,768` and `downloadOneManaged.ts:30,224` — forced for keep-latest videos; **live in production, KEPT.** (2) *The transcode OPERATION* (`70ed00f` 2026-05-19 "transcode failure handling"; half of `bb380c6` 2026-06-18): a channel-page Transcode stage, a `failed-transcodings` ledger, four job kinds, `buckets.untranscoded`, a "transcoded" dot and failed filter on the video list, a registry entry carrying the catalog's only `appliesTo`, and `/operations/transcode` — built for yt-dlp's `-x` step failing or a codec whisper could not read. Neither happened, and it could not have mattered: `transcribeOne.ts:22-31` `resolveAudioFile` falls back to ANY real audio file when `audio.` is absent (strict is opt-in per manual batch, default false — `whisperBatch.ts:73,99`). **DELETED.** (3) *Wrong-format audio cleanup* (the other half of `bb380c6`): `multipleAudioFormats`/`foreignAudio`, both sweeps and their bulk actions. **KEPT**, re-gated on `!!config.audioFormat`. **Decided (operator, 2026-08-30):** - **(A) The slice-5 leftovers ride first**, one commit: `measure-nav.mjs` names live routes and warns on a 3xx; `BUILD_KINDS` is the Pool's eight; `EditorAliasesClient` always has a site. - **(B) Option "the operation".** DELETE layer 2 — the four controllers, four job kinds and their actions/replay handlers, the stage everywhere it is wired, the registry entry and (sole taker) `appliesTo`/`operationApplies`. KEEP `transcodeAudio` and the download path, the per-file "Transcode → " rows on the video page (`transcodeAudioAction`, the one escape hatch), both cleanup sweeps, `buckets.multipleAudioFormats`. - **(C) `buckets.untranscoded` is kept and renamed `wrongFormatAudio`** — it is the orphan sweep's list; its name presupposed a transcode. Old snapshots keep a stray key and read `[]` until their next regen; **no regen tooling** (precedent `unified-ops-step-1.md:52-58`: no snapshot schema version, per-field optionality; an offline writer would race `snapshotScheduler`). Production has 0 in the bucket, so nothing rendered moves. - **(D) `EXTERNAL_BAND_IDS` stays `["download","transcription"]` permanently** (`buildBands.ts:163`; `buildBands.test.ts:243-246,322` keep pinning two). - **(E) Stale `failed-transcodings` files stay on disk** (both empty); nothing reads them after this. - **Cadence:** Fable reviews; one Opus agent implements. Stage by explicit path; `git rm`; e2e detached, once, at the end; never boot the editor against `transcripts/`. **What the census found — every place the obvious move cannot be made literally, and what instead:** 1. **The worker tag is on `/workers`, not `/settings`.** `workers/page.tsx:21-28` builds `knownTags` from `operationCatalog().filter(o => o.scope === "video")`. The "transcode" tag disappears with the catalog entry, no edit needed — FACTS.md:2240 names the wrong page; the docs commit corrects it. 2. **`transcode-audio` is not a registered job kind.** Only `videoActions.ts:84` (the kept per-file action) and `stageStatus.ts:119` (the stage map). Drop the map entry with the stage; the job still shows in `RunningJobsList` and marks no stage as running — right, there is no stage. 3. **`bulkVideoActions.ts:215-218` is Clear-failed-markers, not bulk delete.** The prune of `failedTranscodingsFile` goes, `succeeded` becomes `trxn.pruned`, comment `:202-204` says "the failed-transcriptions file". 4. **Editor before common, or tsc breaks mid-series.** Five editor files import the doomed common modules. Commit 2 removes every editor reader; commit 3 deletes the common side. Each commit passes all six tscs. 5. **The bucket rename crosses the package boundary and must be one commit** (commit 4: writer + `normalizeBuckets` + the two readers). The e2e fixtures spelling `untranscoded: []` are untyped JSON literals — **left alone**: `normalizeBuckets` picks only keys it knows, exactly as production's stray key is ignored. 6. **The catalog count is pinned at eight.** `pauseGates.test.ts:104` `ids.length === 8`, `:87-93` expects `transcode: null`; `pauseGates.ts:60` "all eight ids"; `operations.test.ts:1222-1235` lists `"transcode"`. All go to seven. 7. **`jobKinds.test.ts:97` uses `transcode-failures` as its label-less example** → `clear-failed-transcriptions` (replayable, no label, in neither `OLD_LABELS` nor `ADDED_KINDS`). 8. **The "runner first" order in `pauseLaneFor` stays justified without naming transcode** — it is a property of the catalog's shape (an external entry sharing a runner's queue with no runner of its own must answer null). Same rewording for the four "external with no runner" comments (`operations.ts:1422-1427`, `operations.test.ts:1246-1251`, `operations/[id]/page.tsx:149-152`, `operations/lanes.ts:64-70`) and `OperationDetail.tsx:137-139` ("transcode today"); `NoConsoleView` stays as the fallthrough. This is what makes the `transcod` grep gate exact. 9. **`GROUP_STAGES` stays a `Record` of arrays** (`sync: []` still needs it); `media: ["download"]`, paragraph rewritten around sync. 10. **The page's stage-order filter has nothing left to filter.** `page.tsx:256-261` and `stageOrder.test.ts:28-39` both become the bare `...OPERATION_GROUP_ORDER.flatMap((g) => GROUP_STAGES[g])`; the "without transcode" test case (`:56-71`) goes. 11. **`?stage=transcode` bookmarks already have a rule** — unknown `?stage=` lands on the overview at 200 (`channel-stage-selection.spec.ts:89`); the comment at `page.tsx:267-271` gains transcode as its real example. 12. **`FlowStationId` is a key of four `Record`s** (`stationById`, `sidingsOf`, `reachableInto`, `GAP_LABEL`) — all four lose their `transcode` key with the union member. 13. **The "Transcoded" dot was never the operation's fact** — `VideoPanel.tsx:233` `transcoded={transcodeSources.length === 0}` is true of any `audio.*` file, so a video in the channel's own format read "not transcoded". Nothing pins it. **Deleted** (`:233,535, 540,555`); the strip is Downloaded · Transcribed. The per-file rows (`:119-126,169, 202-205,304-318,763-821`) and `transcodeAudioAction` stay; `video-page.spec.ts:118-133, 398-440` pin exactly those. 14. **`transcodeAudio` has TWO download-path callers** (`audioCheckedDownload.ts:768`, `downloadOneManaged.ts:224` `finalizeAppExtraction`) — both KEEP, both in the grep gate's allowed list. 15. **`operation-settings.spec.ts:238-239`** (the transcode goto + assertion) is deleted; the `/operations/download` pair at `:240-241` keeps the same assertion on the same shape. 16. **`bulk-actions.spec.ts:176-206` is trimmed, not deleted** — the failed-transcoding half goes, counts 2 → 1, the transcription half stays. 17. **`channelFlow.ts:153` `config` is an unused input already** — out of scope; do not "fix" in passing. 18. **No doc outside `plans/` describes the Transcode stage.** README/SETUP/install.md say ffmpeg does "audio transcode" — that is the download path and stays true. 19. **The `test-transcribe` fixture's `audio.m4a` files stay** (`audioFormat: "m4a"` — its `untranscoded` bucket was always empty; `video-page`/`whisper`/`bulk-actions` all use the files for kept features). 20. **`bb380c6` is two things** — the FACTS section says which half went. **Hazards carried through every commit:** - **Another session commits into this checkout.** `git status` before every commit; explicit paths; never `git add -A`. The **`duplicates-page` worktree** edits `common/jobs/jobKinds.ts` (its +30 hunk at `:251-290` vs our deletions at `:348-371` — same file, auto-merge needs a human eye), adds `/duplicates` to `nav.ts` (main's `nav.test.ts` pins eleven; `/review` is the folded slot the IA doc reserved), and still carries files main deleted (`build/page.tsx` with its own `BUILD_KINDS`, `actionable/page.tsx`). Their merge; recorded so nobody is surprised. - **Playwright strict mode:** deletions only, no new locators. `"Select failed"` unchanged. - **`stageStatus.ts` keeps `config`** (the configure stage reads it); only the `operationApplies` call goes. **`whisperActions.ts` keeps** `AUDIO_FORMAT_VALUES`/ `AudioFormat` and `clearFailedTranscriptions`; **`jobReplayRegistry.ts` keeps** `AudioFormat`. - **`grep` reads `common/controller/sweepRecency.ts` as binary** — identifier sweeps skip it; trust tsc. - **e2e detached once after commit 5; never against `transcripts/`.** ## Step 0 — the plan on disk Write this file verbatim to `plans/editor-transcode-removed.md`; commit alone: `plans: the transcode removal planned`. ## Order: four code commits after the plan, then the docs 1. **`editor: the slice-5 leftovers — the nav timer names live routes, the pool lists its eight kinds, the aliases tab always has a site`** 2. **`channels: the transcode stage is gone`** 3. **`transcode: the operation is deleted — it never fired in production`** 4. **`snapshot: the untranscoded bucket is wrong-format audio`** 5. **`plans: the transcode operation is removed, and the docs say so`** Gates after each of 1–4: `pnpm -C exec tsc --noEmit` for `common editor export homepage umtool mcp`; `pnpm -C common test`; `pnpm -C editor exec tsx --test "app/**/*.test.ts"` (**121 at `f3b187a` → 119 after commit 2**, unchanged after); `pnpm -C common exec tsx --test controller/noCorpusWalkInRenderPaths.test.ts`. e2e once after commit 5, detached. --- ## Commit 1 — `editor: the slice-5 leftovers — the nav timer names live routes, the pool lists its eight kinds, the aliases tab always has a site` **`editor/scripts/measure-nav.mjs`** - `:38` comment → ``` // Routes worth timing, with the segment name used to build a state tree. THE // ELEVEN SINGLE-SEGMENT PAGES IN THE SIDEBAR (editor/app/lib/nav.ts), and // only those: stateTree() below encodes ONE segment, so /sites//charts // and /operations/ cannot be listed — each would need the nested // [segment, {children: [param, …]}] form, a different tree per route. A // RETIRED ROUTE ANSWERS 307 (editor/next.config.ts redirects); with // redirect: "manual" that is a millisecond stub, not a page timing, and the // >= 400 check below let five of them pass silently for a while. A 3xx is // flagged now. ``` - `:39-50` → the eleven: `["/", "__PAGE__"]`, `/channels`, `/review`, `/operations`, `/sites`, `/jobs`, `/workers`, `/cleanup`, `/saved-videos`, `/settings`, `/changelog` (each `["/x", "x"]`). - `:132` add `const moved = [doc, nav, refresh, pre].find((r) => r.status >= 300 && r.status < 400);` and extend `:144`'s warning ternary: `bad` first, else `moved ? \` ⚠️ ${moved.status} redirect — a stub, not a page timing\` : ""`. `APIS` (`:52-59`) unchanged (none redirected). **`editor/app/sites/page.tsx:29-41`** → ```ts // Which live jobs the Pool section lists: the eight kinds the Pool's own // buttons enqueue (BuildButtons.tsx → sites/lib/buildAction.ts), and only // those. build-export and build-deploy are a site's Publish tab's, build-all // and build-deploy-all are the batch panel's above — each has its own console // and is not repeated here. const BUILD_KINDS = new Set([ "build-index", "build-stats", "normalize-transcripts", "archive-transcripts", "archive-combined-transcripts", "normalize-live-chat", "archive-live-chat", "archive-combined-live-chat", ]); ``` (Traced: `BuildButtons.tsx:12-20` imports exactly those eight actions; kinds at `buildAction.ts:44,59,164,179,194,208,223,238`; `:88,123,260,319` are the four excluded.) **`editor/app/sites/components/EditorAliasesClient.tsx`**: `:3-5` → "Authoring UI for search aliases. Two independent sections — the global dictionary and the site's overrides — each edits a list of concept rows and saves the whole list via a server action." `:82-83` → `siteConfig: AliasConfig; siteId: string;`. `:94-102` → the `` unwrapped (delete `{siteId && siteConfig && (` and `)}`). Only renderer `sites/[siteId]/aliases/page.tsx:39-44` passes both non-null; `aliases.spec.ts` asserts presence only — unchanged. --- ## Commit 2 — `channels: the transcode stage is gone` **`git rm editor/app/channels/[slug]/components/stages/TranscodeStage.tsx`** (244 lines; its labels "Transcode failures", "untranscoded list", "failed transcodings section" leave with it). **`editor/app/channels/[slug]/page.tsx`**: delete `:32` (`loadFailedTranscodings`), `:67` (`TranscodeStage`), `:74` (`operationApplies,`), `:202`, `:227`, `:237`, `:289-290`, `:378-387` (the `case "transcode"`), `:560-561`. `:253-261` → comment "`media`, `transcript`, `digest`, `speakers` each spread their stages; there is no per-channel filter — every stage in the Record applies to every channel." and `...OPERATION_GROUP_ORDER.flatMap((g) => GROUP_STAGES[g]),`. `:267-271` → "…a saved link to a stage that no longer exists (`?stage=transcode`, retired 2026-08-30) should still open the channel." `:482` → `wrongFormatApplies={!!config!.audioFormat}` (`config!` — the block already spells `config!.keepLatest` at `:488`). `:476-481` (the `buckets.untranscoded` fold into `foreignAudioIds`) unchanged in this commit. `TRANSCRIPTION_QUEUE` (`:44`) stays (`:397`). **`…/lib/stageStatus.ts`**: `:9` delete `operationApplies,`; `:51` delete `| "transcode"`; `:74-78` → ``` // NOT 1:1 with the groups, and no honest derivation makes it so: `sync` owns // NO stage (below), and a group may own more than one — `media` did while the // transcode stage existed (retired 2026-08-30). So this is a Record of ARRAYS, // spread in OPERATION_GROUP_ORDER — which yields exactly today's order and // today's cardinality. It is a compile-time membership check, not a re-shaping // of the page. ``` `:85` → `media: ["download"],`; `:115-119` delete all five `"transcode…": "transcode"` map entries (incl. `transcode-audio`); `:162`, `:190`, `:214`, `:220-221` delete; `:325-362` delete the transcode block; `:630` delete `transcode,`. **`…/lib/channelFlow.ts`**: `:66` delete the union member; `:106-108` → "The pipelines that run at this station, in dependency order. Empty for playlist, which is not an operation the registry dispatches or counts — it keeps the plain coverage meter."; `:155-156`, `:230-231` delete; `:318-334` (station), `:387` (spread), `:435-437` (siding), `:508` (gap), `:517` (label) delete. **`…/components/flow/FlowStation.tsx:100-101`** → "The one station that is not a registry operation — playlist — keeps the plain meter." **`…/components/flow/AttentionStrip.tsx`**: `:37-38`, `:63-70` delete. **`…/videos/page.tsx`**: `:7`, `:14`, `:134-135`, `:143-144` delete. **`…/lib/videoRowsServer.ts`**: `:27-28`, `:49`, `:73`, `:76`, `:87`, `:105`, `:111` delete; `:91` → `if (isFailedT) status = "failed";`. **Keep `:39`** (the set feeds `:113`). **`…/lib/videoRows.ts`**: `:16-17`, `:30` delete; `:98-99` → `r.failedTranscription`; `:102` → `return r.failedTranscription;`. **`…/components/VideoListPane.tsx`**: `:253` → "— no queued job."; `:279` → `if (r.failedTranscription) next.add(r.id);`; `:726-731` delete `tcColor`; `:740` → `row.failedTranscription ? "bg-destructive" : null`; `:756` delete the span. **`…/bulkVideoActions.ts`**: `:11-14` delete; `:113`/`:138` "never triggers a transcode" → "never queues a job"; `:202-204` → "Clearing failure markers is a set operation over the failed-transcriptions file, so `succeeded` reports how many ids were actually removed (some selected ids may not have been marked failed)."; `:215-218` delete; `:224` → `succeeded: trxn.pruned,`. **`…/whisperActions.ts`**: `:19-25` delete (keep `:26` `clearFailedTranscriptions`); `:239-346` delete the four actions. **`editor/app/jobs/jobReplayRegistry.ts`**: `:28`, `:30`, `:32-33`, `:216-235` delete. **`editor/app/cleanup/lib/loadCleanup.ts`**: `:10` delete; `:26` → "// Housekeeping list size (failed transcriptions)."; `:28`, `:90`, `:101` delete; `:137-143` → "// The brief already carries the snapshot; only the failed-transcriptions list is still a per-channel read." + `const failedT = await loadFailedTranscriptions(paths, channel.slug); return rowOf(channel, channel.snapshot, failedT.length);`; `:218` → "also reads the failed-transcriptions list per channel". **`editor/app/cleanup/components/ChannelCleanupCard.tsx`**: `:14`, `:27`, `:39` delete; `:45` → `const noHousekeeping = failedTranscriptions === 0;`; `:152-160` delete. **`editor/app/cleanup/page.tsx:142`** delete. **`…/components/stages/CleanupStage.tsx`**: `:28` → `// Both audio-format sweeps compare against the channel's audioFormat; a channel with none has no "wrong" format and neither section applies.` + `wrongFormatApplies: boolean;`; `:54` rename; `:136`, `:151` → `{wrongFormatApplies && (`. **`…/components/SocialChannelPanel.tsx:222`** → "no per-post download or transcription stage." **`…/videos/[id]/components/VideoPanel.tsx`**: `:233`, `:535`, `:540`, `:555` delete (finding 13); the per-file rows untouched. **`editor/app/components/channelWork/sections.test.ts:79`** → "(diarization, attribution-*)". **`editor/app/operations/[id]/page.tsx:224-227`** → "diarization and both attribution operations get no table at all". **Unit tests:** `channelFlow.test.ts` — `:46,48,52,58,64-65` and `:286-309` delete (13 → 12). `stageOrder.test.ts` — `:6-9` the bare expression; `:17` trim; `:28-39` → `function stageOrder(): StageId[]` with the bare spread; `:41-54` → title "the derived stage order is the list that shipped", nine entries, no `"transcode"`; `:56-71` delete (3 → 2). Editor total **121 → 119**. **Specs:** `whisper.spec.ts:227-260` delete (its helpers all still used 8–14× elsewhere in the file). `bulk-actions.spec.ts`: `:128-129` delete; `:176` title → "Clear failed markers prunes the failure file and clears the glyph"; `:180` → "// vidA failed transcription."; `:182` delete; `:187` → "// One failure glyph is shown before clearing."; `:188` → `toHaveCount(1)`; `:191` → `"1 selected"`; `:197` comment singular; `:203-205` delete (`readFile` still used at `:200`). `helpers.ts:340` delete `| "transcode"` from `ChannelStage`. --- ## Commit 3 — `transcode: the operation is deleted — it never fired in production` **`git rm`** `common/controller/transcodeFailures.ts`, `transcodeUntranscoded.ts`, `failedTranscodings.ts`, `removeFailedTranscodings.ts` (no other importer remains after commit 2 — verified). **`common/lib/mediaFiles.ts`**: `:145-156` → one paragraph: ``` // --------------------------------------------------------------------------- // Which audio file to use when a video dir holds more than one: mp3 first, // because that is what this corpus overwhelmingly holds (900 audio.mp3 against // 12 audio.mp4 and 1 audio.aac) and what the transcribe and diarize paths were // tuned against. A second ordering — the format most likely to be the ORIGINAL // download, for picking a transcode SOURCE — left with the transcode operation // on 2026-08-30; pickPreferredAudio keeps its `preference` parameter and every // caller (transcribeOne, diarizeOne, operations.ts) takes the default. ``` `:163-170` (`AUDIO_TRANSCODE_SOURCE_PREFERENCE`) delete. **`common/lib/mediaFiles.test.ts`**: `:5`, `:179-187`, `:198-203` delete (19 → 17). **`common/jobs/jobKinds.ts:348-371`** delete the four entries. **`common/jobs/jobKinds.test.ts:97`** → `assert.equal(jobKindLabel("clear-failed-transcriptions"), "clear-failed-transcriptions");`. **`common/lib/operations.ts`**: `:77` delete the `ChannelConfig` import; `:1364-1384` delete the entry; `:1422-1427` → "The auto-queue runner that dispatches this, when one does. `dispatch` alone cannot answer it: download and transcription are `external` WITH a runner, and an external operation with none is a legal entry (transcode was one, 2026-08-26 → 08-30). A console that guessed from the id would hand such an entry the transcription runner's controls — a live Start button over the wrong lane. Absent for every backfill kind: those are dispatched by the sweep and the arbiter."; `:1433-1440` (`appliesTo` field + comment) delete; `:1441-1443` → "// See Operation.settingsBlock. Absent for every external operation but sync: nothing in settings.json configures a download as an operation."; `:1471-1478` (`operationApplies`) delete. **`common/lib/operations.test.ts`**: `:12`, `:1227`, `:1268-1290` delete (65 → 64); `:1246-1251` reword per finding 8; `:1347-1348` → "// Sync is its own group, not `media`: that group is download's /channels column group, and sync draws no column." **`common/lib/pauseGates.ts`**: `:55-58` → ``` // `runner` IS ASKED FIRST, and that order is load-bearing. An external // operation may share a runner's queue and have no runner of its own (transcode // did, on TRANSCRIPTION_QUEUE, until 2026-08-30); a queue-key map alone would // hand it that runner's pause — a live Pause button over a lane that would // never dispatch it. Such an entry's own answer is null. ``` `:60` → "all seven ids". **`common/lib/pauseGates.test.ts`**: `:87` → "pauseLaneFor answers for every catalog id"; `:90-93` delete; `:104` → `7`. **`common/controller/videoOperations.ts:16-17`** → "Download and transcription already have a per-video surface". **`editor/app/operations/[id]/page.tsx:149-152`**, **`OperationDetail.tsx:137-139`**, **`operations/lanes.ts:64-70`** → reword per finding 8 (the agent's exact texts; `NoConsoleView` stays). **Spec:** `operation-settings.spec.ts:238-239` delete (the `/operations/download` pair stays). `/operations/transcode` now 404s through the catalog-as-route-table (`[id]/page.tsx:143-147`); the `transcode` tag on `/workers` is gone with the entry. --- ## Commit 4 — `snapshot: the untranscoded bucket is wrong-format audio` **`common/controller/channelSnapshot.ts`**: `:110` → ```ts // Has audio on disk, none of it in the channel's audioFormat — the orphan // half of what "Remove wrong-format audio" deletes (the other half is // multipleAudioFormats: target present plus extras). Was `untranscoded` // until 2026-08-30, when the transcode operation that read it as a to-do // list was removed; the population itself is still the sweep's. Old // snapshots keep a stray `untranscoded` key and read this one as [] until // their next regen — no schema version, per-field optionality, as always. wrongFormatAudio: string[]; ``` `:765` accumulator, `:923` push, `:1145` write → renamed; `:873-875` comment → "// audio — short-circuit so it doesn't land in wrongFormatAudio / downloadedNoTranscript (which would transcribe corrupt audio, or offer the kept file to the wrong-format sweep) or the wrong-format cleanup estimate (which would delete it)." The predicate (`:551-553`, `:918-922`) unchanged. **`…/lib/stageStatus.ts:25`** → `wrongFormatAudio: raw?.wrongFormatAudio ?? [],`. **`…/page.tsx:478`** → `...buckets.wrongFormatAudio,`. **`…/lib/videoRowsServer.ts`**: `:39` → `const wrongFormatAudio = new Set(buckets.wrongFormatAudio);`; `:113` → `wrongFormatAudio.has(id) || multipleAudioFormats.has(id),`. No regen, no tooling (decided C). --- ## Locator table — the specs | locator | before | after | spec | |---|---|---|---| | `channelStage("test-transcribe","transcode")`, `/^Transcode failures → mp3$/`, `"Transcode failures output"` | the stage | **gone** (test deleted; `ChannelStage` loses the member) | `whisper:227-260`, `helpers:340` | | `getByTitle("failure recorded")` count | 2 | **1** | `bulk-actions:188` | | `"2 selected"` | after Select failed | **`"1 selected"`** | `bulk-actions:191` | | `readFile(failedTranscodingsFile)` | asserted pruned | **gone** | `bulk-actions:203-205` | | `goto("/operations/transcode")` + no settings form | 200, no form | **gone** (404); `/operations/download` pair stays | `operation-settings:238-239` | | `/^Transcode audio\.m4a → mp3$/`, `"Transcode audio.m4a to mp3 output"`, `"Transcode stage summary"` | per-file rows | **unchanged** | `video-page:118-133,398-440` | | `getByLabel("pipeline status")` | three dots | unchanged (two dots) | `channel-embedded-video:25-55` | | unknown `?stage=` → overview, 200 | pinned | unchanged; `?stage=transcode` is now a real instance | `channel-stage-selection:89` | | `untranscoded: []` in seeded snapshot JSON | | **left** (stray key, ignored) | `auto-queue:80,133`, `channel-work:97`, `cleanup-actionable:30`, `disk-space:106`, `widget:42` | | `"Select failed"`, `"Clear failed markers"`, wrong-format labels | | unchanged | `bulk-actions`, `ChannelCleanupCard` | ## Verification 1. After each of commits 1–4: the six tscs; `pnpm -C common test` (operations.test 65 → 64, mediaFiles.test 19 → 17, pauseGates.test 6, jobKinds.test 4 — after commit 3); editor units **121 → 119** (commit 2); the render-path guard test. 2. Grep gates after commit 4, over `editor/app common editor/scripts` (`*.ts *.tsx *.mjs`, excluding `node_modules`): - `transcod` (case-insensitive) → **only** the KEEP list: `common/controller/transcode.ts`; `common/ytdlp/audioCheckedDownload.ts`; `common/ytdlp/downloadOneManaged.ts`; `ffmpegStreamClassify.ts`/`ffmpegStreamProbe.ts`/`progressParsers.ts:180`/ `scanCorruptMedia.ts:11,77`/`review/actions.ts:138`/`RunMediaScanButton.tsx:17` (probe prose); `channelConfig.ts:36`; `mediaFiles.ts` (download-path prose + rewritten `:145-152`); `mediaFiles.test.ts` (scratch-file prose); `pauseGates.ts` (rewritten); `operations.ts` (rewritten); `OperationDetail.tsx` (rewritten); `operations/lanes.ts` (rewritten); `VideoPanel.tsx` (per-file rows); `videoActions.ts`; `videos/[id]/page.tsx:235`; `channelSnapshot.ts` (the new `:110` comment); `stageStatus.ts:74-78`; `channels/[slug]/page.tsx:270`. Anything else is a miss. - `untranscoded` → **0** under `editor/app common` (the five e2e fixtures remain, deliberate). - `failedTranscod|FailedTranscod|failed-transcodings` → **0** under `editor/app common` and `editor/e2e` (the spec's const goes too). - `operationApplies|appliesTo` → **0**. - `TranscodeStage|transcodeApplies|transcodeDefaultQueueKey|AUDIO_TRANSCODE_SOURCE_PREFERENCE|runTranscodeFailures|runTranscodeUntranscoded|removeFailedTranscodings|clearFailedTranscodings|loadFailedTranscodings|pruneFailedTranscodings` → **0**. - `"transcode"` (quoted id) → **0** under `editor/app common`. - `wrongFormatAudio` → `channelSnapshot.ts` (4), `stageStatus.ts` (1), `page.tsx` (1), `videoRowsServer.ts` (3); `wrongFormatApplies` → `CleanupStage.tsx` (4), `page.tsx` (1). - `EXTERNAL_BAND_IDS = \["download", "transcription"\]` → exactly one hit (`buildBands.ts:163`); `buildBands.test.ts` unchanged. - `ids.length, 7` → `pauseGates.test.ts`; `length, 8` → 0 in `common/lib`. - `BUILD_KINDS` set → 8 entries; `WRONG IN` → 0; `siteId: string \| null` → 0 in `EditorAliasesClient.tsx`; `/actionable|/auto-queue|"/build"|"/charts"|"/aliases"` → 0 in `measure-nav.mjs`. 3. e2e once after commit 5, detached (`cd editor && setsid nohup sh -c 'pnpm e2e -- ; echo exit=$?' > $CLAUDE_JOB_DIR/tmp/e2e.log 2>&1 < /dev/null & disown`), exactly: `whisper.spec.ts bulk-actions.spec.ts operation-settings.spec.ts video-page.spec.ts channel-stage-selection.spec.ts channel-line.spec.ts channel-embedded-video.spec.ts cleanup-page.spec.ts cleanup-holds.spec.ts disk-space.spec.ts cleanup-actionable.spec.ts channel-work.spec.ts auto-queue.spec.ts widget.spec.ts jobs.spec.ts workers.spec.ts aliases.spec.ts build.spec.ts navigation.spec.ts perf-budget.spec.ts` (all names verified to exist). Port fallback `PORT=3111 EXPORT_PORT=3110 OLLAMA_STUB_PORT=11535`; never kill anything. 4. **No editor boot against `transcripts/`; nothing written under it.** The census is `jq` only. 5. Manual (optional, `PORT=3021 pnpm dev:test`): `/channels/test-transcribe` has no Transcode stage; `?stage=transcode` opens the overview; `?stage=cleanup` shows both audio-format sections (fixture has `audioFormat`); a channel with none shows neither; `/channels/test-transcribe/videos/vidA` shows Downloaded · Transcribed and the "Transcode audio.m4a → mp3" row; `/operations/transcode` is 404; `/workers` offers no `transcode` tag; `/cleanup` cards have no "Clear failed transcodings"; `node scripts/measure-nav.mjs --base http://localhost:3021` lists eleven routes, none flagged. ## Commit 5 — `plans: the transcode operation is removed, and the docs say so` - **`editor/CHANGELOG.md` `[Unreleased]` first bullet** (house voice): **The transcode operation is gone — it never fired.** A channel page had a *Transcode* stage, `/operations/transcode` had a "no console here" panel, `/cleanup` offered "Clear failed transcodings", and the video list drew a third status dot — all for a re-encode step built against two failures that never happened in production: in 68 channels, no snapshot has ever listed a video as missing its target format, no `failed-transcodings` file has ever held an id, and only four channels even met the stage's gate. Transcription never needed it — a video whose audio is in another format transcribes from that file. What stayed is everything that was never the operation's: the download path still re-encodes what it extracts itself, the video page still offers **Transcode audio.\ → \** per file, and both audio-format sweeps on the Cleanup stage and `/cleanup` are unchanged (gated on the channel having an `audioFormat`, which is what they compare against). The snapshot bucket behind the sweep is `wrongFormatAudio` now — its operator-facing name — and old reports keep their stray key until their next refresh. A `?stage=transcode` bookmark opens the channel overview. **Nothing on disk changes.** Also: the Pool's running-jobs list names the eight kinds its buttons enqueue, and the site's Search aliases tab no longer carries a "no site selected" branch that could not run. - **`plans/editor-operations-ia.md:60`** → the Transcode row becomes "**REMOVED 2026-08-30 — never fired in production; the band question is closed**" with the census one-liner, what was deleted, what stayed, `EXTERNAL_BAND_IDS` is two permanently, plan + FACTS pointers (the agent's exact row text). In "Slice 5, as shipped": `:767` (finding 14, `BUILD_KINDS`) and `:776` (what stayed: `BUILD_KINDS`' mismatch, `EditorAliasesClient`'s null branch) each gain "— fixed 2026-08-30, first commit of the transcode removal". - **`plans/STATE.md`**: `:6` "Last updated" prepend (previous demoted to "Previously:"); after `:326` item `14. ~~**The transcode operation**~~ — **REMOVED 2026-08-30**, `` → ``. Plan: [`editor-transcode-removed.md`]; census in FACTS "Verified 2026-08-30 — transcode: what it was, what stayed". The band question is closed by deletion; `EXTERNAL_BAND_IDS` is two for good.`; `:328-331` → "**Recommended next (editor IA).** The UI slices are done and the transcode band is no longer a candidate. What is left is **Phase 6** and **slice 9**, which is gated on the sweep running in production."; `:459-463` gains "**Removed 2026-08-30** — see item 14 above." - **`plans/FACTS.md`**: append `## Verified 2026-08-30 — transcode: what it was, what stayed`: the census table verbatim; the two building commits and which half of `bb380c6` stayed; the `resolveAudioFile` fallback; the two `transcodeAudio` callers and `persistencePlan.ts:92-94`; `transcode-audio` never registered; the bucket rename and why no regen; the "runner first" order surviving without its example; catalog 8 → 7 and where pinned; the worker tag being `/workers` not `/settings` (correcting `:2240`); the `duplicates-page` merge hazard. At `:2229` prepend "**SUPERSEDED 2026-08-30 — removed, see the section at the end of this file.**" - Memory: left to the reviewer. ## Out of scope `channelFlow.ts:153`'s unused `config` input. Regenerating snapshots or deleting the two empty `failed-transcodings` files. Renaming the five e2e fixtures' `untranscoded: []`. Any change to `EXTERNAL_BAND_IDS`, `buildBands`, or the pipelines rail. The download path (`audioCheckedDownload`, `downloadOneManaged`, `ExtractionMode` docs). The per-file transcode rows and `transcodeAudioAction`. The `duplicates-page` worktree. `measure-nav.mjs` measuring dynamic routes. The `bulk-actions.spec.ts:133,169,210,243` prose comments. ## Handoff — the cadence On approval, Fable does not implement (memory `plan-then-opus-implements`): write the plan to `plans/editor-transcode-removed.md`, commit it alone, then spawn one `general-purpose` agent, `model: "opus"`, with: the plan path; the shell caveats (quote every glob and every `[slug]`/`[siteId]`/`[id]` path, `--include='*.ts'`, no bare `====` echo; POSIX loops inside `sh -c`; `git commit -F `; stage by explicit path, never `git add -A`; `git rm` for the five deletions); never boot against `transcripts/`; e2e detached (memory `e2e-run-detached`); the two trailer lines; the report contract (shas + one line each; exact gate outputs incl. unit-test counts before/after each commit — expect 121 → 119 at commit 2, common 65/19 → 64/17 at commit 3; every grep gate's actual file list against the expected one; e2e per spec with retries; every divergence and why; anything undone). Fable reviews on return (`git log --oneline f3b187a..`; the `page.tsx` hunks — the spread, the `:270` comment, `wrongFormatApplies`; `stageStatus.ts` — the `GROUP_STAGES` paragraph, the map, the return; `channelFlow.ts`'s four Records; `videoRowsServer.ts:39,113`; `bulkClearFailedMarkersAction`; the import lines; `CleanupStage` gating; the `VideoPanel` strip; `operations.ts` — the entry, `appliesTo`, the rewritten comments, the `ChannelConfig` import; `pauseGates.ts:55-60` + test; `mediaFiles.ts:145-152`; `channelSnapshot.ts:110` and the four renames; the three specs and `helpers.ts`; `measure-nav.mjs`; `BUILD_KINDS`; `EditorAliasesClient`; re-runs grep gates + `pnpm -C common test` + editor units, not e2e), sends fixes via SendMessage, and reports.