# Deploy the homepage from the editor Written 2026-09-26 (evening) by the slice N implementer, on the operator's ask. Rules: `plans/tools/implementer-rules.md` (one Opus implementer in a worktree, one read-only Opus review, the parent merges). Small: one slice, editor + ops only; nothing in `homepage/` or `export/` changes. ## The operator's ask (verbatim, 2026-09-26) "Is there a way to redeploy archilyzer.pages.dev from the UI? If not, work adding one into the plan and deploy it yourself in the meantime" **There is not.** - `/sites` → Hub (`editor/app/sites/page.tsx:183-201`) builds and deploys the HUB (`HubBuildButtons.tsx`, `sites/lib/hubActions.ts`: job kinds `build-hub`, `deploy-hub`, `build-deploy-hub`). - For the homepage, the same section only says, in prose, that the `homepage` package "deploys to its own project, `archilyzer`, with `archilyzer deploy homepage`". That is a CLI, with no job, no `/jobs` row and no `pnpm ops` verb. **Done in the meantime.** - 2026-09-26 17:18–17:19, from the primary checkout at `main` `555bc454`, with the release-10 runbook's own `~/reports/release-10/scripts/r10-home.sh`. Its guards: FINAL `4afa08d8` is an ancestor of HEAD; `data-accent=` is present in `homepage/out/index.html`. - `archilyzer build homepage` took 47 s. `archilyzer deploy homepage` took 38 s: 135 files uploaded, 41 already there, production branch `main`, deployment `https://857e0092.archilyzer.pages.dev`. - `https://archilyzer.pages.dev/` answers 200 with `data-accent="signal"`, and `/icons/icon.svg` carries the Found-line mark (`#151b20 #3f4c56 #e7edf1`). - The primary checkout's `git status` stayed clean. - **This is the homepage half of the release-10 runbook's step 5, done early.** Its logs are in `~/reports/release-10/tmp/r10-home-{build,deploy}.log`. - **The homepage's `/changelog` renders `export/CHANGELOG.md` as it stands**, so L1's three hub bullets are public there under `[Unreleased]` until the operator's re-cut and a redeploy. ## Verified anchors (primary checkout, `main` `555bc454`) - `common/publish/build.ts:938-1021`: - `composeHomepage` runs `pnpm run compose` in `homepage/`. - `buildHomepage` runs compose, then `pnpm exec next build`. `pnpm exec`, so `prebuild` (which runs `build:index`, a WRITER) does not run; the index is read as it stands. - `homepageDeployArgs` and `deployHomepage` take `previewBranch?`. They throw when `homepage/out/index.html` is missing, and otherwise deploy to the constant `HOMEPAGE_PAGES_PROJECT` with `--branch main` for production. - `common/bin/archilyzer.ts:117-125` (`build homepage`), `:160-172` (`deploy homepage [--preview]`). - The hub pattern to mirror: - `editor/app/sites/lib/hubActions.ts`: `runManagedFunction`; build on queue `build`; deploy and build-deploy on queue `deploy`; refusals before any job; build-deploy is one job and one log. - `editor/app/sites/components/HubBuildButtons.tsx`: one lane at a time, "Deploy after build" unticked. - `editor/app/api/ops/{build-hub,deploy-hub}/route.ts` + `_lib.ts`. - `scripts/archilyzer-ops.mjs:104-107` (the verb list) and `:302` (the help text), with their tests in `scripts/archilyzer-ops.test.mjs`. - `editor/e2e/ops-api.spec.ts:1094-` (deploy-hub refusals). - `editor/app/sites/page.tsx:30-45` (`BUILD_KINDS`: the Pool lists only its own kinds, and each section has its own console). ## Decisions 1. **Three job kinds, as for the hub:** - `build-homepage` on queue `build`. It writes `homepage/out`, not `export/out`, so it cannot race a site build for `export/`, but `next build` is heavy and the build queue serialises heavy builds. - `deploy-homepage` and `build-deploy-homepage` on queue `deploy`. - Bodies: `buildHomepage` and `deployHomepage` unchanged. The build-deploy runs `buildHomepage`, and deploys only on exit 0 (the runbook's rule: a failed build never reaches the deploy, because `homepage/out` still holds the previous build). 2. **Refusals before any job:** - a bad preview name (`previewBranchProblem`); - for a deploy-only, no build (`homepage/out/index.html` missing). - The job re-checks both. No project check: the project is the constant `archilyzer`. 3. **The surface: a "Homepage" section on `/sites`**, after Hub. Its prose moves there from the Hub paragraph. - Buttons "Build homepage" (with "Deploy after build", unticked) and "Deploy homepage", in a `JobLane` exactly like `HubBuildButtons`. - A preview-branch field, as the hub's ops verb has. - One line naming what a deploy ships: `homepage/out` built , to `archilyzer` (production), and the live URL (`PROJECT_URL`). - A note that the homepage reads the index as it stands, so run Build index first when the numbers should move. 4. **Ops:** - `pnpm ops build-homepage [--json '{"deploy":true}']` and `pnpm ops deploy-homepage [--preview ]`; - routes `/api/ops/build-homepage` and `/api/ops/deploy-homepage`, mirroring the hub's (`previewUrl` from `previewAliasUrl(HOMEPAGE_PAGES_PROJECT, branch)`). 5. **No brand check in the job.** The runbook's `data-accent=` grep is a release-10 rollout guard, not a property of every future build. ## Implementation 1. `editor/app/sites/lib/homepageDeployActions.ts`: `buildHomepageAction`, `deployHomepageAction`, `buildAndDeployHomepageAction` (decisions 1–2). A pure `builtHomepageProblem(outDir)` goes in `common/lib/builtExport.ts` beside `builtHubProblem`, with a unit test. 2. `editor/app/sites/components/HomepageBuildButtons.tsx` and the `/sites` section (decision 3). The hub paragraph loses its homepage sentence. 3. The two ops routes and the two verbs; `archilyzer-ops.test.mjs` for the verb list and the help text. 4. e2e: - `ops-api.spec.ts` + deploy-homepage refuses a missing build and a bad preview name. - `sites` spec(s): the section renders, and the Build homepage lane starts a `build-homepage` job. Check how the hub buttons' spec avoids a real `next build` / wrangler before choosing: the fake-binary env the editor suite uses, or a refusal-only assertion. - Never run a real deploy from e2e (see the release 7 lessons: e2e can start a real deploy). 5. Docs: the `/sites` prose; README/AGENTS only if they name `archilyzer deploy homepage` as the only way (grep). 6. Record + `[Unreleased]` bullet. ## Gates tsc; common; editor unit; `test:scripts`; mcp unchanged; `pnpm --filter editor exec next build`; e2e `ops-api.spec.ts` + the sites spec(s) touched + every spec that greps `deploy-hub|build-hub`. Numbers tool: none. ## Rollout The live :3001 editor must be restarted to show the section, which fits the next editor restart. After it: Build homepage with Deploy after build unticked, then Deploy homepage, and the job's log ends with the Pages URL. ## As shipped (2026-09-28, release 11 slice O4) Built as planned; record: [`release-11.md`](release-11.md), "Slice O4, as shipped". What the build corrected here: - **The ops preview is a body key, not a flag.** `pnpm ops deploy-homepage [--preview ]` (decision 4) is `pnpm ops deploy-homepage --json '{"preview":""}'`: the ops client has no per-verb flags, and `deploy-hub` takes its preview the same way. - **No job-kind registry entries.** The hub's three kinds have none in `common/jobs/jobKinds.ts`, and neither does any publish kind (`build-export`, `deploy-export`, `build-deploy`, `build-all`, `build-deploy-all`). A kind with no entry shows its raw name on `/jobs` and is not drainable, not replayable and needs no media, which is right for all three. Mirrored: the kinds are declared where the hub's are, in the action's `kind` and in `/sites`' `BUILD_KINDS` comment. - **`homepageOutDir(paths)` is exported from `common/publish/build.ts`.** The editor needs `homepage/out` for `builtHomepageProblem` and for "built ", and `homepageDir` was private. After review, `deployHomepage` takes its directory from `homepageOutDir` too, with no behaviour change. Otherwise `buildHomepage` and `deployHomepage` are unchanged, so the job's own refusal keeps its sentence ("… — run archilyzer build homepage first"). The refusal before the job says "… — build the homepage first". - **`JobLane` needed a fix to be testable at all.** Under `next dev`, Strict Mode's mount → cleanup → mount dropped every update of a lane's one launch, so it sat on "Starting…" while its job ran. Production mounts once and never showed it; no spec had clicked a lane button before. - **How the e2e avoids a real `next build` and wrangler.** There is no fake wrangler, and the hub's buttons have no spec. The Build homepage spec holds the `build` and `deploy` queues with `/api/test/stuck-job`, so its job only queues, and it cancels the job from the lane before it starts. The deploy-homepage refusals run with both queues held the same way (after review; each test held only one queue at first). The missing-build refusal is asked only when the checkout has no `homepage/out/index.html`, and asked as a preview.