build: migrate from npm to pnpm

Ten git worktrees each carried their own 225 MB node_modules (1.1 GB across
five) and paid 11s per `npm ci`. pnpm hardlinks from a shared store: the same
five worktrees cost ~250 MB total, and a fresh install is 4s.

What changed beyond the mechanical rename:

- `overrides` moved to `pnpm-workspace.yaml`. pnpm 11 does not read the `pnpm`
  field in package.json *or* npm's top-level `overrides`, and it fails silently
  — the vite/defu/language-server pins would have quietly stopped applying.
- Build scripts are blocked by default in pnpm; esbuild and sharp are allowed
  explicitly via `allowBuilds` (renamed from `onlyBuiltDependencies` in 11).
- `packageManager` + `engines` pin the toolchain.
- gate.sh rejects a package-lock.json/yarn.lock/bun.lock outright, so an agent
  running `npm install` out of habit fails loudly instead of building a second,
  divergent dependency tree.
- CI bootstraps pnpm with `npm install --global pnpm@11.25.0` rather than
  corepack (unbundled as of Node 25) or pnpm/action-setup (this self-hosted
  act-runner has never run a job; fetching a third-party action is not
  something to discover on the first one).

Two pre-existing CI bugs fixed while in the file:

- the gate installed with `npm install --package-lock=false`, which discarded
  the lockfile the previous session had just fixed.
- the visual-regression step imported `playwright`, which is not a dependency,
  and `visual-regression.mjs` has no compare mode anyway — in CI it overwrote
  its own baselines and passed unconditionally. Removed with a comment; it
  comes back when it can diff.

The `publish` job is now manual (`workflow_dispatch`). During the migration
dist/ holds three HTML files against the live pages branch's ten, so publishing
on every push to main would take the site down to a stub. Restore at task 20.

HANDOVER.md's incident log still says npm where it describes what happened at
the time; that is history, not a missed rename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Marcos Paulo
2026-09-05 04:29:42 +00:00
parent 63da0a4727
commit 48c31dc1b3
53 changed files with 5958 additions and 9642 deletions
+41 -39
View File
@@ -8,27 +8,28 @@ adding a chapter is a component and a content entry rather than a copy-pasted
file — **without changing how the site looks, what it says, or what it costs a
visitor to load.**
**Session state: see [`HANDOVER.md`](HANDOVER.md).** Phase 0 is done and
green on four branches; nothing is merged or pushed.
**Session state: see [`HANDOVER.md`](HANDOVER.md).** Phase 0 is done and green
on four branches; nothing is merged or pushed.
## Read before starting anything
| File | Why |
| --- | --- |
| [`../../AGENTS.md`](../../AGENTS.md) | entry point |
| [`../../.agents/context/design-system.md`](../../.agents/context/design-system.md) | three drifting palettes, a font that has never rendered |
| [`../../.agents/context/verification.md`](../../.agents/context/verification.md) | 42 assertions that will all break, and must not be deleted |
| [`../../.agents/context/publishing.md`](../../.agents/context/publishing.md) | Gitea Pages serves a branch and cannot build |
| File | Why |
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| [`../../AGENTS.md`](../../AGENTS.md) | entry point |
| [`../../.agents/context/design-system.md`](../../.agents/context/design-system.md) | three drifting palettes, a font that has never rendered |
| [`../../.agents/context/verification.md`](../../.agents/context/verification.md) | 42 assertions that will all break, and must not be deleted |
| [`../../.agents/context/publishing.md`](../../.agents/context/publishing.md) | Gitea Pages serves a branch and cannot build |
## The three things most likely to go wrong
1. **Content loss that nobody notices.** 50 KB of bilingual copy moves between
files. Snapshot every route *before* migrating it — task 03 exists to make
files. Snapshot every route _before_ migrating it — task 03 exists to make
that possible and blocks all page work.
2. **Assertions deleted to make a red suite green.** That converts a content-loss
bug into a passing build. `gate.sh` refuses a coverage drop.
2. **Assertions deleted to make a red suite green.** That converts a
content-loss bug into a passing build. `gate.sh` refuses a coverage drop.
3. **Base-path bugs.** The site lives at `/ai-for-dummies/`. It will work
perfectly in `npm run preview` and 404 in production. Verify on the real host.
perfectly in `pnpm run preview` and 404 in production. Verify on the real
host.
## Phases
@@ -40,28 +41,28 @@ Phase 3 pages 12 ∥ 13 ∥ 14 ∥ 17, then 15 ∥ 16
Phase 4 polish 18 ∥ 19, then 20
```
| # | Task | Agent | Depends on | Parallel with |
| --- | --- | --- | --- | --- |
| 01 | [scaffold + gates](task-01-scaffold.md) | astro-architect | — | — |
| 02 | [design tokens](task-02-tokens.md) | design-system-keeper | 01 | 03, 04 |
| 03 | [verification net](task-03-verification-net.md) | verification-engineer | 01 | 02, 04 |
| 04 | [content schema](task-04-content-schema.md) | content-i18n-migrator | 01 | 02, 03 |
| 05 | [guide content](task-05-content-guide.md) | content-i18n-migrator | 04 | 06 |
| 06 | [review-desk content](task-06-content-review.md) | content-i18n-migrator | 04 | 05 |
| 07 | [primitives](task-07-primitives.md) | component-builder | 02 | — |
| 08 | [route cards](task-08-route-cards.md) | component-builder | 07 | 09, 10, 11 |
| 09 | [chapter blocks](task-09-chapter-blocks.md) | component-builder | 07 | 08, 10, 11 |
| 10 | [guide blocks](task-10-guide-blocks.md) | component-builder | 07 | 08, 09, 11 |
| 11 | [review-desk blocks](task-11-review-blocks.md) | component-builder | 07 | 08, 09, 10 |
| 12 | [landing page](task-12-page-landing.md) | page-migrator | 03, 08 | 13, 14, 17 |
| 13 | [chapter pages ×4](task-13-page-chapters.md) | page-migrator | 03, 09 | 12, 14, 17 |
| 14 | [rules page](task-14-page-rules.md) | page-migrator | 03, 09 | 12, 13, 17 |
| 15 | [full guide](task-15-page-full-guide.md) | page-migrator | 05, 10, 13 | 16 |
| 16 | [review desk](task-16-page-review-desk.md) | page-migrator | 06, 11, 13 | 15 |
| 17 | [hands-on passthrough](task-17-hands-on.md) | astro-architect | 01 | 12, 13, 14 |
| 18 | [motion pass](task-18-motion.md) | motion-designer | 15, 16 | 19 |
| 19 | [contract re-point](task-19-verify-repoint.md) | verification-engineer | 15, 16 | 18 |
| 20 | [cutover + cleanup](task-20-cutover.md) | astro-architect | all | — |
| # | Task | Agent | Depends on | Parallel with |
| --- | ------------------------------------------------ | --------------------- | ---------- | ------------- |
| 01 | [scaffold + gates](task-01-scaffold.md) | astro-architect | — | — |
| 02 | [design tokens](task-02-tokens.md) | design-system-keeper | 01 | 03, 04 |
| 03 | [verification net](task-03-verification-net.md) | verification-engineer | 01 | 02, 04 |
| 04 | [content schema](task-04-content-schema.md) | content-i18n-migrator | 01 | 02, 03 |
| 05 | [guide content](task-05-content-guide.md) | content-i18n-migrator | 04 | 06 |
| 06 | [review-desk content](task-06-content-review.md) | content-i18n-migrator | 04 | 05 |
| 07 | [primitives](task-07-primitives.md) | component-builder | 02 | — |
| 08 | [route cards](task-08-route-cards.md) | component-builder | 07 | 09, 10, 11 |
| 09 | [chapter blocks](task-09-chapter-blocks.md) | component-builder | 07 | 08, 10, 11 |
| 10 | [guide blocks](task-10-guide-blocks.md) | component-builder | 07 | 08, 09, 11 |
| 11 | [review-desk blocks](task-11-review-blocks.md) | component-builder | 07 | 08, 09, 10 |
| 12 | [landing page](task-12-page-landing.md) | page-migrator | 03, 08 | 13, 14, 17 |
| 13 | [chapter pages ×4](task-13-page-chapters.md) | page-migrator | 03, 09 | 12, 14, 17 |
| 14 | [rules page](task-14-page-rules.md) | page-migrator | 03, 09 | 12, 13, 17 |
| 15 | [full guide](task-15-page-full-guide.md) | page-migrator | 05, 10, 13 | 16 |
| 16 | [review desk](task-16-page-review-desk.md) | page-migrator | 06, 11, 13 | 15 |
| 17 | [hands-on passthrough](task-17-hands-on.md) | astro-architect | 01 | 12, 13, 14 |
| 18 | [motion pass](task-18-motion.md) | motion-designer | 15, 16 | 19 |
| 19 | [contract re-point](task-19-verify-repoint.md) | verification-engineer | 15, 16 | 18 |
| 20 | [cutover + cleanup](task-20-cutover.md) | astro-architect | all | — |
Widest parallelism: **four agents** (tasks 0811, then 12/13/14/17). More than
that and they start contending on review capacity, not on files.
@@ -75,14 +76,14 @@ cd ../af-task-08
# .agents/agents/component-builder.md (+ the skills it names)
```
The script runs `npm ci` and `verify-hooks.sh` for you. That matters: `.husky/_`
is generated, not committed, so a hand-made worktree has hooks configured but
**silently not running**.
The script runs `pnpm install --frozen-lockfile` and `verify-hooks.sh` for you.
That matters: `.husky/_` is generated, not committed, so a hand-made worktree
has hooks configured but **silently not running**.
Finishing:
```bash
npm run gate # tier 2, same as pre-push
pnpm run gate # tier 2, same as pre-push
# reviewer agent reads the diff against .agents/checklists/before-merge.md
.agents/scripts/worktree.sh finish 08 route-cards
```
@@ -94,4 +95,5 @@ See [`MODEL-ROUTING.md`](MODEL-ROUTING.md).
## These files must be committed
Worktrees check out tracked files. If this plan stays untracked, every worktree
you create will be missing it. Commit `plans/` and `.agents/` before fanning out.
you create will be missing it. Commit `plans/` and `.agents/` before fanning
out.