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
+10 -8
View File
@@ -21,11 +21,11 @@ reality won — update the rule and say so.
## Specialists ## Specialists
Each agent has one responsibility, one set of rules, and its own worktree. Each agent has one responsibility, one set of rules, and its own worktree. Full
Full definitions in [`agents/`](agents/). definitions in [`agents/`](agents/).
| Agent | Owns | Loads skills | | Agent | Owns | Loads skills |
| --- | --- | --- | | ---------------------------------------------------------- | --------------------------------------------------- | -------------------------------------- |
| [`astro-architect`](agents/astro-architect.md) | project scaffold, config, routing, layouts | `astro-page` | | [`astro-architect`](agents/astro-architect.md) | project scaffold, config, routing, layouts | `astro-page` |
| [`design-system-keeper`](agents/design-system-keeper.md) | tokens, the single palette, type scale | `design-tokens` | | [`design-system-keeper`](agents/design-system-keeper.md) | tokens, the single palette, type scale | `design-tokens` |
| [`component-builder`](agents/component-builder.md) | one component per task, from templates | `astro-component`, `design-tokens` | | [`component-builder`](agents/component-builder.md) | one component per task, from templates | `astro-component`, `design-tokens` |
@@ -37,12 +37,14 @@ Full definitions in [`agents/`](agents/).
## Working agreement ## Working agreement
1. **One task, one worktree, one agent.** See [`rules/git-worktrees.md`](rules/git-worktrees.md). 1. **One task, one worktree, one agent.** See
2. **Read `context/` first.** Especially `design-system.md` and `verification.md`. [`rules/git-worktrees.md`](rules/git-worktrees.md).
Most wrong answers here come from assuming the CSS is already coherent. 2. **Read `context/` first.** Especially `design-system.md` and
`verification.md`. Most wrong answers here come from assuming the CSS is
already coherent.
3. **Templates over invention.** `templates/components/` and `templates/pages/` 3. **Templates over invention.** `templates/components/` and `templates/pages/`
exist so ten parallel agents produce one house style, not ten. exist so ten parallel agents produce one house style, not ten.
4. **The gate is `npm run verify` plus the relevant checklist.** Green tests 4. **The gate is `pnpm run verify` plus the relevant checklist.** Green tests
with deleted assertions is a failed task. with deleted assertions is a failed task.
5. **Report what you did not do.** Partial work with an honest boundary is 5. **Report what you did not do.** Partial work with an honest boundary is
useful; silent narrowing is not. useful; silent narrowing is not.
@@ -58,7 +60,7 @@ agent loads .agents/agents/<role>.md + its skills
checklists/before-*.md ← self-gate checklists/before-*.md ← self-gate
npm run verify ← hard gate pnpm run verify ← hard gate
reviewer agent on the diff ← merge gate reviewer agent on the diff ← merge gate
``` ```
+15 -9
View File
@@ -1,14 +1,18 @@
--- ---
name: astro-architect name: astro-architect
description: Owns the Astro scaffold — config, routing, layouts, build pipeline, and the publishing decision. Use for task 01 and any later change to astro.config.mjs, package.json, or the deploy path. Do not use for component or page work. description:
Owns the Astro scaffold — config, routing, layouts, build pipeline, and the
publishing decision. Use for task 01 and any later change to astro.config.mjs,
package.json, or the deploy path. Do not use for component or page work.
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
--- ---
You own the foundation. Everything other agents build sits on your decisions, You own the foundation. Everything other agents build sits on your decisions, so
so wrong choices here are expensive and late-discovered. wrong choices here are expensive and late-discovered.
**Read first**: `.agents/context/architecture.md`, `.agents/context/publishing.md`, **Read first**: `.agents/context/architecture.md`,
`.agents/rules/astro.md`. **Load skill**: `astro-page`. `.agents/context/publishing.md`, `.agents/rules/astro.md`. **Load skill**:
`astro-page`.
## You own ## You own
@@ -19,11 +23,13 @@ writer of these. Other agents report problems with them; they do not edit.
## Non-negotiable outcomes ## Non-negotiable outcomes
- `base: '/ai-for-dummies'` set and **verified against the real host**, not just - `base: '/ai-for-dummies'` set and **verified against the real host**, not just
`npm run preview`. Base-path bugs are the most likely production-only failure. `pnpm run preview`. Base-path bugs are the most likely production-only
failure.
- Every existing URL resolves identically, trailing slash included. - Every existing URL resolves identically, trailing slash included.
- Zero JS by default. Astro ships none unless a component asks. - Zero JS by default. Astro ships none unless a component asks.
- `hands-on/starter/` and `hands-on/rules/` copied to `public/` **verbatim, - `hands-on/starter/` and `hands-on/rules/` copied to `public/` **verbatim,
unprocessed**. They are lab fixtures; the exercise is that they are plain files. unprocessed**. They are lab fixtures; the exercise is that they are plain
files.
- No UI framework, no CSS framework, no runtime dependencies. - No UI framework, no CSS framework, no runtime dependencies.
## The publishing decision is yours to make and document ## The publishing decision is yours to make and document
@@ -39,6 +45,6 @@ pod restart silently kills CI. Put that in the runbook.
## Done when ## Done when
`npm run build` succeeds, one migrated page serves correctly from the real host `pnpm run build` succeeds, one migrated page serves correctly from the real host
under `/ai-for-dummies/`, `npm run verify` and `node scripts/audit-ui.mjs` are under `/ai-for-dummies/`, `pnpm run verify` and `node scripts/audit-ui.mjs` are
green, and the operations guide matches reality. green, and the operations guide matches reality.
+5 -2
View File
@@ -1,6 +1,9 @@
--- ---
name: component-builder name: component-builder
description: Builds one Astro component per task from the project templates. Use for the component-extraction tasks (05-09). Do not use for page migration, token changes, or verify.mjs. description:
Builds one Astro component per task from the project templates. Use for the
component-extraction tasks (05-09). Do not use for page migration, token
changes, or verify.mjs.
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
--- ---
@@ -34,4 +37,4 @@ You may not edit `tokens.css`, `verify.mjs`, `astro.config.mjs`, or
`.agents/checklists/before-component.md` is fully checked, rendered text diffs `.agents/checklists/before-component.md` is fully checked, rendered text diffs
clean against the markup you replaced, screenshots at four widths look clean against the markup you replaced, screenshots at four widths look
unchanged, and `npm run verify` is green **with no assertion deleted**. unchanged, and `pnpm run verify` is green **with no assertion deleted**.
+17 -12
View File
@@ -1,19 +1,23 @@
--- ---
name: content-i18n-migrator name: content-i18n-migrator
description: Moves bilingual copy out of app.js and catalog.js into typed Astro content collections without altering a string. Use for tasks 03-04 and any later content relocation. Do not use for markup or styling. description:
Moves bilingual copy out of app.js and catalog.js into typed Astro content
collections without altering a string. Use for tasks 03-04 and any later
content relocation. Do not use for markup or styling.
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
--- ---
You own `src/content/` and `src/content/config.ts`, and you are their only You own `src/content/` and `src/content/config.ts`, and you are their only
writer. Your job is a lossless move, not an edit. writer. Your job is a lossless move, not an edit.
**Read first**: `.agents/rules/content-i18n.md`. **Load skill**: `content-migration`. **Read first**: `.agents/rules/content-i18n.md`. **Load skill**:
`content-migration`.
## What you are moving ## What you are moving
~50 `{ en, pt }` keys from `app.js` (`phases`, `handsOnPrompts`, `modelGuide`, ~50 `{ en, pt }` keys from `app.js` (`phases`, `handsOnPrompts`, `modelGuide`,
`skillSources`, `skillInstallPrompts`), plus 24 review entries from `skillSources`, `skillInstallPrompts`), plus 24 review entries from `catalog.js`
`catalog.js` and `submitted-catalog.js`. and `submitted-catalog.js`.
These are hand-written translations with deliberate tone. **Copy them These are hand-written translations with deliberate tone. **Copy them
mechanically. Never retype.** Retyping introduces drift nobody notices until a mechanically. Never retype.** Retyping introduces drift nobody notices until a
@@ -21,9 +25,9 @@ Portuguese speaker does.
## Procedure ## Procedure
Extract → write into collection → diff extracted-before against Extract → write into collection → diff extracted-before against extracted-after
extracted-after → only then delete the source. If the diff is not empty, you → only then delete the source. If the diff is not empty, you changed content.
changed content. Fix it before continuing. Fix it before continuing.
Both locales required in the schema. A missing `pt` must be a **build error**, Both locales required in the schema. A missing `pt` must be a **build error**,
never a silent English fallback — that is how bilingual sites quietly become never a silent English fallback — that is how bilingual sites quietly become
@@ -35,11 +39,12 @@ monolingual.
`scripts/build-skill-review.mjs` and the output is **committed**. Move `scripts/build-skill-review.mjs` and the output is **committed**. Move
`catalog.js` and the generator keeps running against nothing — silently. Either `catalog.js` and the generator keeps running against nothing — silently. Either
re-point it or replace it, and update `package.json`, `README.md`, re-point it or replace it, and update `package.json`, `README.md`,
`docs/operations-guide.md`, and the review desk footer, all of which reference it. `docs/operations-guide.md`, and the review desk footer, all of which reference
it.
Also: the review desk's diff view compares original and improved **source text**. Also: the review desk's diff view compares original and improved **source
If you convert `improved` to rendered Markdown, keep the raw string available or text**. If you convert `improved` to rendered Markdown, keep the raw string
the diff view breaks. available or the diff view breaks.
## The language-switching decision ## The language-switching decision
@@ -50,4 +55,4 @@ a decision, record it. Either way `<html lang>` tracks the active language.
## Done when ## Done when
The string diff is empty, both locales validate, the generator still produces The string diff is empty, both locales validate, the generator still produces
identical output, and `npm run verify` is green. identical output, and `pnpm run verify` is green.
+8 -5
View File
@@ -1,6 +1,9 @@
--- ---
name: design-system-keeper name: design-system-keeper
description: Owns src/styles/tokens.css — the palette, type scale, and breakpoints. Use for task 02 and any later token change or check-tokens failure. Do not use for building components. description:
Owns src/styles/tokens.css — the palette, type scale, and breakpoints. Use for
task 02 and any later token change or check-tokens failure. Do not use for
building components.
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
--- ---
@@ -18,7 +21,7 @@ palettes and a broken `@font-face`. **Load skills**: `design-tokens`,
sub-perceptual and can be canonicalized. `--blue` (`#527f9f` vs `#215675`) is sub-perceptual and can be canonicalized. `--blue` (`#527f9f` vs `#215675`) is
visibly different — screenshot both and get a human decision. visibly different — screenshot both and get a human decision.
2. **The fonts have never rendered.** The `@font-face` in `styles.css:1` points 2. **The fonts have never rendered.** The `@font-face` in `styles.css:1` points
`src:` at a Google Fonts *stylesheet*, so Manrope and DM Mono have always `src:` at a Google Fonts _stylesheet_, so Manrope and DM Mono have always
fallen back to Arial and generic monospace. Self-hosting them is a redesign, fallen back to Arial and generic monospace. Self-hosting them is a redesign,
not a refactor. Default: delete the dead rule, declare the stacks that not a refactor. Default: delete the dead rule, declare the stacks that
actually render. Escalate if someone wants the real fonts. actually render. Escalate if someone wants the real fonts.
@@ -28,9 +31,9 @@ palettes and a broken `@font-face`. **Load skills**: `design-tokens`,
`src/styles/tokens.css`, `src/styles/base.css`, and `src/styles/tokens.css`, `src/styles/base.css`, and
`.agents/scripts/check-tokens.mjs`. `.agents/scripts/check-tokens.mjs`.
Deliver: one value per token, a named type scale (`--step-*`) replacing 14 ad-hoc Deliver: one value per token, a named type scale (`--step-*`) replacing 14
`clamp()` triples, five named breakpoints replacing sixteen, and an enforcement ad-hoc `clamp()` triples, five named breakpoints replacing sixteen, and an
script wired into `npm run verify`. enforcement script wired into `pnpm run verify`.
## Preserve the house style ## Preserve the house style
+8 -4
View File
@@ -1,13 +1,17 @@
--- ---
name: page-migrator name: page-migrator
description: Migrates one hand-written HTML page to an Astro route with identical URL, content, and JS budget. Use for the page-migration tasks (10-16). Do not use for component extraction or config changes. description:
Migrates one hand-written HTML page to an Astro route with identical URL,
content, and JS budget. Use for the page-migration tasks (10-16). Do not use
for component extraction or config changes.
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
--- ---
You migrate **one page per task**. The bar is that a visitor cannot tell. You migrate **one page per task**. The bar is that a visitor cannot tell.
**Read first**: `.agents/context/architecture.md`, `.agents/context/verification.md`. **Read first**: `.agents/context/architecture.md`,
**Load skills**: `astro-page`, `content-migration`, `visual-regression`. `.agents/context/verification.md`. **Load skills**: `astro-page`,
`content-migration`, `visual-regression`.
## Snapshot before you touch anything ## Snapshot before you touch anything
@@ -38,5 +42,5 @@ them, it is wrong — stop and report.
## Done when ## Done when
`.agents/checklists/before-page.md` complete, snapshot diff empty (or every line `.agents/checklists/before-page.md` complete, snapshot diff empty (or every line
justified), `node scripts/audit-ui.mjs` and `npm run verify` green, screenshots justified), `node scripts/audit-ui.mjs` and `pnpm run verify` green, screenshots
compared, and your task report lists what you deliberately left alone. compared, and your task report lists what you deliberately left alone.
+7 -4
View File
@@ -1,6 +1,9 @@
--- ---
name: verification-engineer name: verification-engineer
description: Keeps scripts/verify.mjs meaningful across the migration and builds the snapshot/visual-regression net. Use for tasks 18-19 and whenever a verify assertion needs re-pointing. The only role permitted to reduce coverage. description:
Keeps scripts/verify.mjs meaningful across the migration and builds the
snapshot/visual-regression net. Use for tasks 18-19 and whenever a verify
assertion needs re-pointing. The only role permitted to reduce coverage.
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
--- ---
@@ -45,6 +48,6 @@ this "dependency-free" site. Add `@import`, `src: url(https:…)`, and
## Done when ## Done when
Coverage has not fallen, every removal has a reason, snapshots exist for all Coverage has not fallen, every removal has a reason, snapshots exist for all ten
ten routes, `check-tokens.mjs` and the extended audit are wired into routes, `check-tokens.mjs` and the extended audit are wired into
`npm run verify`, and the suite runs green on the migrated site. `pnpm run verify`, and the suite runs green on the migrated site.
+7 -4
View File
@@ -1,6 +1,7 @@
# Checklist: before you call a component done # Checklist: before you call a component done
- [ ] It appears (or will appear) in **three** places, or has a name a person says out loud - [ ] It appears (or will appear) in **three** places, or has a name a person
says out loud
- [ ] Lives in the right folder: `primitives/`, `blocks/`, or `islands/` - [ ] Lives in the right folder: `primitives/`, `blocks/`, or `islands/`
- [ ] Typed `interface Props`; every field intentional; no `any` - [ ] Typed `interface Props`; every field intentional; no `any`
- [ ] **No raw hex, px font sizes, or ad-hoc breakpoints** — tokens only - [ ] **No raw hex, px font sizes, or ad-hoc breakpoints** — tokens only
@@ -8,8 +9,10 @@
- [ ] Markup under ~120 lines - [ ] Markup under ~120 lines
- [ ] Native elements: `<button>` for actions, `<a>` for navigation - [ ] Native elements: `<button>` for actions, `<a>` for navigation
- [ ] Keyboard: tab to it, operate with Enter/Space, focus ring visible - [ ] Keyboard: tab to it, operate with Enter/Space, focus ring visible
- [ ] Interactive state exposed via ARIA (`aria-pressed`, `aria-current`), not just colour - [ ] Interactive state exposed via ARIA (`aria-pressed`, `aria-current`), not
- [ ] Any motion respects `prefers-reduced-motion` and animates only `transform`/`opacity` just colour
- [ ] Any motion respects `prefers-reduced-motion` and animates only
`transform`/`opacity`
- [ ] Renders correctly at 560 / 800 / 1100 / 1600 px - [ ] Renders correctly at 560 / 800 / 1100 / 1600 px
- [ ] Both `en` and `pt` strings present; no hard-coded copy - [ ] Both `en` and `pt` strings present; no hard-coded copy
- [ ] `npm run verify` green with no assertion deleted - [ ] `pnpm run verify` green with no assertion deleted
+5 -3
View File
@@ -1,9 +1,11 @@
# Checklist: before you merge a task branch # Checklist: before you merge a task branch
- [ ] Rebased on current `origin/main`, conflicts resolved in the worktree - [ ] Rebased on current `origin/main`, conflicts resolved in the worktree
- [ ] `npm run verify` and `node scripts/audit-ui.mjs` both green - [ ] `pnpm run verify` and `node scripts/audit-ui.mjs` both green
- [ ] **Assertion count in `verify.mjs` did not fall** (`grep -c 'throw new Error' scripts/verify.mjs`) - [ ] **Assertion count in `verify.mjs` did not fall**
- [ ] Only files in your task's scope changed — `git diff --stat origin/main` matches the brief (`grep -c 'throw new Error' scripts/verify.mjs`)
- [ ] Only files in your task's scope changed — `git diff --stat origin/main`
matches the brief
- [ ] No contended file edited unless you own it (see `rules/git-worktrees.md`) - [ ] No contended file edited unless you own it (see `rules/git-worktrees.md`)
- [ ] No `.serena/`, `__pycache__/`, `dist/`, or scratch files staged - [ ] No `.serena/`, `__pycache__/`, `dist/`, or scratch files staged
- [ ] Commit message: what, why, and what you deliberately did not do - [ ] Commit message: what, why, and what you deliberately did not do
+13 -7
View File
@@ -1,14 +1,20 @@
# Checklist: before you call a page migrated # Checklist: before you call a page migrated
- [ ] URL identical to the old one, **trailing slash included** - [ ] URL identical to the old one, **trailing slash included**
- [ ] Any query params the old page honoured still work (`?author=`, `?skill=`, `?view=`, `?file=`, `?compare=`, `?render=`) - [ ] Any query params the old page honoured still work (`?author=`, `?skill=`,
- [ ] Rendered text content diffs clean against the pre-migration snapshot — or every difference is listed and justified `?view=`, `?file=`, `?compare=`, `?render=`)
- [ ] Same number of JS bytes or fewer; a previously-zero-JS page still ships zero - [ ] Rendered text content diffs clean against the pre-migration snapshot — or
- [ ] `<title>`, `<meta name="description">`, and `<meta name="viewport">` preserved every difference is listed and justified
- [ ] Same number of JS bytes or fewer; a previously-zero-JS page still ships
zero
- [ ] `<title>`, `<meta name="description">`, and `<meta name="viewport">`
preserved
- [ ] One `<h1>`; heading levels do not skip - [ ] One `<h1>`; heading levels do not skip
- [ ] Every ARIA attribute from the original survived (`verify.mjs` asserts several) - [ ] Every ARIA attribute from the original survived (`verify.mjs` asserts
- [ ] No external `<script>`, `<link>`, `@import`, or `url()``node scripts/audit-ui.mjs` green several)
- [ ] No external `<script>`, `<link>`, `@import`, or `url()`
`node scripts/audit-ui.mjs` green
- [ ] Internal links go through `BASE_URL`, not hand-written absolute paths - [ ] Internal links go through `BASE_URL`, not hand-written absolute paths
- [ ] Screenshots at 560 / 800 / 1100 / 1600 px compared against the old page - [ ] Screenshots at 560 / 800 / 1100 / 1600 px compared against the old page
- [ ] `<html lang>` correct and still switching with the language toggle - [ ] `<html lang>` correct and still switching with the language toggle
- [ ] `npm run verify` green with no assertion deleted - [ ] `pnpm run verify` green with no assertion deleted
+6 -5
View File
@@ -9,7 +9,8 @@ because the Pages Server publishes a branch, not a directory.
Live at `https://netcracker.pages.marcospaulo.dev.br/ai-for-dummies/`. Live at `https://netcracker.pages.marcospaulo.dev.br/ai-for-dummies/`.
Full procedure, including the fact that `merge --ff-only main` fails (the Full procedure, including the fact that `merge --ff-only main` fails (the
histories diverged), is in [`../../docs/operations-guide.md`](../../docs/operations-guide.md). histories diverged), is in
[`../../docs/operations-guide.md`](../../docs/operations-guide.md).
## What Astro changes ## What Astro changes
@@ -17,9 +18,9 @@ Astro emits `dist/`. The Pages Server cannot run a build, so **something has to
put built output on `pages`**. Pick one, deliberately, in task 01: put built output on `pages`**. Pick one, deliberately, in task 01:
| Option | How | Cost | | Option | How | Cost |
| --- | --- | --- | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| **A. Build locally, commit `dist/` to `pages`** | `npm run build`, copy `dist/*` into the `pages` worktree, commit | `pages` stops being "the exact source". Diffs become unreadable. Publishing depends on one workstation. Simple, no new infra. | | **A. Build locally, commit `dist/` to `pages`** | `pnpm run build`, copy `dist/*` into the `pages` worktree, commit | `pages` stops being "the exact source". Diffs become unreadable. Publishing depends on one workstation. Simple, no new infra. |
| **B. Gitea Actions builds and pushes `pages`** | workflow on `main``npm ci && npm run build` → force-push `dist/` to `pages` | `pages` becomes a machine-owned branch (force-push is fine *because* nothing else writes it). Needs the act-runner to be healthy. **Recommended.** | | **B. Gitea Actions builds and pushes `pages`** | workflow on `main``pnpm install --frozen-lockfile && pnpm run build` → force-push `dist/` to `pages` | `pages` becomes a machine-owned branch (force-push is fine _because_ nothing else writes it). Needs the act-runner to be healthy. **Recommended.** |
| **C. Serve from a container instead** | drop Pages Server for an nginx pod behind the existing ingress | most control, most infra, changes the URL story | | **C. Serve from a container instead** | drop Pages Server for an nginx pod behind the existing ingress | most control, most infra, changes the URL story |
**Recommended: B**, with A as the documented manual fallback for when the runner **Recommended: B**, with A as the documented manual fallback for when the runner
@@ -39,7 +40,7 @@ through `import.meta.env.BASE_URL` or Astro's `<a href={...}>` helpers rather
than a hand-written absolute `/models/`. than a hand-written absolute `/models/`.
This is the single most likely source of "works locally, 404s in production" in This is the single most likely source of "works locally, 404s in production" in
this migration. Verify it on the real host, not just `npm run preview`. this migration. Verify it on the real host, not just `pnpm run preview`.
## Verification before you call it published ## Verification before you call it published
+5 -5
View File
@@ -1,8 +1,8 @@
# Context: the verification contract # Context: the verification contract
`scripts/verify.mjs` is 13 KB, 42 `throw new Error` sites, 16 checkpoints. It `scripts/verify.mjs` is 13 KB, 42 `throw new Error` sites, 16 checkpoints. It
reads 26 source files and asserts that specific **string tokens** appear in reads 26 source files and asserts that specific **string tokens** appear in them
them `data-phase="plan"`, `renderTree`, `.change-lens`, `data-phase="plan"`, `renderTree`, `.change-lens`,
`styles.css?v=20260904-vote-widget`, and so on. `styles.css?v=20260904-vote-widget`, and so on.
## Why this matters more than it looks ## Why this matters more than it looks
@@ -11,7 +11,7 @@ These assertions are the only thing standing between this site and silent
content loss during a large refactor. They are also **all going to break**, content loss during a large refactor. They are also **all going to break**,
because they assert against files that will stop existing. because they assert against files that will stop existing.
The failure mode to guard against: an agent runs `npm run verify`, sees red, The failure mode to guard against: an agent runs `pnpm run verify`, sees red,
and "fixes" it by deleting the assertion. The suite goes green and the site and "fixes" it by deleting the assertion. The suite goes green and the site
loses a section. **Deleting an assertion is a change that requires review, the loses a section. **Deleting an assertion is a change that requires review, the
same as deleting a feature.** same as deleting a feature.**
@@ -21,9 +21,9 @@ same as deleting a feature.**
Three kinds of assertion, three different fates: Three kinds of assertion, three different fates:
| Kind | Example | Fate | | Kind | Example | Fate |
| --- | --- | --- | | ------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| **Content presence** | `'data-phase="plan"'` in `full-guide/index.html` | Re-point at built output (`dist/`) — the token should survive rendering. If it does not, the component dropped content. | | **Content presence** | `'data-phase="plan"'` in `full-guide/index.html` | Re-point at built output (`dist/`) — the token should survive rendering. If it does not, the component dropped content. |
| **Implementation detail** | `'const phases'`, `'renderTree'` in `app.js` | Obsolete. Replace with an assertion about *behaviour or output*, never delete outright. | | **Implementation detail** | `'const phases'`, `'renderTree'` in `app.js` | Obsolete. Replace with an assertion about _behaviour or output_, never delete outright. |
| **Cache-busting version** | `'app.js?v=20260904-vote-widget'` | Obsolete — Astro hashes assets. Replace with "the built HTML references a hashed asset". | | **Cache-busting version** | `'app.js?v=20260904-vote-widget'` | Obsolete — Astro hashes assets. Replace with "the built HTML references a hashed asset". |
**Rule: the assertion count must not fall.** Every removed token is replaced by **Rule: the assertion count must not fall.** Every removed token is replaced by
+10 -8
View File
@@ -5,14 +5,14 @@ worktrees stay fast** — the whole point is that a gate you are tempted to skip
is not a gate. is not a gate.
| Tier | Hook | Scope | Budget | Runs | | Tier | Hook | Scope | Budget | Runs |
| --- | --- | --- | --- | --- | | ---- | ------------------ | ------------------------------------- | ------- | -------------------- |
| 1 | `pre-commit` | **staged files only** (lint-staged) | < 3s | every commit | | 1 | `pre-commit` | **staged files only** (lint-staged) | < 3s | every commit |
| 2 | `pre-push` | whole project: build + verify + audit | < 90s | every push | | 2 | `pre-push` | whole project: build + verify + audit | < 90s | every push |
| 3 | CI (Gitea Actions) | tier 2 + visual regression | minutes | every push to `main` | | 3 | CI (Gitea Actions) | tier 2 + visual regression | minutes | every push to `main` |
Tier 1 must stay under a few seconds. If it creeps, move the check to tier 2. Tier 1 must stay under a few seconds. If it creeps, move the check to tier 2. An
An agent that waits 40s per commit will start passing `--no-verify`, and then agent that waits 40s per commit will start passing `--no-verify`, and then you
you have no gate at all. have no gate at all.
## Tier 1 — pre-commit (lint-staged) ## Tier 1 — pre-commit (lint-staged)
@@ -21,7 +21,8 @@ parallel worktrees do ten small jobs, not ten full-project sweeps.
- Prettier + ESLint on `*.{js,mjs,ts,astro}` - Prettier + ESLint on `*.{js,mjs,ts,astro}`
- Prettier + Stylelint on `*.css` - Prettier + Stylelint on `*.css`
- `check-tokens.mjs` on changed `.astro`/`.css` — catches raw hex before it lands - `check-tokens.mjs` on changed `.astro`/`.css` — catches raw hex before it
lands
- Prettier on `*.{json,md}` - Prettier on `*.{json,md}`
## Tier 2 — pre-push ## Tier 2 — pre-push
@@ -56,13 +57,14 @@ If a gate is wrong, fix the gate in its own commit. Do not route around it.
- The heavy tier-2 gate takes a **lock** (`.git/af-gate.lock`, shared across - The heavy tier-2 gate takes a **lock** (`.git/af-gate.lock`, shared across
worktrees) so ten agents pushing at once do not run ten concurrent builds and worktrees) so ten agents pushing at once do not run ten concurrent builds and
thrash the machine. Waiters queue; they do not fail. thrash the machine. Waiters queue; they do not fail.
- `npm ci` in a fresh worktree should use `--prefer-offline` to avoid registry - `pnpm install --frozen-lockfile` in a fresh worktree is cheap: pnpm hardlinks
contention when several spin up at once. from the shared content-addressable store, so a second worktree costs seconds
and almost no disk instead of another 225 MB. No `--prefer-offline` needed.
## The silent-failure mode you must know about ## The silent-failure mode you must know about
Husky sets `core.hooksPath` to `.husky/_`, and **`.husky/_` is generated by Husky sets `core.hooksPath` to `.husky/_`, and **`.husky/_` is generated by
`npm install`, not committed**. A fresh `git worktree add` therefore has hooks `pnpm install`, not committed**. A fresh `git worktree add` therefore has hooks
configured but the directory missing — so **hooks silently do not run**. Every configured but the directory missing — so **hooks silently do not run**. Every
commit passes. Nothing is checked. commit passes. Nothing is checked.
+9 -7
View File
@@ -8,7 +8,7 @@ This project teaches worktrees. It should use them properly.
# from the main checkout # from the main checkout
git worktree add ../af-task-07 -b refactor/task-07-route-cards git worktree add ../af-task-07 -b refactor/task-07-route-cards
cd ../af-task-07 cd ../af-task-07
npm ci pnpm install --frozen-lockfile
``` ```
Naming: directory `../af-task-NN`, branch `refactor/task-NN-<slug>`. Both Naming: directory `../af-task-NN`, branch `refactor/task-NN-<slug>`. Both
@@ -18,8 +18,8 @@ derived from the task file so the mapping is never ambiguous.
The migration runs many agents in parallel over the same small set of files The migration runs many agents in parallel over the same small set of files
(`tokens.css`, `verify.mjs`, `astro.config.mjs` are contended). Worktrees give (`tokens.css`, `verify.mjs`, `astro.config.mjs` are contended). Worktrees give
each agent its own working directory over one object store — cheap, and no each agent its own working directory over one object store — cheap, and no agent
agent can see another's half-finished state. can see another's half-finished state.
The failure mode without them: two agents both "fix" `verify.mjs`, and the The failure mode without them: two agents both "fix" `verify.mjs`, and the
second overwrites the first's assertions. second overwrites the first's assertions.
@@ -30,7 +30,7 @@ These are touched by many tasks. Whoever owns them per the plan is the **only**
writer; everyone else opens an issue in their task report instead of editing: writer; everyone else opens an issue in their task report instead of editing:
| File | Owner | | File | Owner |
| --- | --- | | ---------------------------------- | ----------------------- |
| `src/styles/tokens.css` | `design-system-keeper` | | `src/styles/tokens.css` | `design-system-keeper` |
| `scripts/verify.mjs` | `verification-engineer` | | `scripts/verify.mjs` | `verification-engineer` |
| `astro.config.mjs`, `package.json` | `astro-architect` | | `astro.config.mjs`, `package.json` | `astro-architect` |
@@ -44,7 +44,7 @@ writer; everyone else opens an issue in their task report instead of editing:
## Before you finish ## Before you finish
1. `npm run verify` green — without deleting assertions. 1. `pnpm run verify` green — without deleting assertions.
2. The relevant checklist in [`../checklists/`](../checklists/) complete. 2. The relevant checklist in [`../checklists/`](../checklists/) complete.
3. `git rebase origin/main` again, resolve conflicts in your worktree. 3. `git rebase origin/main` again, resolve conflicts in your worktree.
4. Task report: what changed, what you verified, **what you did not do**. 4. Task report: what changed, what you verified, **what you did not do**.
@@ -64,6 +64,8 @@ should be short.
- Never work directly on `main`. - Never work directly on `main`.
- Never force-push a shared branch. The `pages` branch is the sole exception, - Never force-push a shared branch. The `pages` branch is the sole exception,
and only if CI owns it (see [`../context/publishing.md`](../context/publishing.md)). and only if CI owns it (see
[`../context/publishing.md`](../context/publishing.md)).
- Never `git add -A` from the repository root. This repo has untracked local - Never `git add -A` from the repository root. This repo has untracked local
scratch (`.serena/`, `scripts/inspect.py`) that must not be swept into a commit. scratch (`.serena/`, `scripts/inspect.py`) that must not be swept into a
commit.
+11 -8
View File
@@ -15,7 +15,7 @@ the token layer is missing something (add it there, not inline).
```css ```css
/* forbidden */ /* forbidden */
color: #172f42; color: #172f42;
color: rgba(23,47,66,.6); color: rgba(23, 47, 66, 0.6);
/* required */ /* required */
color: var(--ink); color: var(--ink);
@@ -23,7 +23,7 @@ color: var(--muted);
``` ```
No raw hex outside `tokens.css`. `.agents/scripts/check-tokens.mjs` enforces it; No raw hex outside `tokens.css`. `.agents/scripts/check-tokens.mjs` enforces it;
wire it into `npm run verify`. wire it into `pnpm run verify`.
## Semantic names, not literal ones ## Semantic names, not literal ones
@@ -31,8 +31,8 @@ wire it into `npm run verify`.
`--deep` are the existing vocabulary. Keep it — it is already semantic and the `--deep` are the existing vocabulary. Keep it — it is already semantic and the
team reads it fluently. Do not rename to `--color-neutral-900`. team reads it fluently. Do not rename to `--color-neutral-900`.
If a genuine second surface is needed, extend semantically If a genuine second surface is needed, extend semantically (`--surface-lab`,
(`--surface-lab`, `--ink-inverse`), never numerically. `--ink-inverse`), never numerically.
## Type scale ## Type scale
@@ -56,8 +56,11 @@ repetitions.
Five named widths replace the current sixteen: Five named widths replace the current sixteen:
```css ```css
--bp-sm: 560px; --bp-md: 800px; --bp-lg: 1100px; --bp-sm: 560px;
--bp-xl: 1600px; --bp-2xl: 2200px; --bp-md: 800px;
--bp-lg: 1100px;
--bp-xl: 1600px;
--bp-2xl: 2200px;
``` ```
When collapsing a component's old breakpoint onto a named one, screenshot at the When collapsing a component's old breakpoint onto a named one, screenshot at the
@@ -74,5 +77,5 @@ When collapsing a component's old breakpoint onto a named one, screenshot at the
## Fonts ## Fonts
Do not add a webfont without an explicit decision recorded in the task. The Do not add a webfont without an explicit decision recorded in the task. The
intended Manrope/DM Mono has never rendered; introducing it is a visual redesign, intended Manrope/DM Mono has never rendered; introducing it is a visual
not a refactor. Default: match what renders today. redesign, not a refactor. Default: match what renders today.
+7 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
// Fails when a raw colour, px font-size, or ad-hoc breakpoint appears outside // Fails when a raw colour, px font-size, or ad-hoc breakpoint appears outside
// the token layer. A rule nobody checks is a suggestion — wire this into // the token layer. A rule nobody checks is a suggestion — wire this into
// `npm run verify`. // `pnpm run verify`.
// //
// Usage: node .agents/scripts/check-tokens.mjs [srcDir] // Usage: node .agents/scripts/check-tokens.mjs [srcDir]
@@ -29,7 +29,9 @@ for (const path of targets) {
if (!['.astro', '.css'].includes(extname(path))) continue; if (!['.astro', '.css'].includes(extname(path))) continue;
if (TOKEN_FILES.some((allowed) => path.endsWith(allowed))) continue; if (TOKEN_FILES.some((allowed) => path.endsWith(allowed))) continue;
readFileSync(path, 'utf8').split('\n').forEach((line, index) => { readFileSync(path, 'utf8')
.split('\n')
.forEach((line, index) => {
const at = `${path}:${index + 1}`; const at = `${path}:${index + 1}`;
// Raw hex — the drifted-palette failure mode this whole layer exists to stop. // Raw hex — the drifted-palette failure mode this whole layer exists to stop.
@@ -47,7 +49,9 @@ for (const path of targets) {
// Ad-hoc breakpoints are how sixteen of them accumulated last time. // Ad-hoc breakpoints are how sixteen of them accumulated last time.
const media = line.match(/@media[^{]*?\(\s*(?:max|min)-width:\s*(\d+px)/); const media = line.match(/@media[^{]*?\(\s*(?:max|min)-width:\s*(\d+px)/);
if (media && !ALLOWED_BREAKPOINTS.includes(media[1])) if (media && !ALLOWED_BREAKPOINTS.includes(media[1]))
findings.push(`${at}: breakpoint ${media[1]} is not a named one (${ALLOWED_BREAKPOINTS.join(', ')})`); findings.push(
`${at}: breakpoint ${media[1]} is not a named one (${ALLOWED_BREAKPOINTS.join(', ')})`,
);
}); });
} }
+13 -4
View File
@@ -26,18 +26,27 @@ step() { printf '\n\033[1m▸ %s\033[0m\n' "$1"; }
# Fail loudly rather than passing vacuously when the toolchain is not installed. # Fail loudly rather than passing vacuously when the toolchain is not installed.
if [ ! -d node_modules ]; then if [ ! -d node_modules ]; then
echo "gate: node_modules missing — run 'npm ci --prefer-offline' first" >&2 echo "gate: node_modules missing — run 'pnpm install --frozen-lockfile' first" >&2
exit 1
fi
# This project is pnpm-only. An agent that runs `npm install` out of habit gets
# a second, divergent dependency tree and a lockfile nobody reads — the same
# class of failure that cost two sessions during phase 0. Catch it here.
if [ -f package-lock.json ] || [ -f yarn.lock ] || [ -f bun.lock ] || [ -f bun.lockb ]; then
echo "gate: a non-pnpm lockfile is present. This project uses pnpm only." >&2
echo " Delete it, then run 'pnpm install --frozen-lockfile'." >&2
exit 1 exit 1
fi fi
step "types" step "types"
npx --no-install astro check pnpm exec astro check
step "build" step "build"
npm run build pnpm run build
step "content contracts" step "content contracts"
npm run verify pnpm run verify
# The assertion count is the thing agents are most tempted to "fix" downward. # The assertion count is the thing agents are most tempted to "fix" downward.
# Compare against origin/main and refuse a silent reduction. # Compare against origin/main and refuse a silent reduction.
+3 -3
View File
@@ -58,11 +58,11 @@ else
git worktree add "$dir" -b "$branch" "$base" git worktree add "$dir" -b "$branch" "$base"
fi fi
# npm ci only once task 01 has produced a lockfile. The pre-existing root # pnpm install works only once task 01 has produced a lockfile. The pre-existing root
# package.json carries two scripts and no dependencies, so before task 01 there # package.json carries two scripts and no dependencies, so before task 01 there
# is no toolchain to install and no hooks to verify. # is no toolchain to install and no hooks to verify.
if [ -f "$dir/package-lock.json" ]; then if [ -f "$dir/pnpm-lock.yaml" ]; then
( cd "$dir" && npm ci --prefer-offline && .agents/scripts/verify-hooks.sh ) ( cd "$dir" && pnpm install --frozen-lockfile && .agents/scripts/verify-hooks.sh )
fi fi
prompt=$(cat <<PROMPT prompt=$(cat <<PROMPT
+2 -2
View File
@@ -2,7 +2,7 @@
# Guards the silent-failure mode described in .agents/rules/gates.md. # Guards the silent-failure mode described in .agents/rules/gates.md.
# #
# Husky points core.hooksPath at `.husky/_`, but that directory is GENERATED by # Husky points core.hooksPath at `.husky/_`, but that directory is GENERATED by
# `npm install` and is NOT committed. A fresh `git worktree add` therefore has # `pnpm install` and is NOT committed. A fresh `git worktree add` therefore has
# hooks configured and the directory missing — so every hook silently does # hooks configured and the directory missing — so every hook silently does
# nothing and every commit passes unchecked. # nothing and every commit passes unchecked.
# #
@@ -43,7 +43,7 @@ fi
if [ "$fail" -ne 0 ]; then if [ "$fail" -ne 0 ]; then
echo echo
echo "Fix: npm ci --prefer-offline (its prepare script regenerates .husky/_)" echo "Fix: pnpm install --frozen-lockfile (its prepare script regenerates .husky/_)"
exit 1 exit 1
fi fi
+1 -1
View File
@@ -30,7 +30,7 @@ try {
({ chromium } = await import('playwright')); ({ chromium } = await import('playwright'));
} catch { } catch {
throw new Error( throw new Error(
'visual regression requires Playwright; install its project dependency and run npx playwright install chromium', 'visual regression requires Playwright; install its project dependency and run pnpm exec playwright install chromium',
); );
} }
+1 -1
View File
@@ -24,7 +24,7 @@ case "$action" in
# Not optional. `.husky/_` is generated by install and is NOT committed, so # Not optional. `.husky/_` is generated by install and is NOT committed, so
# a fresh worktree has hooks configured but absent — every commit would pass # a fresh worktree has hooks configured but absent — every commit would pass
# unchecked. --prefer-offline keeps ten parallel spin-ups off the registry. # unchecked. --prefer-offline keeps ten parallel spin-ups off the registry.
( cd "$dir" && npm ci --prefer-offline && .agents/scripts/verify-hooks.sh ) ( cd "$dir" && pnpm install --frozen-lockfile && .agents/scripts/verify-hooks.sh )
echo echo
echo "worktree : $dir" echo "worktree : $dir"
+18 -5
View File
@@ -1,6 +1,9 @@
--- ---
name: astro-component name: astro-component
description: Build one Astro component for the ai-for-dummies site from the project templates. Use when creating a primitive, block, or island, or when splitting existing markup into a component. description:
Build one Astro component for the ai-for-dummies site from the project
templates. Use when creating a primitive, block, or island, or when splitting
existing markup into a component.
--- ---
# Building an Astro component # Building an Astro component
@@ -38,6 +41,7 @@ interface Props {
} }
const { number, title, summary, href } = Astro.props; const { number, title, summary, href } = Astro.props;
--- ---
<article class="card"> <article class="card">
<b>{number}</b> <b>{number}</b>
<h2>{title}</h2> <h2>{title}</h2>
@@ -46,8 +50,16 @@ const { number, title, summary, href } = Astro.props;
</article> </article>
<style> <style>
.card { display: grid; gap: 10px; padding: 22px; background: var(--paper); } .card {
b { color: var(--accent); font: var(--font-eyebrow); } display: grid;
gap: 10px;
padding: 22px;
background: var(--paper);
}
b {
color: var(--accent);
font: var(--font-eyebrow);
}
</style> </style>
``` ```
@@ -64,5 +76,6 @@ Rules that bite most often here:
1. Render it at 560 / 800 / 1100 / 1600 px. 1. Render it at 560 / 800 / 1100 / 1600 px.
2. Tab to it. Focus ring visible (`outline: 3px solid #a7483f`). 2. Tab to it. Focus ring visible (`outline: 3px solid #a7483f`).
3. Diff its rendered text against the markup it replaced. 3. Diff its rendered text against the markup it replaced.
4. Run [`../../checklists/before-component.md`](../../checklists/before-component.md). 4. Run
5. `npm run verify` — green, with no assertion deleted. [`../../checklists/before-component.md`](../../checklists/before-component.md).
5. `pnpm run verify` — green, with no assertion deleted.
+9 -5
View File
@@ -1,6 +1,9 @@
--- ---
name: astro-page name: astro-page
description: Migrate one hand-written HTML page of the ai-for-dummies site to an Astro route without changing its URL, content, or JS budget. Use for any page-level migration task. description:
Migrate one hand-written HTML page of the ai-for-dummies site to an Astro
route without changing its URL, content, or JS budget. Use for any page-level
migration task.
--- ---
# Migrating a page to Astro # Migrating a page to Astro
@@ -10,7 +13,7 @@ description: Migrate one hand-written HTML page of the ai-for-dummies site to an
The snapshot is the only objective evidence that no content was lost. The snapshot is the only objective evidence that no content was lost.
```bash ```bash
npm run serve & # vanilla site on :4173 pnpm run serve & # vanilla site on :4173
node .agents/scripts/snapshot-route.mjs http://localhost:4173/models/ \ node .agents/scripts/snapshot-route.mjs http://localhost:4173/models/ \
> .agents/snapshots/models.txt > .agents/snapshots/models.txt
``` ```
@@ -30,7 +33,8 @@ Then, in order:
2. Move copy into `src/content/`. Both `en` and `pt`, copied literally — never 2. Move copy into `src/content/`. Both `en` and `pt`, copied literally — never
retyped. retyped.
3. Move page CSS into component `<style>` blocks. Do **not** port 3. Move page CSS into component `<style>` blocks. Do **not** port
`responsive.css` wholesale; take what this page needs and prove the rest dead. `responsive.css` wholesale; take what this page needs and prove the rest
dead.
4. Keep every `data-*` hook. `verify.mjs` asserts many of them by name 4. Keep every `data-*` hook. `verify.mjs` asserts many of them by name
(`data-phase`, `data-tree`, `data-route`, `data-model-provider`, …). (`data-phase`, `data-tree`, `data-route`, `data-model-provider`, …).
5. Keep every ARIA attribute and the `<title>` / `<meta name="description">`. 5. Keep every ARIA attribute and the `<title>` / `<meta name="description">`.
@@ -49,11 +53,11 @@ documented in the page footer and shared externally.
## Prove it ## Prove it
```bash ```bash
npm run build pnpm run build
node .agents/scripts/snapshot-route.mjs dist/models/index.html > /tmp/after.txt node .agents/scripts/snapshot-route.mjs dist/models/index.html > /tmp/after.txt
diff .agents/snapshots/models.txt /tmp/after.txt # empty, or justify every line diff .agents/snapshots/models.txt /tmp/after.txt # empty, or justify every line
node scripts/audit-ui.mjs node scripts/audit-ui.mjs
npm run verify pnpm run verify
``` ```
Then screenshots at the same four widths, and Then screenshots at the same four widths, and
+8 -4
View File
@@ -1,6 +1,10 @@
--- ---
name: design-tokens name: design-tokens
description: Extract, name, and enforce the design token layer for the ai-for-dummies site. Use when touching any colour, font size, spacing value, or breakpoint, when consolidating the three drifted palettes, or when a check-tokens failure needs resolving. description:
Extract, name, and enforce the design token layer for the ai-for-dummies site.
Use when touching any colour, font size, spacing value, or breakpoint, when
consolidating the three drifted palettes, or when a check-tokens failure needs
resolving.
--- ---
# Design tokens # Design tokens
@@ -9,8 +13,8 @@ description: Extract, name, and enforce the design token layer for the ai-for-du
Read [`../../context/design-system.md`](../../context/design-system.md). This Read [`../../context/design-system.md`](../../context/design-system.md). This
site has **three drifting palettes** and a **broken `@font-face`**. Both are site has **three drifting palettes** and a **broken `@font-face`**. Both are
traps. If you have not read that file, you will "preserve the styles" by traps. If you have not read that file, you will "preserve the styles" by copying
copying a bug. a bug.
## Extracting ## Extracting
@@ -60,7 +64,7 @@ a breakpoint, **screenshot at the old value** — that is where the regression i
node .agents/scripts/check-tokens.mjs # fails on raw hex outside tokens.css node .agents/scripts/check-tokens.mjs # fails on raw hex outside tokens.css
``` ```
Wire it into `npm run verify`. A rule nobody checks is a suggestion. Wire it into `pnpm run verify`. A rule nobody checks is a suggestion.
## The font decision ## The font decision
+10 -8
View File
@@ -1,6 +1,8 @@
--- ---
name: visual-regression name: visual-regression
description: Prove a refactor did not change how the site looks. Use before and after any page migration, token consolidation, or breakpoint change on ai-for-dummies. description:
Prove a refactor did not change how the site looks. Use before and after any
page migration, token consolidation, or breakpoint change on ai-for-dummies.
--- ---
# Visual regression # Visual regression
@@ -31,8 +33,8 @@ with sync_playwright() as p:
browser.close() browser.close()
``` ```
Run once against the vanilla site (`npm run serve`), once against Run once against the vanilla site (`pnpm run serve`), once against
`npm run preview`. Keep both sets. `pnpm run preview`. Keep both sets.
## Compare ## Compare
@@ -43,8 +45,8 @@ for f in before/*.png; do
done done
``` ```
Pixel-exact is not the bar — antialiasing differs. Judge by eye where the Pixel-exact is not the bar — antialiasing differs. Judge by eye where the metric
metric is non-trivial, and attach the pair to the task report. is non-trivial, and attach the pair to the task report.
## The three widths that catch the most ## The three widths that catch the most
@@ -61,9 +63,9 @@ Also screenshot at the **old** breakpoint values you removed (520, 530, 600,
Expect and accept: sub-pixel text shifts, antialiasing. Expect and accept: sub-pixel text shifts, antialiasing.
Investigate: anything that moves by more than ~2px, any colour change (that is Investigate: anything that moves by more than ~2px, any colour change (that is a
a token bug), any element that appears or disappears (that is content loss — token bug), any element that appears or disappears (that is content loss — stop
stop and check the snapshot diff). and check the snapshot diff).
## Reduced motion ## Reduced motion
+29 -16
View File
@@ -6,6 +6,14 @@ on:
push: push:
branches: [main] branches: [main]
pull_request: pull_request:
# Publication is deliberately manual for the duration of the Astro migration.
# See the `publish` job below for why.
workflow_dispatch:
inputs:
publish:
description: 'Force-push dist/ to the pages branch'
type: boolean
default: false
jobs: jobs:
gate: gate:
@@ -17,22 +25,27 @@ jobs:
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
cache: npm # Node 25 dropped the bundled corepack, and this self-hosted act-runner
- run: npm install --package-lock=false --prefer-offline # is not a good place to discover whether it can fetch a third-party
- run: npm run lint # action. Bootstrapping pnpm with npm needs neither. Keep the version in
# step with package.json's `packageManager`.
- run: npm install --global pnpm@11.25.0
- run: pnpm install --frozen-lockfile
- run: pnpm run lint
- run: ./.agents/scripts/gate.sh - run: ./.agents/scripts/gate.sh
- name: visual regression # The visual-regression step is intentionally absent. `visual-regression.mjs`
run: | # only *captures* baselines — it has no compare mode — so in CI it would
npx playwright install --with-deps chromium # overwrite .agents/snapshots/before/ and pass unconditionally. It also
node .agents/scripts/visual-regression.mjs # imports `playwright`, which is not a dependency of this project. Wire it
- uses: actions/upload-artifact@v4 # back in once it can actually diff. See task 03's report.
if: failure()
with:
name: screenshots
path: .agents/snapshots/diff/
publish: publish:
if: github.ref == 'refs/heads/main' # Until the migration finishes, `dist/` holds only /summary/ and the two
# hands-on fixtures, while the live `pages` branch serves ten pages.
# Publishing on every push to main would take the site down to a stub, so
# this job runs only when a human asks for it. Make it unconditional on
# main again at task 20 (cutover), not before.
if: github.event_name == 'workflow_dispatch' && inputs.publish
needs: gate needs: gate
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -40,9 +53,9 @@ jobs:
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
cache: npm - run: npm install --global pnpm@11.25.0
- run: npm install --package-lock=false --prefer-offline - run: pnpm install --frozen-lockfile
- run: npm run build - run: pnpm run build
- name: Publish generated site to pages - name: Publish generated site to pages
run: | run: |
git config user.name 'gitea-actions[bot]' git config user.name 'gitea-actions[bot]'
+7
View File
@@ -15,3 +15,10 @@ dist/
# agent run logs # agent run logs
.agents/logs/ .agents/logs/
# pnpm-only project. A stray npm/yarn/bun run here is a bug the gate rejects;
# ignoring the artefacts keeps one from being committed by accident.
package-lock.json
yarn.lock
bun.lock
bun.lockb
+2 -2
View File
@@ -3,8 +3,8 @@
# Husky v9+: no `husky.sh` sourcing (deprecated in v9, removed in v10). # Husky v9+: no `husky.sh` sourcing (deprecated in v9, removed in v10).
# Guard the silent-failure mode: if hooks are configured but .husky/_ is # Guard the silent-failure mode: if hooks are configured but .husky/_ is
# missing (fresh worktree that never ran `npm install`), this file would not # missing (fresh worktree that never ran `pnpm install`), this file would not
# execute at all. Nothing we can do from inside it — so verify-hooks.sh exists # execute at all. Nothing we can do from inside it — so verify-hooks.sh exists
# and worktree.sh runs it at creation time. # and worktree.sh runs it at creation time.
npx --no-install lint-staged pnpm exec lint-staged
+1
View File
@@ -5,3 +5,4 @@ submitted-skills
skill-reviews skill-reviews
vote-service vote-service
.agents/snapshots .agents/snapshots
pnpm-lock.yaml
+29 -19
View File
@@ -12,54 +12,64 @@ rules, and verification.** It is published as a static site on a self-hosted
Gitea Pages Server, and it doubles as its own teaching artifact: the hands-on Gitea Pages Server, and it doubles as its own teaching artifact: the hands-on
labs are dependency-free HTML/CSS/JS that workshop attendees point an agent at. labs are dependency-free HTML/CSS/JS that workshop attendees point an agent at.
- **Current stack**: hand-written HTML + CSS + ES modules, no build step, no dependencies - **Current stack**: hand-written HTML + CSS + ES modules, no build step, no
- **Target stack**: Astro (see [`plans/astro-refactor/`](plans/astro-refactor/README.md)) — migration in progress dependencies
- **Target stack**: Astro (see
[`plans/astro-refactor/`](plans/astro-refactor/README.md)) — migration in
progress
- **Languages**: English and Brazilian Portuguese, toggled client-side - **Languages**: English and Brazilian Portuguese, toggled client-side
- **Companion service**: `vote-service/` (Go + Kubernetes) — separate lifecycle, see its own README - **Companion service**: `vote-service/` (Go + Kubernetes) — separate lifecycle,
see its own README
## Essential commands ## Essential commands
```bash ```bash
npm run verify # content + interaction contracts (scripts/verify.mjs) — the gate pnpm run verify # content + interaction contracts (scripts/verify.mjs) — the gate
node scripts/audit-ui.mjs # responsive / no-external-dependency audit node scripts/audit-ui.mjs # responsive / no-external-dependency audit
node scripts/build-skill-review.mjs # regenerate skill-reviews/improved/ from catalog.js node scripts/build-skill-review.mjs # regenerate skill-reviews/improved/ from catalog.js
npm run serve # python3 -m http.server 4173 pnpm run serve # python3 -m http.server 4173
``` ```
`npm run verify` is not a formality. It is a set of ~42 string-token assertions `pnpm run verify` is not a formality. It is a set of ~42 string-token assertions
that pin the site's real content and interactions. **A refactor that "passes" that pin the site's real content and interactions. **A refactor that "passes" by
by deleting assertions has failed.** See deleting assertions has failed.** See
[`.agents/context/verification.md`](.agents/context/verification.md). [`.agents/context/verification.md`](.agents/context/verification.md).
## Publishing ## Publishing
`main` is the source of truth. The `pages` branch is what the Gitea Pages `main` is the source of truth. The `pages` branch is what the Gitea Pages Server
Server actually serves, and its tree must end up identical to `main`'s. The actually serves, and its tree must end up identical to `main`'s. The full
full procedure — including why `merge --ff-only` does *not* work here — is in procedure — including why `merge --ff-only` does _not_ work here — is in
[`docs/operations-guide.md`](docs/operations-guide.md). [`docs/operations-guide.md`](docs/operations-guide.md).
Adding a build step changes this contract. Read Adding a build step changes this contract. Read
[`.agents/context/publishing.md`](.agents/context/publishing.md) before doing so. [`.agents/context/publishing.md`](.agents/context/publishing.md) before doing
so.
## Never touch ## Never touch
- `hands-on/starter/` and `hands-on/rules/`**lab fixtures.** The exercise *is* - `hands-on/starter/` and `hands-on/rules/`**lab fixtures.** The exercise
that they are dependency-free vanilla HTML/CSS/JS an attendee can hand to an _is_ that they are dependency-free vanilla HTML/CSS/JS an attendee can hand to
agent. Componentizing them destroys the lesson. They ship as static assets. an agent. Componentizing them destroys the lesson. They ship as static assets.
- `submitted-skills/` — other people's submitted work, reproduced verbatim - `submitted-skills/` — other people's submitted work, reproduced verbatim
- `skill-reviews/improved/` — generated; edit `skills-review/catalog.js` instead - `skill-reviews/improved/` — generated; edit `skills-review/catalog.js` instead
- `vote-service/` — separate deploy lifecycle; do not fold into the site build - `vote-service/` — separate deploy lifecycle; do not fold into the site build
- `dist/`, `node_modules/` — build output, never committed - `dist/`, `node_modules/` — build output, never committed
- `package-lock.json`**committed, but never hand-edited.** Change it only - `pnpm-lock.yaml`**committed, but never hand-edited.** Change it only as a
as a side effect of `npm install`. Every worktree spins up with `npm ci`, side effect of `pnpm install`. Every worktree spins up with
which fails outright without it. `pnpm install --frozen-lockfile`, which fails outright without it.
- This project is **pnpm-only** (`packageManager` in `package.json` pins the
version). Never run `npm install` or `bun install` here — the gate rejects a
`package-lock.json`, `yarn.lock`, or `bun.lock` outright. pnpm settings live
in `pnpm-workspace.yaml`, **not** in a `pnpm` field in `package.json`; pnpm 11
ignores that field silently.
## Rules ## Rules
Binding. Read the one that covers what you are about to do. Binding. Read the one that covers what you are about to do.
| Rule | When | | Rule | When |
| --- | --- | | ---------------------------------------------------------- | ------------------------------------ |
| [`astro.md`](.agents/rules/astro.md) | any `.astro` file | | [`astro.md`](.agents/rules/astro.md) | any `.astro` file |
| [`theming.md`](.agents/rules/theming.md) | any colour, font, or spacing value | | [`theming.md`](.agents/rules/theming.md) | any colour, font, or spacing value |
| [`componentization.md`](.agents/rules/componentization.md) | creating or splitting a component | | [`componentization.md`](.agents/rules/componentization.md) | creating or splitting a component |
+42 -34
View File
@@ -3,16 +3,15 @@
A lightweight, presentation-style field guide to AI-assisted engineering. A lightweight, presentation-style field guide to AI-assisted engineering.
It explains how to combine a strong planning/review model with faster workers, It explains how to combine a strong planning/review model with faster workers,
reusable skills, subagent handoffs, Git worktrees, and explicit verification. reusable skills, subagent handoffs, Git worktrees, and explicit verification. An
An interactive field kit compares common behavior skills such as interactive field kit compares common behavior skills such as `ponytail-lite`,
`ponytail-lite`, `caveman`, `unlazy`, research, debugging, and review. `caveman`, `unlazy`, research, debugging, and review. The skill-forge workflow
The skill-forge workflow covers discovery, triggers, package anatomy, covers discovery, triggers, package anatomy, progressive instructions,
progressive instructions, structural validation, and behavioral iteration. structural validation, and behavioral iteration. The hands-on lab provides a
The hands-on lab provides a tiny starter project and copy-ready baseline and tiny starter project and copy-ready baseline and skill-enabled prompts for a
skill-enabled prompts for a short side-by-side exercise. short side-by-side exercise. An interactive model gearbox separates capability
An interactive model gearbox separates capability tier from reasoning effort tier from reasoning effort across OpenAI, Claude, and Gemini, and every featured
across OpenAI, Claude, and Gemini, and every featured skill links to a pinned skill links to a pinned source with an approval-first installation prompt.
source with an approval-first installation prompt.
## Run locally ## Run locally
@@ -27,23 +26,30 @@ Then open <http://localhost:4173>.
Verify the content and interaction contracts with: Verify the content and interaction contracts with:
```bash ```bash
npm run verify pnpm run verify
``` ```
## Project structure ## Project structure
- `index.html` — default route map and focused chapter navigation - `index.html` — default route map and focused chapter navigation
- `full-guide/` — the complete bilingual presentation, with responsive audit overrides - `full-guide/` — the complete bilingual presentation, with responsive audit
- `styles.css` / `app.js` — editorial visual system and bilingual field-guide interactions overrides
- `styles.css` / `app.js` — editorial visual system and bilingual field-guide
interactions
- `responsive.css` — interactive diagrams and Full HD-to-4K adaptations - `responsive.css` — interactive diagrams and Full HD-to-4K adaptations
- `docs/references/` — bundled research sources and notes - `docs/references/` — bundled research sources and notes
- `docs/operations-guide.md` — canonical SilverBullet operations and skills guide - `docs/operations-guide.md` — canonical SilverBullet operations and skills
guide
- `hands-on/starter/` — dependency-free Tiny Tasks exercise - `hands-on/starter/` — dependency-free Tiny Tasks exercise
- `hands-on/rules/` — dependency-free Guardrails lab; toggles rule sources into the prompt - `hands-on/rules/` — dependency-free Guardrails lab; toggles rule sources into
the prompt
- `rules/` — bilingual case study of skills, CLI ratchets, Husky, and PR review - `rules/` — bilingual case study of skills, CLI ratchets, Husky, and PR review
- `skills/` — reusable design and rules-case-study skills, plus an interactive package anatomy explorer - `skills/` — reusable design and rules-case-study skills, plus an interactive
- `skills-review/` — static review desk for submitted skills; its reader vote widget calls the separate `vote-service` package anatomy explorer
- `vote-service/` — small Go API + Kubernetes manifests backing the skills-review vote widget (see `vote-service/README.md`) - `skills-review/` — static review desk for submitted skills; its reader vote
widget calls the separate `vote-service`
- `vote-service/` — small Go API + Kubernetes manifests backing the
skills-review vote widget (see `vote-service/README.md`)
- `GATES.md` — acceptance ledger for the project - `GATES.md` — acceptance ledger for the project
## Publishing ## Publishing
@@ -54,9 +60,9 @@ The Gitea instance has a Pages Server configured to publish a repositorys
<https://netcracker.pages.marcospaulo.dev.br/ai-for-dummies/> <https://netcracker.pages.marcospaulo.dev.br/ai-for-dummies/>
If the URL is not available yet, verify that the `pages` branch exists and that If the URL is not available yet, verify that the `pages` branch exists and that
the repositorys `pages` branch exists. Gitea itself does not the repositorys `pages` branch exists. Gitea itself does not provide a built-in
provide a built-in Pages server; this setup uses the instances separate Pages Pages server; this setup uses the instances separate Pages Server and Actions
Server and Actions deployment path. deployment path.
For the complete authoring, verification, publication, rollback, worktree, and For the complete authoring, verification, publication, rollback, worktree, and
skill workflow, see [docs/operations-guide.md](docs/operations-guide.md). skill workflow, see [docs/operations-guide.md](docs/operations-guide.md).
@@ -64,9 +70,9 @@ skill workflow, see [docs/operations-guide.md](docs/operations-guide.md).
## Reader voting on the skills-review desk ## Reader voting on the skills-review desk
`skills-review/` is static, so its "which draft would you ship?" vote widget `skills-review/` is static, so its "which draft would you ship?" vote widget
calls a separate stateful service — `vote-service/`, a small Go API on its calls a separate stateful service — `vote-service/`, a small Go API on its own
own pod, one vote per visitor enforced server-side by IP (a MAC address is pod, one vote per visitor enforced server-side by IP (a MAC address is never
never visible to a server across the internet, so it cannot be used). See visible to a server across the internet, so it cannot be used). See
[vote-service/README.md](vote-service/README.md) for the API, the anti-abuse [vote-service/README.md](vote-service/README.md) for the API, the anti-abuse
design, and the build/push/deploy steps; `skills-review/index.html` sets design, and the build/push/deploy steps; `skills-review/index.html` sets
`window.SKILLS_REVIEW_VOTE_API` to point at it once deployed. `window.SKILLS_REVIEW_VOTE_API` to point at it once deployed.
@@ -74,19 +80,21 @@ design, and the build/push/deploy steps; `skills-review/index.html` sets
## Research ## Research
See [docs/references/README.md](docs/references/README.md) for official Claude, See [docs/references/README.md](docs/references/README.md) for official Claude,
Codex, and Git documentation. The [additional reading path](docs/references/additional-reading.md) Codex, and Git documentation. The
bundles 12 verified articles and guides, including Medium and practitioner sources. [additional reading path](docs/references/additional-reading.md) bundles 12
See [model routing](docs/references/model-routing.md) for current provider controls verified articles and guides, including Medium and practitioner sources. See
and [verified skill sources](docs/references/skill-sources.md) for commit-pinned provenance. [model routing](docs/references/model-routing.md) for current provider controls
and [verified skill sources](docs/references/skill-sources.md) for commit-pinned
provenance.
## Rules and enforcement case study ## Rules and enforcement case study
Open `/rules/` for a concise walkthrough grounded in the Open `/rules/` for a concise walkthrough grounded in the `netcracker/interview`
`netcracker/interview` repository. It shows how `AGENTS.md`, project-local repository. It shows how `AGENTS.md`, project-local skills, machine-readable
skills, machine-readable repo ledgers, a UI contract ratchet, lint-staged, repo ledgers, a UI contract ratchet, lint-staged, Husky, commitlint, specialist
Husky, commitlint, specialist verifier agents, and PR review reinforce one verifier agents, and PR review reinforce one another. Every example links to its
another. Every example links to its source file in Gitea, and the page includes source file in Gitea, and the page includes a copy-ready prompt for mapping the
a copy-ready prompt for mapping the same layers in another repository. same layers in another repository.
The implementation patterns are also packaged as project-local skills in The implementation patterns are also packaged as project-local skills in
[skills/](skills/README.md). Use `editorial-playbook` when adding chapters or [skills/](skills/README.md). Use `editorial-playbook` when adding chapters or
+7 -7
View File
@@ -25,7 +25,7 @@ worktree practices taught by the presentation fit together.
| Local checkout | `/home/marcos/Projects/ai-for-dummies` | | Local checkout | `/home/marcos/Projects/ai-for-dummies` |
| Source branch | `main` | | Source branch | `main` |
| Published branch | `pages` | | Published branch | `pages` |
| Local verification | `npm run verify` | | Local verification | `pnpm run verify` |
| SilverBullet page | `Guides/AI For Dummies Presentation` | | SilverBullet page | `Guides/AI For Dummies Presentation` |
| Skills-review vote API | `vote-service/` — separate pod, see `vote-service/README.md` | | Skills-review vote API | `vote-service/` — separate pod, see `vote-service/README.md` |
@@ -51,7 +51,7 @@ migration task.
```mermaid ```mermaid
flowchart LR flowchart LR
E[Edit main] --> V[npm run gate] E[Edit main] --> V[pnpm run gate]
V --> C[Commit] V --> C[Commit]
C --> P[Push main] C --> P[Push main]
P --> G[Gitea Actions: gate + build] P --> G[Gitea Actions: gate + build]
@@ -76,7 +76,7 @@ part of the published site.
### 2. Preview locally ### 2. Preview locally
```bash ```bash
npm run dev pnpm run dev
``` ```
Open Open
@@ -88,7 +88,7 @@ least one desktop and one mobile viewport. The production site is under the
### 3. Verify before committing ### 3. Verify before committing
```bash ```bash
npm run gate pnpm run gate
``` ```
The gate runs Astro types and build checks, content contracts, the runtime The gate runs Astro types and build checks, content contracts, the runtime
@@ -120,8 +120,8 @@ If the runner is unavailable, use the following manual fallback from a clean
`main` checkout. It deliberately replaces the generated branch tree only: `main` checkout. It deliberately replaces the generated branch tree only:
```bash ```bash
npm install --package-lock=false --prefer-offline pnpm install --frozen-lockfile
npm run build pnpm run build
git worktree add /tmp/ai-for-dummies-pages --detach pages git worktree add /tmp/ai-for-dummies-pages --detach pages
git -C /tmp/ai-for-dummies-pages rm -rf . git -C /tmp/ai-for-dummies-pages rm -rf .
cp -a dist/. /tmp/ai-for-dummies-pages/ cp -a dist/. /tmp/ai-for-dummies-pages/
@@ -523,7 +523,7 @@ ordinary content recovery.
- [ ] Portuguese static and dynamic copy is complete. - [ ] Portuguese static and dynamic copy is complete.
- [ ] Mouse and keyboard interactions work. - [ ] Mouse and keyboard interactions work.
- [ ] Full HD, 4K, and mobile layouts remain readable. - [ ] Full HD, 4K, and mobile layouts remain readable.
- [ ] `npm run verify`, `node --check app.js`, and `git diff --check` pass. - [ ] `pnpm run verify`, `node --check app.js`, and `git diff --check` pass.
- [ ] `main` is pushed. - [ ] `main` is pushed.
- [ ] `pages` fast-forwards to the same commit. - [ ] `pages` fast-forwards to the same commit.
- [ ] Live endpoint returns HTTP 200 and contains the new section. - [ ] Live endpoint returns HTTP 200 and contains the new section.
-9205
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -30,9 +30,9 @@
"stylelint-config-standard": "^39", "stylelint-config-standard": "^39",
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },
"overrides": { "packageManager": "pnpm@11.25.0",
"@astrojs/language-server": "2.15.0", "engines": {
"defu": "6.1.7", "node": ">=22.13",
"vite": "6.2.1" "pnpm": ">=11"
} }
} }
+31 -30
View File
@@ -7,29 +7,29 @@ merged into anything.
## Where the work is ## Where the work is
| Branch | Head | Worktree | Gate | | Branch | Head | Worktree | Gate |
| --- | --- | --- | --- | | ----------------------------------- | --------- | --------------------------- | ----------- |
| `main` | `a45aa84` | `~/Projects/ai-for-dummies` | n/a | | `main` | `a45aa84` | `~/Projects/ai-for-dummies` | n/a |
| `refactor/task-01-scaffold` | `71021e6` | `~/Projects/af-task-01` | passed | | `refactor/task-01-scaffold` | `71021e6` | `~/Projects/af-task-01` | passed |
| `refactor/task-02-tokens` | `c9ec9e3` | `~/Projects/af-task-02` | passed | | `refactor/task-02-tokens` | `c9ec9e3` | `~/Projects/af-task-02` | passed |
| `refactor/task-03-verification-net` | `2e79aac` | `~/Projects/af-task-03` | passed | | `refactor/task-03-verification-net` | `2e79aac` | `~/Projects/af-task-03` | passed |
| `refactor/task-04-content-schema` | `b554f87` | `~/Projects/af-task-04` | **not run** | | `refactor/task-04-content-schema` | `b554f87` | `~/Projects/af-task-04` | **not run** |
02, 03 and 04 all branch from `refactor/task-01-scaffold`, not from `main`. 02, 03 and 04 all branch from `refactor/task-01-scaffold`, not from `main`. Task
Task 01 must merge to `main` first, then the other three. 01 must merge to `main` first, then the other three.
## What each task actually produced ## What each task actually produced
**01 — scaffold (Codex).** Astro 5.5 with `base: '/ai-for-dummies'`, strict TS, **01 — scaffold (Codex).** Astro 5.5 with `base: '/ai-for-dummies'`, strict TS,
`BaseLayout.astro`, `summary.astro` as the single smoke page, lint/format/husky `BaseLayout.astro`, `summary.astro` as the single smoke page, lint/format/husky
configs, `.gitea/workflows/`, rewritten publishing section in configs, `.gitea/workflows/`, rewritten publishing section in
`docs/operations-guide.md`, `hands-on/` copied verbatim into `public/`. `docs/operations-guide.md`, `hands-on/` copied verbatim into `public/`. Build
Build passes, gate passes, hooks verified live, 42/42 assertions intact. passes, gate passes, hooks verified live, 42/42 assertions intact.
**02 — tokens (Gemini).** `src/styles/tokens.css` and `src/styles/base.css` **02 — tokens (Gemini).** `src/styles/tokens.css` and `src/styles/base.css` only
only — 43 lines. It did **not** touch the legacy CSS, deliberately: rewriting — 43 lines. It did **not** touch the legacy CSS, deliberately: rewriting
`styles.css` would break `verify.mjs`'s exact-string assertions. So the three `styles.css` would break `verify.mjs`'s exact-string assertions. So the three
drifting palettes are now resolved *in the new token layer*, while the live drifting palettes are now resolved _in the new token layer_, while the live site
site still runs on the old values. That is the correct scope, but it means the still runs on the old values. That is the correct scope, but it means the
consolidation is not proven visually yet. consolidation is not proven visually yet.
**03 — verification net (Codex).** `.agents/scripts/visual-regression.mjs`, **03 — verification net (Codex).** `.agents/scripts/visual-regression.mjs`,
@@ -38,15 +38,15 @@ rendered-text snapshots for all 10 routes, and PNG baselines at 4 widths in
purpose — they are the regression baseline). purpose — they are the regression baseline).
**04 — content schema (MiniMax).** `src/content/config.ts` with eight empty **04 — content schema (MiniMax).** `src/content/config.ts` with eight empty
collections and a strict `localized({en, pt})` helper. No content moved; that collections and a strict `localized({en, pt})` helper. No content moved; that is
is tasks 05 and 06. tasks 05 and 06.
## Things I fixed that the plan got wrong ## Things I fixed that the plan got wrong
- `AGENTS.md` listed `package-lock.json` under **Never touch**, meaning never - `AGENTS.md` listed `package-lock.json` under **Never touch**, meaning never
hand-edit. Task 01 read it as never create and shipped with hand-edit. Task 01 read it as never create and shipped with
`--package-lock=false`. `npm ci` — which is how every worktree spins up `--package-lock=false`. `npm ci` — which is how every worktree spun up at the
cannot work without it. Wording corrected; lockfile committed. time — cannot work without it. Wording corrected; lockfile committed.
- The first lockfile was reconstructed from a `node_modules` installed without - The first lockfile was reconstructed from a `node_modules` installed without
one, so its entries had no `resolved`/`integrity` and `npm ci` failed with one, so its entries had no `resolved`/`integrity` and `npm ci` failed with
`ETARGET tinyglobby@0.2.17`. Regenerated from a clean install. `ETARGET tinyglobby@0.2.17`. Regenerated from a clean install.
@@ -62,17 +62,17 @@ is tasks 05 and 06.
- **Task 03 went outside its brief.** It rewrote `scripts/audit-ui.mjs` to ban - **Task 03 went outside its brief.** It rewrote `scripts/audit-ui.mjs` to ban
external CSS dependencies — a sound check that the pre-existing malformed external CSS dependencies — a sound check that the pre-existing malformed
`@font-face` in `styles.css` violates, so it left the gate red for every `@font-face` in `styles.css` violates, so it left the gate red for every
downstream task. Reverted in `2e79aac`; the snapshot net was kept. **The downstream task. Reverted in `2e79aac`; the snapshot net was kept. **The check
check should come back once the font decision is made.** should come back once the font decision is made.**
- **Task 03 rebased instead of branching**, flattening task 01's merge into - **Task 03 rebased instead of branching**, flattening task 01's merge into four
four duplicate commits with new SHAs — the same divergent-history trap that duplicate commits with new SHAs — the same divergent-history trap that broke
broke the `pages` branch. Replayed onto the proper base; task 01 is an the `pages` branch. Replayed onto the proper base; task 01 is an ancestor
ancestor again. Worth adding to `.agents/rules/git-worktrees.md`: **never again. Worth adding to `.agents/rules/git-worktrees.md`: **never rebase a task
rebase a task branch onto anything.** branch onto anything.**
- **Task 02 left 29 MB of screenshots in `~/Projects/af-task-02/before/`** — - **Task 02 left 29 MB of screenshots in `~/Projects/af-task-02/before/`** —
untracked, and in the wrong place (`.agents/snapshots/` is the right one). untracked, and in the wrong place (`.agents/snapshots/` is the right one). Its
Its report claims the screenshot box is unticked while the files exist. report claims the screenshot box is unticked while the files exist. Delete
Delete them or move them; do not commit them where they are. them or move them; do not commit them where they are.
## Still open from task 01's own report ## Still open from task 01's own report
@@ -84,15 +84,16 @@ is tasks 05 and 06.
## The decision that is still yours ## The decision that is still yours
`styles.css:1` has an `@font-face` whose `src:` points at a Google Fonts `styles.css:1` has an `@font-face` whose `src:` points at a Google Fonts
*stylesheet*, not a font file. Manrope and DM Mono have therefore never _stylesheet_, not a font file. Manrope and DM Mono have therefore never
rendered; the site has always been Arial and generic monospace. Self-hosting rendered; the site has always been Arial and generic monospace. Self-hosting
them during the migration would silently redesign the site. Task 02 defaulted them during the migration would silently redesign the site. Task 02 defaulted to
to matching what renders today. Decide explicitly, then task 19 can restore the matching what renders today. Decide explicitly, then task 19 can restore the
external-CSS assertion. See `.agents/context/design-system.md`. external-CSS assertion. See `.agents/context/design-system.md`.
## Next steps, in order ## Next steps, in order
1. Verify task 04: `cd ~/Projects/af-task-04 && npm ci --prefer-offline && npm run gate` 1. Verify task 04:
`cd ~/Projects/af-task-04 && pnpm install --frozen-lockfile && pnpm run gate`
2. Review the four diffs against `.agents/checklists/before-merge.md`. Use a 2. Review the four diffs against `.agents/checklists/before-merge.md`. Use a
different model than the one that wrote each — `MODEL-ROUTING.md` says never different model than the one that wrote each — `MODEL-ROUTING.md` says never
review with the author. review with the author.
@@ -112,9 +113,9 @@ external-CSS assertion. See `.agents/context/design-system.md`.
`.agents/scripts/launch.sh <nn> <slug> [--base ref] [--cli codex|agy|mm] [--fg]` `.agents/scripts/launch.sh <nn> <slug> [--base ref] [--cli codex|agy|mm] [--fg]`
Routing is automatic: Codex for 01/03/15/16/19, `agy` (Gemini 3.1 Pro) for Routing is automatic: Codex for 01/03/15/16/19, `agy` (Gemini 3.1 Pro) for
02/18, `mm` (Claude Code against MiniMax-M3) for the rest. All three launch 02/18, `mm` (Claude Code against MiniMax-M3) for the rest. All three launch with
with permission prompts disabled, because a blocked edit in an unattended run permission prompts disabled, because a blocked edit in an unattended run just
just hangs. Logs land in `.agents/logs/` (gitignored). hangs. Logs land in `.agents/logs/` (gitignored).
**Task 07 is the routing calibration point.** It is small and easy to judge. **Task 07 is the routing calibration point.** It is small and easy to judge.
Check it before committing to MiniMax for the other twelve. Check it before committing to MiniMax for the other twelve.
+15 -13
View File
@@ -8,13 +8,13 @@ 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 file — **without changing how the site looks, what it says, or what it costs a
visitor to load.** visitor to load.**
**Session state: see [`HANDOVER.md`](HANDOVER.md).** Phase 0 is done and **Session state: see [`HANDOVER.md`](HANDOVER.md).** Phase 0 is done and green
green on four branches; nothing is merged or pushed. on four branches; nothing is merged or pushed.
## Read before starting anything ## Read before starting anything
| File | Why | | File | Why |
| --- | --- | | ---------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| [`../../AGENTS.md`](../../AGENTS.md) | entry point | | [`../../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/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/verification.md`](../../.agents/context/verification.md) | 42 assertions that will all break, and must not be deleted |
@@ -23,12 +23,13 @@ green on four branches; nothing is merged or pushed.
## The three things most likely to go wrong ## The three things most likely to go wrong
1. **Content loss that nobody notices.** 50 KB of bilingual copy moves between 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. that possible and blocks all page work.
2. **Assertions deleted to make a red suite green.** That converts a content-loss 2. **Assertions deleted to make a red suite green.** That converts a
bug into a passing build. `gate.sh` refuses a coverage drop. 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 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 ## Phases
@@ -41,7 +42,7 @@ Phase 4 polish 18 ∥ 19, then 20
``` ```
| # | Task | Agent | Depends on | Parallel with | | # | Task | Agent | Depends on | Parallel with |
| --- | --- | --- | --- | --- | | --- | ------------------------------------------------ | --------------------- | ---------- | ------------- |
| 01 | [scaffold + gates](task-01-scaffold.md) | astro-architect | — | — | | 01 | [scaffold + gates](task-01-scaffold.md) | astro-architect | — | — |
| 02 | [design tokens](task-02-tokens.md) | design-system-keeper | 01 | 03, 04 | | 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 | | 03 | [verification net](task-03-verification-net.md) | verification-engineer | 01 | 02, 04 |
@@ -75,14 +76,14 @@ cd ../af-task-08
# .agents/agents/component-builder.md (+ the skills it names) # .agents/agents/component-builder.md (+ the skills it names)
``` ```
The script runs `npm ci` and `verify-hooks.sh` for you. That matters: `.husky/_` The script runs `pnpm install --frozen-lockfile` and `verify-hooks.sh` for you.
is generated, not committed, so a hand-made worktree has hooks configured but That matters: `.husky/_` is generated, not committed, so a hand-made worktree
**silently not running**. has hooks configured but **silently not running**.
Finishing: Finishing:
```bash ```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 # reviewer agent reads the diff against .agents/checklists/before-merge.md
.agents/scripts/worktree.sh finish 08 route-cards .agents/scripts/worktree.sh finish 08 route-cards
``` ```
@@ -94,4 +95,5 @@ See [`MODEL-ROUTING.md`](MODEL-ROUTING.md).
## These files must be committed ## These files must be committed
Worktrees check out tracked files. If this plan stays untracked, every worktree 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.
+18 -14
View File
@@ -1,7 +1,8 @@
# Task 01 — Astro scaffold, gates, and the publishing decision # Task 01 — Astro scaffold, gates, and the publishing decision
**Agent**: `astro-architect` · **Model**: Codex · **Depends on**: nothing **Agent**: `astro-architect` · **Model**: Codex · **Depends on**: nothing
**Blocks**: everything · **Worktree**: `.agents/scripts/worktree.sh start 01 scaffold` **Blocks**: everything · **Worktree**:
`.agents/scripts/worktree.sh start 01 scaffold`
## Goal ## Goal
@@ -10,16 +11,16 @@ host under `/ai-for-dummies/`, and has all three gate tiers live.
## Scope ## Scope
`astro.config.mjs`, `package.json`, `tsconfig.json`, `src/layouts/BaseLayout.astro`, `astro.config.mjs`, `package.json`, `tsconfig.json`,
`.husky/`, `.lintstagedrc.json`, lint configs, `.gitea/workflows/verify.yml`, `src/layouts/BaseLayout.astro`, `.husky/`, `.lintstagedrc.json`, lint configs,
`docs/operations-guide.md`. `.gitea/workflows/verify.yml`, `docs/operations-guide.md`.
Copy the configs from `.agents/templates/config/` — they are written for this Copy the configs from `.agents/templates/config/` — they are written for this
project (correct ignores for `hands-on/`, `submitted-skills/`, `vote-service/`). project (correct ignores for `hands-on/`, `submitted-skills/`, `vote-service/`).
## Steps ## Steps
1. `npm create astro@latest` into the worktree — minimal template, TypeScript 1. `pnpm create astro@latest` into the worktree — minimal template, TypeScript
strict, **no UI framework, no CSS framework**. strict, **no UI framework, no CSS framework**.
2. Set `base: '/ai-for-dummies'`. Every internal link goes through 2. Set `base: '/ai-for-dummies'`. Every internal link goes through
`import.meta.env.BASE_URL` from here on. `import.meta.env.BASE_URL` from here on.
@@ -28,22 +29,25 @@ project (correct ignores for `hands-on/`, `submitted-skills/`, `vote-service/`).
4. Merge `.agents/templates/config/package.scripts.json` into `package.json`. 4. Merge `.agents/templates/config/package.scripts.json` into `package.json`.
**`"prepare": "husky"` is what makes hooks exist** — without it every hook is **`"prepare": "husky"` is what makes hooks exist** — without it every hook is
inert. inert.
5. `npm install husky lint-staged prettier eslint stylelint …`, then `npx husky init`. 5. `pnpm install husky lint-staged prettier eslint stylelint …`, then
`pnpm exec husky init`.
6. Copy the three hooks and the lint configs into place. Verify with 6. Copy the three hooks and the lint configs into place. Verify with
`.agents/scripts/verify-hooks.sh`. `.agents/scripts/verify-hooks.sh`.
7. Migrate **one** page (`summary/` — smallest, zero JS) as a smoke test. 7. Migrate **one** page (`summary/` — smallest, zero JS) as a smoke test.
8. Copy `hands-on/` into `public/` (task 17 does this properly; a stub is fine here). 8. Copy `hands-on/` into `public/` (task 17 does this properly; a stub is fine
9. **Make the publishing decision** per `.agents/context/publishing.md`. Recommended: here).
Gitea Actions builds `dist/` and pushes `pages`. Copy 9. **Make the publishing decision** per `.agents/context/publishing.md`.
Recommended: Gitea Actions builds `dist/` and pushes `pages`. Copy
`.agents/templates/config/gitea-ci.yaml` to `.gitea/workflows/verify.yml`. `.agents/templates/config/gitea-ci.yaml` to `.gitea/workflows/verify.yml`.
10. Rewrite the publishing section of `docs/operations-guide.md` to match. 10. Rewrite the publishing section of `docs/operations-guide.md` to match.
## Done when ## Done when
- [ ] `npm run build` succeeds; `npm run gate` passes - [ ] `pnpm run build` succeeds; `pnpm run gate` passes
- [ ] `.agents/scripts/verify-hooks.sh` reports hooks live - [ ] `.agents/scripts/verify-hooks.sh` reports hooks live
- [ ] A bad commit message is rejected; a raw hex in a `.astro` file is rejected - [ ] A bad commit message is rejected; a raw hex in a `.astro` file is rejected
- [ ] `/ai-for-dummies/summary/` serves correctly **from the real host**, not just preview - [ ] `/ai-for-dummies/summary/` serves correctly **from the real host**, not
just preview
- [ ] `docs/operations-guide.md` describes the actual publishing path - [ ] `docs/operations-guide.md` describes the actual publishing path
## Do not ## Do not
@@ -54,6 +58,6 @@ project (correct ignores for `hands-on/`, `submitted-skills/`, `vote-service/`).
## Watch for ## Watch for
The base path is the #1 production-only failure in this migration. `npm run The base path is the #1 production-only failure in this migration.
preview` will lie to you. Deploy the smoke-test page and curl it with a `pnpm run preview` will lie to you. Deploy the smoke-test page and curl it with
`?v=<sha>` cache-buster. a `?v=<sha>` cache-buster.
@@ -1,8 +1,8 @@
# Task 03 — Verification net # Task 03 — Verification net
**Agent**: `verification-engineer` · **Model**: Codex **Agent**: `verification-engineer` · **Model**: Codex **Depends on**: 01 ·
**Depends on**: 01 · **Parallel with**: 02, 04 · **Blocks**: 1216 **Parallel with**: 02, 04 · **Blocks**: 1216 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 03 verification-net` `.agents/scripts/worktree.sh start 03 verification-net`
## Goal ## Goal
@@ -14,11 +14,12 @@ This task is on the critical path. Do it early.
## Scope ## Scope
`.agents/snapshots/`, `scripts/audit-ui.mjs`, `.agents/scripts/visual-regression.mjs`. `.agents/snapshots/`, `scripts/audit-ui.mjs`,
`.agents/scripts/visual-regression.mjs`.
## Steps ## Steps
1. `npm run serve` against the **current, unmigrated** site. 1. `pnpm run serve` against the **current, unmigrated** site.
2. Snapshot all ten routes: 2. Snapshot all ten routes:
```bash ```bash
for r in "" full-guide summary models agents skills rules skills-review \ for r in "" full-guide summary models agents skills rules skills-review \
@@ -38,10 +39,12 @@ This task is on the critical path. Do it early.
## Done when ## Done when
- [ ] Ten committed snapshots, each non-empty and containing that page's real prose - [ ] Ten committed snapshots, each non-empty and containing that page's real
prose
- [ ] `visual-regression.mjs` captures 10 routes × 4 widths - [ ] `visual-regression.mjs` captures 10 routes × 4 widths
- [ ] Extended `audit-ui.mjs` **fails** on today's `styles.css` (prove it catches the real bug), then the dead rule is removed by task 02 - [ ] Extended `audit-ui.mjs` **fails** on today's `styles.css` (prove it
- [ ] `npm run gate` green catches the real bug), then the dead rule is removed by task 02
- [ ] `pnpm run gate` green
## Do not ## Do not
+4 -4
View File
@@ -1,9 +1,9 @@
# Task 07 — Primitives # Task 07 — Primitives
**Agent**: `component-builder` · **Model**: MiniMax-M3 — **use this task to **Agent**: `component-builder` · **Model**: MiniMax-M3 — **use this task to
calibrate the model routing before fanning out** calibrate the model routing before fanning out** **Depends on**: 02 ·
**Depends on**: 02 · **Blocks**: 0811 **Blocks**: 0811 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 07 primitives` `.agents/scripts/worktree.sh start 07 primitives`
## Goal ## Goal
@@ -36,7 +36,7 @@ you have found two components, not one.
- [ ] `.agents/checklists/before-component.md` complete for all four - [ ] `.agents/checklists/before-component.md` complete for all four
- [ ] Rendered output visually identical to the CSS classes they replace - [ ] Rendered output visually identical to the CSS classes they replace
- [ ] `npm run gate` green - [ ] `pnpm run gate` green
- [ ] **Routing note written**: how many iterations, what the model got wrong. - [ ] **Routing note written**: how many iterations, what the model got wrong.
This decides whether 0811 run on M3 or move to Codex. This decides whether 0811 run on M3 or move to Codex.
+6 -5
View File
@@ -1,8 +1,8 @@
# Task 08 — Route cards and grid group # Task 08 — Route cards and grid group
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Depends on**: 07 · **Parallel with**: 09, 10, 11 · **Blocks**: 12 **Parallel with**: 09, 10, 11 · **Blocks**: 12 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 08 route-cards` `.agents/scripts/worktree.sh start 08 route-cards`
## Goal ## Goal
@@ -10,7 +10,8 @@ The landing page's six chapter cards become one component over data.
## Scope ## Scope
`src/components/blocks/RouteCard.astro`, `src/components/blocks/GridGroup.astro`. `src/components/blocks/RouteCard.astro`,
`src/components/blocks/GridGroup.astro`.
## Source ## Source
@@ -30,7 +31,7 @@ title, summary, and href. Textbook extraction. Styles in `landing.css` +
- [ ] Six cards render identically to `index.html` today - [ ] Six cards render identically to `index.html` today
- [ ] Screenshots at 560/800/1100/1600 match - [ ] Screenshots at 560/800/1100/1600 match
- [ ] Zero JS - [ ] Zero JS
- [ ] `.agents/checklists/before-component.md` complete; `npm run gate` green - [ ] `.agents/checklists/before-component.md` complete; `pnpm run gate` green
## Do not ## Do not
@@ -1,8 +1,8 @@
# Task 09 — Chapter blocks # Task 09 — Chapter blocks
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Depends on**: 07 · **Parallel with**: 08, 10, 11 · **Blocks**: 13, 14 **Parallel with**: 08, 10, 11 · **Blocks**: 13, 14 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 09 chapter-blocks` `.agents/scripts/worktree.sh start 09 chapter-blocks`
## Goal ## Goal
@@ -23,8 +23,8 @@ is the component.
## Watch for ## Watch for
- `TopBar` carries `aria-current="page"`. Preserve it; it is the only - `TopBar` carries `aria-current="page"`. Preserve it; it is the only indication
indication of location for assistive tech. of location for assistive tech.
- The tables in `/models/` and `/rules/` scroll horizontally on narrow screens - The tables in `/models/` and `/rules/` scroll horizontally on narrow screens
(`overflow-x:auto`, `min-width` on the inner element). Keep that — dropping it (`overflow-x:auto`, `min-width` on the inner element). Keep that — dropping it
makes the tables unreadable on a phone, and `audit-ui.mjs` asserts related makes the tables unreadable on a phone, and `audit-ui.mjs` asserts related
@@ -35,4 +35,4 @@ is the component.
- [ ] All five pages' markup expressible with these components - [ ] All five pages' markup expressible with these components
- [ ] Rendered text identical to current pages - [ ] Rendered text identical to current pages
- [ ] Zero JS - [ ] Zero JS
- [ ] Checklist complete; `npm run gate` green - [ ] Checklist complete; `pnpm run gate` green
+6 -5
View File
@@ -1,8 +1,8 @@
# Task 10 — Full-guide blocks # Task 10 — Full-guide blocks
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Depends on**: 07 · **Parallel with**: 08, 09, 11 · **Blocks**: 15 **Parallel with**: 08, 09, 11 · **Blocks**: 15 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 10 guide-blocks` `.agents/scripts/worktree.sh start 10 guide-blocks`
## Goal ## Goal
@@ -25,7 +25,8 @@ Task 15 wires the interactivity.
`data-tree="main|ui"`, `data-worker="ui"`, `data-route="plan"`, `data-tree="main|ui"`, `data-worker="ui"`, `data-route="plan"`,
`data-model-provider="openai|claude|gemini"`, `data-effort="low|medium|high"`, `data-model-provider="openai|claude|gemini"`, `data-effort="low|medium|high"`,
`data-skill-file="skill"`, `data-skill-step="observe|validate"`, `data-skill-file="skill"`, `data-skill-step="observe|validate"`,
`data-common-skill="ponytail|caveman|unlazy"`, `role="tablist"`, `id="hands-on"`. `data-common-skill="ponytail|caveman|unlazy"`, `role="tablist"`,
`id="hands-on"`.
Each is a real feature hook, not decoration. Losing one is losing a feature. Each is a real feature hook, not decoration. Losing one is losing a feature.
@@ -41,4 +42,4 @@ screenshots agree.
- [ ] Every `data-*` hook above present in rendered output - [ ] Every `data-*` hook above present in rendered output
- [ ] Components take props and render static markup; **no `client:*` yet** - [ ] Components take props and render static markup; **no `client:*` yet**
- [ ] Screenshots match the current guide at four widths - [ ] Screenshots match the current guide at four widths
- [ ] Checklist complete; `npm run gate` green - [ ] Checklist complete; `pnpm run gate` green
@@ -1,8 +1,8 @@
# Task 11 — Review-desk blocks # Task 11 — Review-desk blocks
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Depends on**: 07 · **Parallel with**: 08, 09, 10 · **Blocks**: 16 **Parallel with**: 08, 09, 10 · **Blocks**: 16 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 11 review-blocks` `.agents/scripts/worktree.sh start 11 review-blocks`
## Goal ## Goal
@@ -46,4 +46,4 @@ assistive tech. Colour alone is not enough. It is also asserted.
- [ ] All listed CSS hooks present - [ ] All listed CSS hooks present
- [ ] `aria-pressed`, `role="group"`, `aria-label` preserved - [ ] `aria-pressed`, `role="group"`, `aria-label` preserved
- [ ] Static render matches current desk at four widths - [ ] Static render matches current desk at four widths
- [ ] Checklist complete; `npm run gate` green - [ ] Checklist complete; `pnpm run gate` green
+4 -4
View File
@@ -1,8 +1,8 @@
# Task 12 — Landing page # Task 12 — Landing page
**Agent**: `page-migrator` · **Model**: MiniMax-M3 **Agent**: `page-migrator` · **Model**: MiniMax-M3 **Depends on**: 03, 08 ·
**Depends on**: 03, 08 · **Parallel with**: 13, 14, 17 **Parallel with**: 13, 14, 17 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 12 page-landing` `.agents/scripts/worktree.sh start 12 page-landing`
## Goal ## Goal
@@ -27,4 +27,4 @@ Snapshot first (task 03 baseline exists — diff against it). Then compose from
- [ ] `diff .agents/snapshots/index.txt <(snapshot of dist)` empty - [ ] `diff .agents/snapshots/index.txt <(snapshot of dist)` empty
- [ ] Screenshots match at four widths - [ ] Screenshots match at four widths
- [ ] Built page ships **0 bytes** of JS - [ ] Built page ships **0 bytes** of JS
- [ ] `.agents/checklists/before-page.md` complete; `npm run gate` green - [ ] `.agents/checklists/before-page.md` complete; `pnpm run gate` green
@@ -1,8 +1,8 @@
# Task 13 — Chapter pages ×4 # Task 13 — Chapter pages ×4
**Agent**: `page-migrator` · **Model**: MiniMax-M3 **Agent**: `page-migrator` · **Model**: MiniMax-M3 **Depends on**: 03, 09 ·
**Depends on**: 03, 09 · **Parallel with**: 12, 14, 17 · **Blocks**: 15, 16 **Parallel with**: 12, 14, 17 · **Blocks**: 15, 16 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 13 page-chapters` `.agents/scripts/worktree.sh start 13 page-chapters`
## Goal ## Goal
@@ -25,7 +25,7 @@ Blocks 15 and 16 because those pages link here and share the layout.
- [ ] Four snapshot diffs empty - [ ] Four snapshot diffs empty
- [ ] Three pages ship 0 bytes JS; `/skills/` ships only its island - [ ] Three pages ship 0 bytes JS; `/skills/` ships only its island
- [ ] Screenshots match at four widths - [ ] Screenshots match at four widths
- [ ] Checklist complete for each page; `npm run gate` green - [ ] Checklist complete for each page; `pnpm run gate` green
## Do not ## Do not
+5 -5
View File
@@ -1,8 +1,8 @@
# Task 14 — Rules page # Task 14 — Rules page
**Agent**: `page-migrator` · **Model**: MiniMax-M3 **Agent**: `page-migrator` · **Model**: MiniMax-M3 **Depends on**: 03, 09 ·
**Depends on**: 03, 09 · **Parallel with**: 12, 13, 17 **Parallel with**: 12, 13, 17 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 14 page-rules` `.agents/scripts/worktree.sh start 14 page-rules`
## Goal ## Goal
@@ -24,10 +24,10 @@ deleted. Screenshot at the old widths (900, 600, 2200) to prove equivalence.
## Watch for ## Watch for
`rules/styles.css` shares `--accent` and `--deep` with `styles.css` — it is on `rules/styles.css` shares `--accent` and `--deep` with `styles.css` — it is on
the *first* palette. Confirm task 02 canonicalized it the same way. the _first_ palette. Confirm task 02 canonicalized it the same way.
## Done when ## Done when
- [ ] Snapshot diff empty; bilingual toggle works, `<html lang>` follows - [ ] Snapshot diff empty; bilingual toggle works, `<html lang>` follows
- [ ] Responsive behaviour identical at 600/900/2200 - [ ] Responsive behaviour identical at 600/900/2200
- [ ] No external dependency; checklist complete; `npm run gate` green - [ ] No external dependency; checklist complete; `pnpm run gate` green
+12 -10
View File
@@ -1,8 +1,8 @@
# Task 15 — Full guide # Task 15 — Full guide
**Agent**: `page-migrator` · **Model**: **Codex** — hardest task in the plan **Agent**: `page-migrator` · **Model**: **Codex** — hardest task in the plan
**Depends on**: 05, 10, 13 · **Parallel with**: 16 **Depends on**: 05, 10, 13 · **Parallel with**: 16 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 15 page-full-guide` `.agents/scripts/worktree.sh start 15 page-full-guide`
## Goal ## Goal
@@ -21,7 +21,7 @@ toggle.
Only these hydrate. Everything else is server-rendered. Only these hydrate. Everything else is server-rendered.
| Island | Directive | Why | | Island | Directive | Why |
| --- | --- | --- | | -------------------------------------------------------- | ---------------- | -------------------------------------------------- |
| Phase tabs | `client:visible` | click-driven panel swap | | Phase tabs | `client:visible` | click-driven panel swap |
| Tree / worker / route / model / effort / skill selectors | `client:visible` | same pattern; consider one generic selector island | | Tree / worker / route / model / effort / skill selectors | `client:visible` | same pattern; consider one generic selector island |
| Language toggle | `client:idle` | page-wide, not urgent | | Language toggle | `client:idle` | page-wide, not urgent |
@@ -32,10 +32,11 @@ one selector component with different data.
## Asserted by verify.mjs — all must survive ## Asserted by verify.mjs — all must survive
`const phases`, `const handsOnPrompts`, `const modelGuide`, `const skillSources`, `const phases`, `const handsOnPrompts`, `const modelGuide`,
`const skillInstallPrompts`, `render('plan')`, `renderTree`, `renderWorker`, `const skillSources`, `const skillInstallPrompts`, `render('plan')`,
`renderRoute`, `renderModelProvider`, `renderEffort`, `renderSkillFile`, `renderTree`, `renderWorker`, `renderRoute`, `renderModelProvider`,
`renderSkillWorkflow`, `renderCommonSkill`, `renderHandsOn`, `copyPrompt`. `renderEffort`, `renderSkillFile`, `renderSkillWorkflow`, `renderCommonSkill`,
`renderHandsOn`, `copyPrompt`.
These are **implementation-detail assertions** — they look deletable and are These are **implementation-detail assertions** — they look deletable and are
not. Each pins a feature. Coordinate with task 19 to replace each with an not. Each pins a feature. Coordinate with task 19 to replace each with an
@@ -48,8 +49,8 @@ Also: `data-copy-target="prompt-install-skills|prompt-basic|prompt-skills"`,
- `copyPrompt` uses `navigator.clipboard` with a `document.execCommand` - `copyPrompt` uses `navigator.clipboard` with a `document.execCommand`
fallback. Keep both — the fallback exists for non-secure contexts. fallback. Keep both — the fallback exists for non-secure contexts.
- The hands-on prompt strings are copy-pasted by attendees into an agent. - The hands-on prompt strings are copy-pasted by attendees into an agent. Exact
Exact whitespace and line breaks matter. whitespace and line breaks matter.
- `responsive.css` (30 KB) mostly serves this page. Port what is needed, prove - `responsive.css` (30 KB) mostly serves this page. Port what is needed, prove
the rest dead, delete it. Screenshots are the proof. the rest dead, delete it. Screenshots are the proof.
@@ -59,4 +60,5 @@ Also: `data-copy-target="prompt-install-skills|prompt-basic|prompt-skills"`,
- [ ] Every interaction works: all tabs, both languages, all copy buttons - [ ] Every interaction works: all tabs, both languages, all copy buttons
- [ ] Keyboard: arrow keys move between tabs; focus visible throughout - [ ] Keyboard: arrow keys move between tabs; focus visible throughout
- [ ] JS payload **smaller** than today's 50 KB (content is now static) - [ ] JS payload **smaller** than today's 50 KB (content is now static)
- [ ] Screenshots match at four widths; checklist complete; `npm run gate` green - [ ] Screenshots match at four widths; checklist complete; `pnpm run gate`
green
@@ -1,14 +1,14 @@
# Task 16 — Review desk # Task 16 — Review desk
**Agent**: `page-migrator` · **Model**: **Codex** — highest defect risk **Agent**: `page-migrator` · **Model**: **Codex** — highest defect risk
**Depends on**: 06, 11, 13 · **Parallel with**: 15 **Depends on**: 06, 11, 13 · **Parallel with**: 15 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 16 page-review-desk` `.agents/scripts/worktree.sh start 16 page-review-desk`
## Goal ## Goal
`/skills-review/` → Astro. The most interactive page: search, filtering, `/skills-review/` → Astro. The most interactive page: search, filtering, lazy
lazy file fetching, markdown rendering, a client-side diff view, six URL params, file fetching, markdown rendering, a client-side diff view, six URL params, and
and a live API call to `vote-service/`. a live API call to `vote-service/`.
## The six query params are a public contract ## The six query params are a public contract
@@ -44,7 +44,8 @@ Interaction tokens: `from './catalog.js'`, `from './files.js'`, `renderList`,
`markdownMarkup`, `syncUrl`, `selectFromUrl`, `URLSearchParams`, `markdownMarkup`, `syncUrl`, `selectFromUrl`, `URLSearchParams`,
`navigator.clipboard`, `document.execCommand`. `navigator.clipboard`, `document.execCommand`.
Plus the catalog count: `id:'` occurrences across both catalogs **must equal 24**. Plus the catalog count: `id:'` occurrences across both catalogs **must equal
24**.
Same rule as task 15 — re-point with task 19, never delete. Same rule as task 15 — re-point with task 19, never delete.
@@ -60,4 +61,4 @@ needs raw source text**, not rendered HTML. Keep both available.
- [ ] Search, filter, file tabs, preview, change lens, download, copy all work - [ ] Search, filter, file tabs, preview, change lens, download, copy all work
- [ ] Vote widget reaches the live API; CORS preflight succeeds - [ ] Vote widget reaches the live API; CORS preflight succeeds
- [ ] Snapshot diff empty; screenshots match; checklist complete - [ ] Snapshot diff empty; screenshots match; checklist complete
- [ ] `npm run gate` green - [ ] `pnpm run gate` green
+5 -4
View File
@@ -1,8 +1,8 @@
# Task 18 — Motion pass # Task 18 — Motion pass
**Agent**: `motion-designer` · **Model**: Gemini (perceptual judgement) **Agent**: `motion-designer` · **Model**: Gemini (perceptual judgement)
**Depends on**: 15, 16 · **Parallel with**: 19 **Depends on**: 15, 16 · **Parallel with**: 19 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 18 motion` `.agents/scripts/worktree.sh start 18 motion`
## Goal ## Goal
@@ -13,7 +13,8 @@ Audit the motion that exists, add only motion that earns its place, and make
The audit is worth more than the additions. The audit is worth more than the additions.
1. Inventory every `transition`, `animation`, `@keyframes`, `transform` in `src/`. 1. Inventory every `transition`, `animation`, `@keyframes`, `transform` in
`src/`.
2. Flag anything animating a layout property (`width`, `height`, `top`, `left`, 2. Flag anything animating a layout property (`width`, `height`, `top`, `left`,
`margin`). Those force reflow every frame and fail the 200ms INP budget. The `margin`). Those force reflow every frame and fail the 200ms INP budget. The
Stylelint config already blocks new ones; find the ported ones. Stylelint config already blocks new ones; find the ported ones.
@@ -44,4 +45,4 @@ query params, and reduced motion. If any fails, do not ship it.
- [ ] Zero animations on layout properties - [ ] Zero animations on layout properties
- [ ] Every animation has a one-line written purpose - [ ] Every animation has a one-line written purpose
- [ ] Reduced-motion tested under emulation; end states correct - [ ] Reduced-motion tested under emulation; end states correct
- [ ] Before/after captures attached; `npm run gate` green - [ ] Before/after captures attached; `pnpm run gate` green
@@ -1,8 +1,8 @@
# Task 19 — Re-point the verification contract # Task 19 — Re-point the verification contract
**Agent**: `verification-engineer` · **Model**: Codex **Agent**: `verification-engineer` · **Model**: Codex **Depends on**: 15, 16 ·
**Depends on**: 15, 16 · **Parallel with**: 18 **Parallel with**: 18 **Worktree**:
**Worktree**: `.agents/scripts/worktree.sh start 19 verify-repoint` `.agents/scripts/worktree.sh start 19 verify-repoint`
## Goal ## Goal
@@ -15,27 +15,28 @@ a written reason.
## The three kinds ## The three kinds
| Kind | Example | What to do | | Kind | Example | What to do |
| --- | --- | --- | | --------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Content presence | `data-phase="plan"` | re-point at `dist/full-guide/index.html`; the token should survive rendering. If it does not, a component dropped content — **stop and report** | | Content presence | `data-phase="plan"` | re-point at `dist/full-guide/index.html`; the token should survive rendering. If it does not, a component dropped content — **stop and report** |
| Implementation detail | `const phases`, `renderTree`, `from './catalog.js'` | obsolete as written, but each pins a **feature**. Replace with an output-level assertion of that feature. Never drop | | Implementation detail | `const phases`, `renderTree`, `from './catalog.js'` | obsolete as written, but each pins a **feature**. Replace with an output-level assertion of that feature. Never drop |
| Asset version | `app.js?v=20260904-vote-widget` | Astro hashes assets — assert the built HTML references a hashed asset | | Asset version | `app.js?v=20260904-vote-widget` | Astro hashes assets — assert the built HTML references a hashed asset |
## Steps ## Steps
1. `npm run build`, then re-point `read()` calls at `dist/`. 1. `pnpm run build`, then re-point `read()` calls at `dist/`.
2. Work through all 42 in order. For each: does the fact it pins still exist? 2. Work through all 42 in order. For each: does the fact it pins still exist?
Yes → re-point. No → content was lost; escalate. Yes → re-point. No → content was lost; escalate.
3. Add rendered-text snapshot assertions for all ten routes so this class of 3. Add rendered-text snapshot assertions for all ten routes so this class of
regression is caught structurally, not by string luck. regression is caught structurally, not by string luck.
4. Confirm `check-tokens.mjs` and the extended `audit-ui.mjs` are in 4. Confirm `check-tokens.mjs` and the extended `audit-ui.mjs` are in
`npm run verify`. `pnpm run verify`.
## Done when ## Done when
- [ ] `grep -c 'throw new Error' scripts/verify.mjs` ≥ the `origin/main` baseline - [ ] `grep -c 'throw new Error' scripts/verify.mjs` ≥ the `origin/main`
baseline
- [ ] Every removal has a one-line reason in this file - [ ] Every removal has a one-line reason in this file
- [ ] Snapshot assertions cover all ten routes - [ ] Snapshot assertions cover all ten routes
- [ ] `npm run gate` green, and it **fails** when you deliberately delete a - [ ] `pnpm run gate` green, and it **fails** when you deliberately delete a
paragraph from a component (prove the net works, then revert) paragraph from a component (prove the net works, then revert)
## Do not ## Do not
+7 -6
View File
@@ -1,7 +1,8 @@
# Task 20 — Cutover and cleanup # Task 20 — Cutover and cleanup
**Agent**: `astro-architect`, **with a human watching** · **Model**: MiniMax-M3 assisting **Agent**: `astro-architect`, **with a human watching** · **Model**: MiniMax-M3
**Depends on**: all · **Worktree**: `.agents/scripts/worktree.sh start 20 cutover` assisting **Depends on**: all · **Worktree**:
`.agents/scripts/worktree.sh start 20 cutover`
This task touches production publishing. Do not run it unattended. This task touches production publishing. Do not run it unattended.
@@ -18,9 +19,9 @@ describe reality.
`app.js`, `styles.css`, `responsive.css`, `chapters.css`, `landing.css`, `app.js`, `styles.css`, `responsive.css`, `chapters.css`, `landing.css`,
`rules/app.js`, `rules/styles.css`, `skills/app.js`, `skills/styles.css`, `rules/app.js`, `rules/styles.css`, `skills/app.js`, `skills/styles.css`,
`skills-review/*.js`, `skills-review/*.css`, and the ten old `index.html` `skills-review/*.js`, `skills-review/*.css`, and the ten old `index.html`
files. `git rm`, one commit, reviewable. files. `git rm`, one commit, reviewable. **Keep**: `hands-on/**` (now under
**Keep**: `hands-on/**` (now under `public/`), `submitted-skills/**`, `public/`), `submitted-skills/**`, `skill-reviews/**`, `docs/**`,
`skill-reviews/**`, `docs/**`, `vote-service/**`. `vote-service/**`.
3. **Publish** via the mechanism chosen in task 01. 3. **Publish** via the mechanism chosen in task 01.
4. **Verify on the real host** with a cache-buster: 4. **Verify on the real host** with a cache-buster:
```bash ```bash
@@ -53,6 +54,6 @@ git rev-parse origin/pages # write it down
- [ ] Ten routes 200 on the real host with a fresh cache-buster - [ ] Ten routes 200 on the real host with a fresh cache-buster
- [ ] Vote widget works end-to-end from the published origin (CORS is - [ ] Vote widget works end-to-end from the published origin (CORS is
origin-sensitive — `ALLOWED_ORIGIN` must still match) origin-sensitive — `ALLOWED_ORIGIN` must still match)
- [ ] Old files deleted; `npm run gate` green - [ ] Old files deleted; `pnpm run gate` green
- [ ] Docs match reality - [ ] Docs match reality
- [ ] Previous `pages` SHA recorded for rollback - [ ] Previous `pages` SHA recorded for rollback
+5366
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
# pnpm 11 no longer reads the "pnpm" field in package.json; every setting that
# used to live there (and npm's top-level "overrides") belongs here instead.
# See https://pnpm.io/settings
# Was npm's top-level "overrides". These pins are load-bearing: dropping them
# silently changes the resolved vite/language-server versions.
overrides:
'@astrojs/language-server': 2.15.0
defu: 6.1.7
vite: 6.2.1
# pnpm blocks dependency postinstall scripts by default. Astro's build needs
# both of these: esbuild verifies its platform binary, sharp its libvips
# prebuild. Anything not listed here stays blocked, which is the point.
allowBuilds:
esbuild: true
sharp: true