commit 25f89819dd021ccc071f58f336a006d79d6fb419
parent 1bf60f04b18157b893871f6f4a74b6623ba1cf81
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sat, 26 Sep 2026 18:50:57 -0400
plans: slice P — cut a release from the CLI (one controller; archilyzer release cut/show locally, pnpm ops cut-release through /api/ops/cut-release; the /sites form becomes an adapter); slice N's cookies-mode repeat on main 5/5
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
2 files changed, 90 insertions(+), 2 deletions(-)
diff --git a/plans/cut-release-cli.md b/plans/cut-release-cli.md
@@ -0,0 +1,88 @@
+# Cut a release from the CLI (release 10 slice P)
+
+Written 2026-09-26 (night). Operator: "Are you able to cut releases with the CLI ops? add that ability
+if not." Answer today: NO. The only cutter is the `/sites` page's "Release notes" form
+(`editor/app/sites/components/CutReleaseForm.tsx` → `editor/app/sites/lib/cutReleaseAction.ts`), and
+the runbook's step 0 ("re-cut the release notes first") is a click. Anchors verified on `main` 5f577437.
+
+## What exists (verified)
+
+- `common/lib/changelog.ts`: `cutRelease(source, version, dateISO)` (:58-88; semver + date checks,
+ `## [Unreleased]` → `## [<v>] - <date>`, refuses when nothing is pending), `getLatestReleasedVersion`
+ (:26), `suggestNextVersion` (:31), `extractUnreleasedSection` (:40), `CutReleaseError`.
+- `cutReleaseAction.ts` (the server action, "use server"): form fields `workspace` (editor|export),
+ `version`, `commit`; the dirty-tree guard (`listDirtyPaths`, `common/lib/git.ts:24` — refuses to
+ commit when anything but the changelog is dirty), `readFile` → `cutRelease(…, todayISO())` →
+ `writeFileAtomic` → optional `commitPath(root, relPath, "Release <workspace> <version>")`
+ (`git.ts:36`), then `revalidatePath("/")`, `/changelog`, `/sites`. Paths:
+ `getPaths().editorChangelogFile` / `.exportChangelogFile`, `monorepoRoot`. Returns
+ `{ok:true, version, committed} | {ok:false, error}`. Version strings in `package.json` are never
+ bumped (all 0.1.0) — the changelog heading IS the version; there are no git tags (0).
+- Release commits so far are one-line heading edits: `0e0d8f59 Release editor 0.9.0`,
+ `31798769 Release export 0.9.0`.
+- Two CLIs: `pnpm ops` (`scripts/archilyzer-ops.mjs`, plain node, HTTP-only: `ACTIONS` :88-110 →
+ `POST /api/ops/<action>` with `Bearer WORKER_TOKEN`, usage :277; tests `archilyzer-ops.test.mjs`
+ :15-45 pin "an action becomes a POST to its route" and the refused-unknown-action list) and
+ `archilyzer` (`common/bin/archilyzer.ts`, a table of local tsx verbs over `_cli.ts`; tests
+ `_cli.test.ts`; e.g. `index`, `compose:site`, `sync tick`, `settings-example`).
+- Ops API routes: `editor/app/api/ops/<action>/route.ts` on `ops(request, allowedKeys, handler)`
+ from `_lib.ts` (worker-token auth, JSON body, `OpsInputError` → 400, `optString/optBool/reqSlug`,
+ `actionResponse`); `refresh-report/route.ts` is the model for a synchronous (non-job) action.
+- e2e: `editor/e2e/cut-release.spec.ts` (:53, :74, :95) drives the form against the fixture's
+ changelog copies. Ops routes' e2e precedent: grep `api/ops` in `editor/e2e/*.spec.ts`.
+
+## Decisions (made)
+
+1. **One writer.** New `common/controller/cutRelease.ts`: `cutReleaseForWorkspace({workspace,
+ version, commit, date?})` = the body of `cutReleaseAction` verbatim (guard, read, cut, atomic
+ write, commit), returning `{ok:true, workspace, version, committed, commitSha?, heading} |
+ {ok:false, workspace, error}`. `version: "next"` resolves through
+ `suggestNextVersion(getLatestReleasedVersion(source))` (patch bump) — `"next-minor"` bumps the
+ minor. `cutReleaseAction` becomes a thin FormData adapter over it (keeps `revalidatePath`; the
+ form's fields, labels and `cut-release.spec.ts` unchanged). `workspace: "all"` (CLI/API only) =
+ editor then export with the SAME version, two commits in today's message form; stops at the first
+ failure and reports what was done.
+2. **Local verb, the primary path**: `archilyzer release cut <editor|export|all> <version|next|next-minor>
+ [--commit] [--date YYYY-MM-DD]` in the `archilyzer.ts` table; prints one line per workspace
+ (`editor: ## [0.10.0] - 2026-09-26 (committed 1a2b3c4d)`), exit 1 on any failure. Plus
+ `archilyzer release show [editor|export]`: the latest released version, its date, and how many
+ bullets are pending under `[Unreleased]` — the runbook's step-0 check. Works with NO editor running
+ (this rollout's step 0 precedes step 1's restart, and the live editor is a pre-brand build).
+3. **Ops verb, the remote path**: `pnpm ops cut-release --json '{"workspace":"editor","version":"next",
+ "commit":true}'` → new `editor/app/api/ops/cut-release/route.ts` (synchronous, `ops()` +
+ `OpsInputError` for a bad workspace/version, `actionResponse`-style body `{ok, results:[…]}`),
+ `ACTIONS` + usage gain `cut-release` (usage says what the body takes and that `all` cuts both).
+ Same controller. Note in usage: the route exists only on an editor running this release or later.
+4. No version bump in `package.json`, no git tag (neither is a convention here — say so in the record).
+5. `--date` is for a re-cut of an already-dated release only; default today (local time, as the
+ action does).
+
+## Implementation (one Opus slice, branch `cli/cut-release`, its own worktree)
+
+1. Controller + `cutRelease.test.ts` (temp git repo via `git init`/`commit` in a temp dir: cut →
+ heading + commit message; `next` → patch bump of the latest heading; `next-minor`; nothing pending →
+ error; dirty tree with `commit: true` → refused naming the file; `all` stops at the first failure
+ and reports the first as done; `--date` used verbatim; NEVER touches the real repo — every test
+ passes an explicit root).
+2. `cutReleaseAction.ts` → adapter; `cut-release.spec.ts` must pass unchanged.
+3. `archilyzer.ts` verbs + `_cli.test.ts` cases (parse, usage listing).
+4. `/api/ops/cut-release` + `ACTIONS` + usage + `archilyzer-ops.test.mjs` (+1: the action posts to
+ its route; the unknown-action list includes it) + one editor e2e in the ops style (POST with the
+ worker token against the fixture changelogs → 200 with the heading; a bad workspace → 400).
+5. Docs: `README.md`/`AGENTS.md` only where they mention cutting a release (grep "Cut release",
+ "re-cut"); the runbook is the parent's. Record `### Slice P, as shipped — cut a release from the
+ CLI (2026-09-26)` in `plans/release-10.md` before `## Rollout`; one `[Unreleased]` bullet in
+ `editor/CHANGELOG.md`.
+
+## Gates (worktree root)
+
+tsc; common tests (1,987 baseline on this main → new count); editor unit (85); `pnpm run test:scripts`
+(173 + 1 → new count); mcp 269 unchanged; `pnpm --filter editor exec next build`; editor e2e detached +
+queued: `cut-release.spec.ts` + the new ops e2e + every spec that greps `api/ops` (list them).
+Numbers tool: none.
+
+## Rollout (parent)
+
+The runbook's step 0 becomes `pnpm --filter yt-dlp-transcript-common exec tsx bin/archilyzer.ts
+release show` then `… release cut all next --commit` (or an explicit version) from the primary, before
+step 1's restart. Prove `pnpm ops cut-release` after the restart on a throwaway: `release show` only.
diff --git a/plans/release-10.md b/plans/release-10.md
@@ -1610,8 +1610,8 @@ every video its confirmation check did not reach; `/jobs` says why a job was can
live until the :3001 restart (step 1); then prove: `fetch_clip` `full: true` on `teamrcn/dbnS-cBgStY`
(the slice M no-op) → 202 → done with a file under `transcripts/saved-videos/teamrcn/dbnS-cBgStY/`,
`transcript.json` mtime unchanged, no `audio.*`, and `data/dbnS-cBgStY/metadata.history.json` with one
-entry (its `changed` may be empty). Reviewer's follow-up: run `cookies-mode.spec.ts:241 --repeat-each=5`
-on a `main` checkout when the e2e lock is free; if `main` stays green and the branch failed 2 of 4, re-open.
+entry (its `changed` may be empty). Reviewer's follow-up DONE: `cookies-mode.spec.ts:241 --repeat-each=5` on merged `main` (5f577437)
+→ 5/5 passed in 1.8 min — the branch's 2-of-4 was machine load (7–10 during its run), not slice N.
### Slice M — fetch_clip, merged and proved live (2026-09-26 evening)