commit 7cddd7cc34b26acf31395bef85b2bf808ab9198e
parent 7b274f41fb4b945eee46b4b49e3dc24f43ff513f
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 15 Sep 2026 15:50:57 -0400
plans: one-core Phase 3 slice 1 shipped — views in the core, editor as shell
D record (fe00c4a..7b274f4, the full 533), the slice-1 summary, the STATE.md Next
block and a FACTS anchor for the view layer, its guard and its shells.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
3 files changed, 91 insertions(+), 6 deletions(-)
diff --git a/plans/FACTS.md b/plans/FACTS.md
@@ -4007,3 +4007,43 @@ message, same two chunks. It predates Phase 2 and is the same failure already re
the export responsive redesign. What the hub build DOES prove each time it is run: it compiles
and type-checks, so the bundle resolves `lib/archive/*` and `lib/search/*` in hub mode too and
never pulls `reader-fs.ts` into a client chunk.
+
+## One-core Phase 3 slice 1 (verified 2026-09-15, `main` @ `838da4a`)
+
+- **`common/views/` is the view-model layer.** It sits above `lib/`, `controller/`, `jobs/`
+ and below `components/`; `common/architecture.test.ts` forbids `lib`/`jobs`/`controller` →
+ `views` and `views` → `components`/`ytdlp`/`bin`/`social`, and adds two tests: no bare
+ `react`/`next`/`server-only`/`client-only`/`node:`/`yt-dlp-transcript-common` specifier in a
+ non-test view (the self-name is listed because a package-name self-import hides from the
+ relative scan), and a TEXTUAL, context-blind ban on `getRegistry(`, `getScheduler(`,
+ `getWorkerPool(`, `getPaths(`, `getSettings(`, `getAutoRunnerStatus(`, `readChannelStat(`,
+ `readJobMeta(`, `listChannelBriefs(`, `listChannelConfigs(`, `readSchedulerState(`,
+ `readAutoQueueState(`, `computeLeafPending(`, `readWorkerDefaults(`, `diskGate(`, `Date.now(`.
+ A comment that mentions one with its parenthesis fails it; reword. **The allow-list is 10.**
+- **Views import common RELATIVELY.** `common/package.json` exports `"./views/*": "./views/*.ts"`
+ (a string) and both test-glob brace lists include `views`; `noCorpusWalkInRenderPaths.test.ts`
+ walks `common/views` too. A `.tsx` under `views/` would need its own exports line.
+- **The port is `views/inputs.ts`** (`LiveInputs`, `ObserveInputs`, `RegistryReader`,
+ `PoolReader`; types only) and **the one constructor is `editor/app/lib/liveInputs.ts`**
+ (`liveInputs()` calls the five getters; `observeInputs()` reads `globalThis.__ytt*__`, two
+ `statSync` mtimes and injects `sha1/base64url`). The three globals are declared by
+ `common/jobs/{registry,scheduler,workerPool}.ts`; the pulse route no longer restates them.
+- **The injected reader fields are `channelStat` / `jobMeta`**, not `readChannelStat` /
+ `readJobMeta` — the textual ban forbids the latter spelling inside a view.
+- **Shells at the old paths keep the old names and values:** `jobs/active/buildActiveJobs.ts`
+ (120 lines, holds the 8 KB `readLastLogLine` tail), `workers/buildWorkers.ts` (20),
+ `operations/status.ts` (72; reads `.auto-queue/state.json` ONCE per poll, was 4×),
+ `operations/channelPriorityView.ts` (86; `readPriorityView` stays), `scheduler/status.ts`
+ (32; `resolveHeartbeatSeconds` imports `runTick`, never from views),
+ `widget/lib/syncInputs.ts` (25). `operations/lanes.ts` is DELETED. `api/widget/sync/route.ts`
+ and `api/pulse/route.ts` export exactly `GET` and `dynamic`. Every editor import of a shell
+ is a value import; every payload TYPE is imported from `yt-dlp-transcript-common/views/*`.
+- **`/api/pulse` rev bytes are unchanged**; `views/pulse.test.ts` asserts the hashed string
+ through an identity digest, so a reorder names what moved.
+- **`liveJobRows` never heals; `buildActiveJobsPayload` always heals** (`scheduler.complete`
+ lives in the view) — both asserted in `views/activeJobs.test.ts` against `createScheduler()`.
+- **`pnpm wt rm` takes the worktree DIRECTORY name** (`one-core-phase-3-s1b`), not the branch,
+ and needs `--force` once `export/public` is seeded with the fixture site.
+- **The full editor suite took 38.8 min on `838da4a`** (533/533, nothing flaked) against the
+ 23-min idle-machine baseline; two other Opus implementers were building in sibling
+ worktrees at the time. Wall time is not a signal here; the pass count is.
diff --git a/plans/STATE.md b/plans/STATE.md
@@ -3,7 +3,7 @@
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-14 (the rack landed; see the Next block) — 2026-09-13: **gate A passed and `main` moved; the interlude shipments and one-core Phase 2 are all merged
+**Last updated:** 2026-09-15 (Phase 3 slice 1 and the rack landed; see the Next block) — 2026-09-13: **gate A passed and `main` moved; the interlude shipments and one-core Phase 2 are all merged
on one branch**, `integrate/2026-09-storage-priority`, tip **`bd3d4ec`**, unmerged. Off
`e74f005`: `relocate-channel-media` (from `storage/relocate-media`), then `channel-priority`
(from `channel-priority/s5`), then Phase 2's five slices in the order their reviews cleared —
@@ -333,10 +333,16 @@ on the identical tree — the fast-forward got a `tsc --noEmit` smoke, not a sec
suite). One deck, a sticky rack and a meter bridge; the record is
[`editor-channels-rack.md`](editor-channels-rack.md). The worktree and branch are deleted.
It merged BEFORE the rollout's channel moves on purpose: those go through the `/channels`
-bulk deck, which the rack rewrote, so the operator does them on the merged page. **Next, in
-parallel:** the rollout below (operator-physical), and **one-core Phase 3 slice 1** —
-`common/views/` — planned in [`one-core-phase-3.md`](one-core-phase-3.md), decided with the
-operator 2026-09-14 as slice 1 only. Phase 3 slice 3 rewrites `ChannelsTable.tsx` again;
+bulk deck, which the rack rewrote, so the operator does them on the merged page. **One-core Phase 3 slice 1 SHIPPED 2026-09-15** — `main` `1faa987` → **`838da4a`**, fifteen
+code commits in four fast-forwarded sub-slices (A serial, B ∥ C, D serial), record in
+[`one-core-phase-3.md`](one-core-phase-3.md#slice-1--shipped-2026-09-15). Every payload the
+editor draws is a pure function in `common/views/` handed its singletons, readings and
+clock; the layer guard proves it (allow-list **11 → 10**, two new tests, a textual ban on
+getters, readers and `Date.now`); common **1264/1264**, the full editor suite **533/533**.
+Nothing rendered, routed or serialized moved; the one live change is `/operations` reading
+`.auto-queue/state.json` once per poll instead of four times. **Next, in parallel:** the
+rollout below (operator-physical), then S0-pause one boot later, then **Phase 3 slice 2**
+(unplanned; `one-core.md` §Phase 3 — the plan file's "What slice 2 inherits" is the start). Phase 3 slice 3 rewrites `ChannelsTable.tsx` again;
landing the rack first keeps that a rebase-free rewrite of one file.
**The rollout:** the **O2–O9 rollout**, editor stopped. **The migration script only exists on
@@ -360,7 +366,7 @@ held. Boot this release once, confirm all four `held` keys are in the live `sett
then take it.
**Then the rest of one-core Phase 3** — views in the core, editor as shell, four slices
-([`one-core.md`](one-core.md) §Phase 3); slice 1 is already in flight (above). Read `common/architecture.test.ts`'s allow-list
+([`one-core.md`](one-core.md) §Phase 3); slice 1 shipped (above), slices 2–4 unplanned. Read `common/architecture.test.ts`'s allow-list
first: it is the shortest accurate statement of what is still tangled. Phase 2 left it at
**eleven entries, byte-identical to the base**, while ADDING `"components"` to
`FORBIDDEN.lib` — the forbidden list grew and the debt ledger did not.
diff --git a/plans/one-core-phase-3.md b/plans/one-core-phase-3.md
@@ -427,3 +427,42 @@ Divergences:
**Merged tip `5ddc158`**: tsc clean; common **1264/1264**; editor unit 59/59. The two
`"use client"` importers of a view (`components/pulse.ts`, `WorkersView.tsx`) are type-only.
+
+### D — shipped 2026-09-15
+
+Branch `one-core/phase-3-s1d` off `5ddc158`, rebased onto `36ab02c` and fast-forwarded:
+`3a20156` (repoints + deletions) → `838da4a` (changelog). Worktree and branch deleted.
+
+Gates (worktree #2): tsc clean after each commit; common 1264/1264; editor unit 59/59;
+editor and export `next build` clean; **the full editor suite 533 passed, exit 0**, in 38.8
+min (the 23-min baseline was measured on an idle machine; nothing flaked and nothing was
+re-run). 18 files repointed to `views/*` with `import type`; `operations/lanes.ts` deleted;
+the seven `export type` re-export blocks deleted; both routes export exactly `GET` and
+`dynamic`; every remaining editor import of a shell is a value import (17 lines).
+
+Divergences: `components/pulse.ts`, `api/pulse/route.ts` and `operations/[id]/page.tsx`
+needed nothing (C4 had already left them final; the page names no payload type);
+`channelPriorityView.ts` also lost a dead VALUE re-export of `pendingByLeafFromCounts` /
+`laneFocusSummary` (no importer since C2); no type had to be added to a view.
+
+## Slice 1 — shipped 2026-09-15
+
+`main` `1faa987` (plan) → `838da4a`, 15 code commits plus three records, all fast-forwards.
+**Every payload the editor draws is a pure function in `common/views/`, handed its
+singletons, its readings and its clock; the layer guard proves it (allow-list 11 → 10,
+two new tests, the textual ban); nothing rendered, routed or serialized changed.**
+
+| | |
+|---|---|
+| `common/views/` | 34 files: 4,163 non-test lines (2,662 moved as renames), 2,969 test lines |
+| `editor/app` | −5,338 lines net (`--no-renames`: 78 files, +357 / −5,695) |
+| common tests | 1159 → **1264** (62 moved + 3 guard/assignability + 15 B + 25 C) |
+| editor unit | 92 → 59 |
+| allow-list | 11 → **10**, nothing added |
+| live behaviour | `readAutoQueueState` once per `/operations` poll, was 4× |
+
+What slice 2 inherits: the shells (`buildActiveJobs.ts`, `buildWorkers.ts`,
+`operations/status.ts`, `scheduler/status.ts`, `channelPriorityView.ts`, `widget/lib/syncInputs.ts`)
+and `lib/liveInputs.ts` are the whole editor-side surface of a view; `getAutoRunnerStatus`
+and `computeLeafPending` are still called four times per poll from the status shell (not
+claimed fixed; the reads are in-memory).