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
Each agent has one responsibility, one set of rules, and its own worktree.
Full definitions in [`agents/`](agents/).
Each agent has one responsibility, one set of rules, and its own worktree. Full
definitions in [`agents/`](agents/).
| Agent | Owns | Loads skills |
| --- | --- | --- |
| ---------------------------------------------------------- | --------------------------------------------------- | -------------------------------------- |
| [`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` |
| [`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
1. **One task, one worktree, one agent.** See [`rules/git-worktrees.md`](rules/git-worktrees.md).
2. **Read `context/` first.** Especially `design-system.md` and `verification.md`.
Most wrong answers here come from assuming the CSS is already coherent.
1. **One task, one worktree, one agent.** See
[`rules/git-worktrees.md`](rules/git-worktrees.md).
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/`
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.
5. **Report what you did not do.** Partial work with an honest boundary is
useful; silent narrowing is not.
@@ -58,7 +60,7 @@ agent loads .agents/agents/<role>.md + its skills
checklists/before-*.md ← self-gate
npm run verify ← hard gate
pnpm run verify ← hard gate
reviewer agent on the diff ← merge gate
```
+15 -9
View File
@@ -1,14 +1,18 @@
---
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
---
You own the foundation. Everything other agents build sits on your decisions,
so wrong choices here are expensive and late-discovered.
You own the foundation. Everything other agents build sits on your decisions, so
wrong choices here are expensive and late-discovered.
**Read first**: `.agents/context/architecture.md`, `.agents/context/publishing.md`,
`.agents/rules/astro.md`. **Load skill**: `astro-page`.
**Read first**: `.agents/context/architecture.md`,
`.agents/context/publishing.md`, `.agents/rules/astro.md`. **Load skill**:
`astro-page`.
## You own
@@ -19,11 +23,13 @@ writer of these. Other agents report problems with them; they do not edit.
## Non-negotiable outcomes
- `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.
- Zero JS by default. Astro ships none unless a component asks.
- `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.
## 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
`npm 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
`pnpm run build` succeeds, one migrated page serves correctly from the real host
under `/ai-for-dummies/`, `pnpm run verify` and `node scripts/audit-ui.mjs` are
green, and the operations guide matches reality.
+5 -2
View File
@@ -1,6 +1,9 @@
---
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
---
@@ -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
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
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
---
You own `src/content/` and `src/content/config.ts`, and you are their only
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
~50 `{ en, pt }` keys from `app.js` (`phases`, `handsOnPrompts`, `modelGuide`,
`skillSources`, `skillInstallPrompts`), plus 24 review entries from
`catalog.js` and `submitted-catalog.js`.
`skillSources`, `skillInstallPrompts`), plus 24 review entries from `catalog.js`
and `submitted-catalog.js`.
These are hand-written translations with deliberate tone. **Copy them
mechanically. Never retype.** Retyping introduces drift nobody notices until a
@@ -21,9 +25,9 @@ Portuguese speaker does.
## Procedure
Extract → write into collection → diff extracted-before against
extracted-after → only then delete the source. If the diff is not empty, you
changed content. Fix it before continuing.
Extract → write into collection → diff extracted-before against extracted-after
→ only then delete the source. If the diff is not empty, you changed content.
Fix it before continuing.
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
@@ -35,11 +39,12 @@ monolingual.
`scripts/build-skill-review.mjs` and the output is **committed**. Move
`catalog.js` and the generator keeps running against nothing — silently. Either
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**.
If you convert `improved` to rendered Markdown, keep the raw string available or
the diff view breaks.
Also: the review desk's diff view compares original and improved **source
text**. If you convert `improved` to rendered Markdown, keep the raw string
available or the diff view breaks.
## The language-switching decision
@@ -50,4 +55,4 @@ a decision, record it. Either way `<html lang>` tracks the active language.
## Done when
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
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
---
@@ -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
visibly different — screenshot both and get a human decision.
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,
not a refactor. Default: delete the dead rule, declare the stacks that
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
`.agents/scripts/check-tokens.mjs`.
Deliver: one value per token, a named type scale (`--step-*`) replacing 14 ad-hoc
`clamp()` triples, five named breakpoints replacing sixteen, and an enforcement
script wired into `npm run verify`.
Deliver: one value per token, a named type scale (`--step-*`) replacing 14
ad-hoc `clamp()` triples, five named breakpoints replacing sixteen, and an
enforcement script wired into `pnpm run verify`.
## Preserve the house style
+8 -4
View File
@@ -1,13 +1,17 @@
---
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
---
You migrate **one page per task**. The bar is that a visitor cannot tell.
**Read first**: `.agents/context/architecture.md`, `.agents/context/verification.md`.
**Load skills**: `astro-page`, `content-migration`, `visual-regression`.
**Read first**: `.agents/context/architecture.md`,
`.agents/context/verification.md`. **Load skills**: `astro-page`,
`content-migration`, `visual-regression`.
## Snapshot before you touch anything
@@ -38,5 +42,5 @@ them, it is wrong — stop and report.
## Done when
`.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.
+7 -4
View File
@@ -1,6 +1,9 @@
---
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
---
@@ -45,6 +48,6 @@ this "dependency-free" site. Add `@import`, `src: url(https:…)`, and
## Done when
Coverage has not fallen, every removal has a reason, snapshots exist for all
ten routes, `check-tokens.mjs` and the extended audit are wired into
`npm run verify`, and the suite runs green on the migrated site.
Coverage has not fallen, every removal has a reason, snapshots exist for all ten
routes, `check-tokens.mjs` and the extended audit are wired into
`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
- [ ] 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/`
- [ ] Typed `interface Props`; every field intentional; no `any`
- [ ] **No raw hex, px font sizes, or ad-hoc breakpoints** — tokens only
@@ -8,8 +9,10 @@
- [ ] Markup under ~120 lines
- [ ] Native elements: `<button>` for actions, `<a>` for navigation
- [ ] Keyboard: tab to it, operate with Enter/Space, focus ring visible
- [ ] Interactive state exposed via ARIA (`aria-pressed`, `aria-current`), not just colour
- [ ] Any motion respects `prefers-reduced-motion` and animates only `transform`/`opacity`
- [ ] Interactive state exposed via ARIA (`aria-pressed`, `aria-current`), not
just colour
- [ ] Any motion respects `prefers-reduced-motion` and animates only
`transform`/`opacity`
- [ ] Renders correctly at 560 / 800 / 1100 / 1600 px
- [ ] 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
- [ ] Rebased on current `origin/main`, conflicts resolved in the worktree
- [ ] `npm 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`)
- [ ] Only files in your task's scope changed — `git diff --stat origin/main` matches the brief
- [ ] `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`)
- [ ] 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 `.serena/`, `__pycache__/`, `dist/`, or scratch files staged
- [ ] 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
- [ ] URL identical to the old one, **trailing slash included**
- [ ] Any query params the old page honoured still work (`?author=`, `?skill=`, `?view=`, `?file=`, `?compare=`, `?render=`)
- [ ] Rendered text content diffs clean against the pre-migration snapshot — or 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
- [ ] Any query params the old page honoured still work (`?author=`, `?skill=`,
`?view=`, `?file=`, `?compare=`, `?render=`)
- [ ] Rendered text content diffs clean against the pre-migration snapshot — or
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
- [ ] Every ARIA attribute from the original survived (`verify.mjs` asserts several)
- [ ] No external `<script>`, `<link>`, `@import`, or `url()``node scripts/audit-ui.mjs` green
- [ ] Every ARIA attribute from the original survived (`verify.mjs` asserts
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
- [ ] Screenshots at 560 / 800 / 1100 / 1600 px compared against the old page
- [ ] `<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/`.
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
@@ -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:
| 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. |
| **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.** |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| **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``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 |
**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/`.
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
+5 -5
View File
@@ -1,8 +1,8 @@
# Context: the verification contract
`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
them `data-phase="plan"`, `renderTree`, `.change-lens`,
reads 26 source files and asserts that specific **string tokens** appear in them
`data-phase="plan"`, `renderTree`, `.change-lens`,
`styles.css?v=20260904-vote-widget`, and so on.
## 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**,
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
loses a section. **Deleting an assertion is a change that requires review, the
same as deleting a feature.**
@@ -21,9 +21,9 @@ same as deleting a feature.**
Three kinds of assertion, three different fates:
| 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. |
| **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". |
**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.
| Tier | Hook | Scope | Budget | Runs |
| --- | --- | --- | --- | --- |
| ---- | ------------------ | ------------------------------------- | ------- | -------------------- |
| 1 | `pre-commit` | **staged files only** (lint-staged) | < 3s | every commit |
| 2 | `pre-push` | whole project: build + verify + audit | < 90s | every push |
| 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.
An agent that waits 40s per commit will start passing `--no-verify`, and then
you have no gate at all.
Tier 1 must stay under a few seconds. If it creeps, move the check to tier 2. An
agent that waits 40s per commit will start passing `--no-verify`, and then you
have no gate at all.
## 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 + 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}`
## 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
worktrees) so ten agents pushing at once do not run ten concurrent builds and
thrash the machine. Waiters queue; they do not fail.
- `npm ci` in a fresh worktree should use `--prefer-offline` to avoid registry
contention when several spin up at once.
- `pnpm install --frozen-lockfile` in a fresh worktree is cheap: pnpm hardlinks
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
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
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
git worktree add ../af-task-07 -b refactor/task-07-route-cards
cd ../af-task-07
npm ci
pnpm install --frozen-lockfile
```
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
(`tokens.css`, `verify.mjs`, `astro.config.mjs` are contended). Worktrees give
each agent its own working directory over one object store — cheap, and no
agent can see another's half-finished state.
each agent its own working directory over one object store — cheap, and no agent
can see another's half-finished state.
The failure mode without them: two agents both "fix" `verify.mjs`, and the
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:
| File | Owner |
| --- | --- |
| ---------------------------------- | ----------------------- |
| `src/styles/tokens.css` | `design-system-keeper` |
| `scripts/verify.mjs` | `verification-engineer` |
| `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
1. `npm run verify` green — without deleting assertions.
1. `pnpm run verify` green — without deleting assertions.
2. The relevant checklist in [`../checklists/`](../checklists/) complete.
3. `git rebase origin/main` again, resolve conflicts in your worktree.
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 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
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
/* forbidden */
color: #172f42;
color: rgba(23,47,66,.6);
color: rgba(23, 47, 66, 0.6);
/* required */
color: var(--ink);
@@ -23,7 +23,7 @@ color: var(--muted);
```
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
@@ -31,8 +31,8 @@ wire it into `npm run verify`.
`--deep` are the existing vocabulary. Keep it — it is already semantic and the
team reads it fluently. Do not rename to `--color-neutral-900`.
If a genuine second surface is needed, extend semantically
(`--surface-lab`, `--ink-inverse`), never numerically.
If a genuine second surface is needed, extend semantically (`--surface-lab`,
`--ink-inverse`), never numerically.
## Type scale
@@ -56,8 +56,11 @@ repetitions.
Five named widths replace the current sixteen:
```css
--bp-sm: 560px; --bp-md: 800px; --bp-lg: 1100px;
--bp-xl: 1600px; --bp-2xl: 2200px;
--bp-sm: 560px;
--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
@@ -74,5 +77,5 @@ When collapsing a component's old breakpoint onto a named one, screenshot at the
## Fonts
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,
not a refactor. Default: match what renders today.
intended Manrope/DM Mono has never rendered; introducing it is a visual
redesign, not a refactor. Default: match what renders today.
+7 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
// 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
// `npm run verify`.
// `pnpm run verify`.
//
// 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 (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}`;
// 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.
const media = line.match(/@media[^{]*?\(\s*(?:max|min)-width:\s*(\d+px)/);
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.
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
fi
step "types"
npx --no-install astro check
pnpm exec astro check
step "build"
npm run build
pnpm run build
step "content contracts"
npm run verify
pnpm run verify
# The assertion count is the thing agents are most tempted to "fix" downward.
# 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"
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
# is no toolchain to install and no hooks to verify.
if [ -f "$dir/package-lock.json" ]; then
( cd "$dir" && npm ci --prefer-offline && .agents/scripts/verify-hooks.sh )
if [ -f "$dir/pnpm-lock.yaml" ]; then
( cd "$dir" && pnpm install --frozen-lockfile && .agents/scripts/verify-hooks.sh )
fi
prompt=$(cat <<PROMPT
+2 -2
View File
@@ -2,7 +2,7 @@
# Guards the silent-failure mode described in .agents/rules/gates.md.
#
# 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
# nothing and every commit passes unchecked.
#
@@ -43,7 +43,7 @@ fi
if [ "$fail" -ne 0 ]; then
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
fi
+1 -1
View File
@@ -30,7 +30,7 @@ try {
({ chromium } = await import('playwright'));
} catch {
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
# a fresh worktree has hooks configured but absent — every commit would pass
# 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 "worktree : $dir"
+18 -5
View File
@@ -1,6 +1,9 @@
---
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
@@ -38,6 +41,7 @@ interface Props {
}
const { number, title, summary, href } = Astro.props;
---
<article class="card">
<b>{number}</b>
<h2>{title}</h2>
@@ -46,8 +50,16 @@ const { number, title, summary, href } = Astro.props;
</article>
<style>
.card { display: grid; gap: 10px; padding: 22px; background: var(--paper); }
b { color: var(--accent); font: var(--font-eyebrow); }
.card {
display: grid;
gap: 10px;
padding: 22px;
background: var(--paper);
}
b {
color: var(--accent);
font: var(--font-eyebrow);
}
</style>
```
@@ -64,5 +76,6 @@ Rules that bite most often here:
1. Render it at 560 / 800 / 1100 / 1600 px.
2. Tab to it. Focus ring visible (`outline: 3px solid #a7483f`).
3. Diff its rendered text against the markup it replaced.
4. Run [`../../checklists/before-component.md`](../../checklists/before-component.md).
5. `npm run verify` — green, with no assertion deleted.
4. Run
[`../../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
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
@@ -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.
```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/ \
> .agents/snapshots/models.txt
```
@@ -30,7 +33,8 @@ Then, in order:
2. Move copy into `src/content/`. Both `en` and `pt`, copied literally — never
retyped.
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
(`data-phase`, `data-tree`, `data-route`, `data-model-provider`, …).
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
```bash
npm run build
pnpm run build
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
node scripts/audit-ui.mjs
npm run verify
pnpm run verify
```
Then screenshots at the same four widths, and
+8 -4
View File
@@ -1,6 +1,10 @@
---
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
@@ -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
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
copying a bug.
traps. If you have not read that file, you will "preserve the styles" by copying
a bug.
## 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
```
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
+10 -8
View File
@@ -1,6 +1,8 @@
---
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
@@ -31,8 +33,8 @@ with sync_playwright() as p:
browser.close()
```
Run once against the vanilla site (`npm run serve`), once against
`npm run preview`. Keep both sets.
Run once against the vanilla site (`pnpm run serve`), once against
`pnpm run preview`. Keep both sets.
## Compare
@@ -43,8 +45,8 @@ for f in before/*.png; do
done
```
Pixel-exact is not the bar — antialiasing differs. Judge by eye where the
metric is non-trivial, and attach the pair to the task report.
Pixel-exact is not the bar — antialiasing differs. Judge by eye where the metric
is non-trivial, and attach the pair to the task report.
## 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.
Investigate: anything that moves by more than ~2px, any colour change (that is
a token bug), any element that appears or disappears (that is content loss —
stop and check the snapshot diff).
Investigate: anything that moves by more than ~2px, any colour change (that is a
token bug), any element that appears or disappears (that is content loss — stop
and check the snapshot diff).
## Reduced motion
+29 -16
View File
@@ -6,6 +6,14 @@ on:
push:
branches: [main]
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:
gate:
@@ -17,22 +25,27 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm install --package-lock=false --prefer-offline
- run: npm run lint
# Node 25 dropped the bundled corepack, and this self-hosted act-runner
# is not a good place to discover whether it can fetch a third-party
# 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
- name: visual regression
run: |
npx playwright install --with-deps chromium
node .agents/scripts/visual-regression.mjs
- uses: actions/upload-artifact@v4
if: failure()
with:
name: screenshots
path: .agents/snapshots/diff/
# The visual-regression step is intentionally absent. `visual-regression.mjs`
# only *captures* baselines — it has no compare mode — so in CI it would
# overwrite .agents/snapshots/before/ and pass unconditionally. It also
# imports `playwright`, which is not a dependency of this project. Wire it
# back in once it can actually diff. See task 03's report.
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
runs-on: ubuntu-latest
steps:
@@ -40,9 +53,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm install --package-lock=false --prefer-offline
- run: npm run build
- run: npm install --global pnpm@11.25.0
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- name: Publish generated site to pages
run: |
git config user.name 'gitea-actions[bot]'
+7
View File
@@ -15,3 +15,10 @@ dist/
# agent run 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).
# 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
# 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
vote-service
.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
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
- **Target stack**: Astro (see [`plans/astro-refactor/`](plans/astro-refactor/README.md)) — migration in progress
- **Current stack**: hand-written HTML + CSS + ES modules, no build step, no
dependencies
- **Target stack**: Astro (see
[`plans/astro-refactor/`](plans/astro-refactor/README.md)) — migration in
progress
- **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
```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/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
that pin the site's real content and interactions. **A refactor that "passes"
by deleting assertions has failed.** See
`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" by
deleting assertions has failed.** See
[`.agents/context/verification.md`](.agents/context/verification.md).
## Publishing
`main` is the source of truth. The `pages` branch is what the Gitea Pages
Server actually serves, and its tree must end up identical to `main`'s. The
full procedure — including why `merge --ff-only` does *not* work here — is in
`main` is the source of truth. The `pages` branch is what the Gitea Pages Server
actually serves, and its tree must end up identical to `main`'s. The full
procedure — including why `merge --ff-only` does _not_ work here — is in
[`docs/operations-guide.md`](docs/operations-guide.md).
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
- `hands-on/starter/` and `hands-on/rules/`**lab fixtures.** The exercise *is*
that they are dependency-free vanilla HTML/CSS/JS an attendee can hand to an
agent. Componentizing them destroys the lesson. They ship as static assets.
- `hands-on/starter/` and `hands-on/rules/`**lab fixtures.** The exercise
_is_ that they are dependency-free vanilla HTML/CSS/JS an attendee can hand to
an agent. Componentizing them destroys the lesson. They ship as static assets.
- `submitted-skills/` — other people's submitted work, reproduced verbatim
- `skill-reviews/improved/` — generated; edit `skills-review/catalog.js` instead
- `vote-service/` — separate deploy lifecycle; do not fold into the site build
- `dist/`, `node_modules/` — build output, never committed
- `package-lock.json`**committed, but never hand-edited.** Change it only
as a side effect of `npm install`. Every worktree spins up with `npm ci`,
which fails outright without it.
- `pnpm-lock.yaml`**committed, but never hand-edited.** Change it only as a
side effect of `pnpm install`. Every worktree spins up with
`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
Binding. Read the one that covers what you are about to do.
| Rule | When |
| --- | --- |
| ---------------------------------------------------------- | ------------------------------------ |
| [`astro.md`](.agents/rules/astro.md) | any `.astro` file |
| [`theming.md`](.agents/rules/theming.md) | any colour, font, or spacing value |
| [`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.
It explains how to combine a strong planning/review model with faster workers,
reusable skills, subagent handoffs, Git worktrees, and explicit verification.
An interactive field kit compares common behavior skills such as
`ponytail-lite`, `caveman`, `unlazy`, research, debugging, and review.
The skill-forge workflow covers discovery, triggers, package anatomy,
progressive instructions, structural validation, and behavioral iteration.
The hands-on lab provides a tiny starter project and copy-ready baseline and
skill-enabled prompts for a short side-by-side exercise.
An interactive model gearbox separates capability tier from reasoning effort
across OpenAI, Claude, and Gemini, and every featured skill links to a pinned
source with an approval-first installation prompt.
reusable skills, subagent handoffs, Git worktrees, and explicit verification. An
interactive field kit compares common behavior skills such as `ponytail-lite`,
`caveman`, `unlazy`, research, debugging, and review. The skill-forge workflow
covers discovery, triggers, package anatomy, progressive instructions,
structural validation, and behavioral iteration. The hands-on lab provides a
tiny starter project and copy-ready baseline and skill-enabled prompts for a
short side-by-side exercise. An interactive model gearbox separates capability
tier from reasoning effort across OpenAI, Claude, and Gemini, and every featured
skill links to a pinned source with an approval-first installation prompt.
## Run locally
@@ -27,23 +26,30 @@ Then open <http://localhost:4173>.
Verify the content and interaction contracts with:
```bash
npm run verify
pnpm run verify
```
## Project structure
- `index.html` — default route map and focused chapter navigation
- `full-guide/` — the complete bilingual presentation, with responsive audit overrides
- `styles.css` / `app.js` — editorial visual system and bilingual field-guide interactions
- `full-guide/` — the complete bilingual presentation, with responsive audit
overrides
- `styles.css` / `app.js` — editorial visual system and bilingual field-guide
interactions
- `responsive.css` — interactive diagrams and Full HD-to-4K adaptations
- `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/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
- `skills/` — reusable design and rules-case-study skills, plus an interactive package anatomy explorer
- `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`)
- `skills/` — reusable design and rules-case-study skills, plus an interactive
package anatomy explorer
- `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
## 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/>
If the URL is not available yet, verify that the `pages` branch exists and that
the repositorys `pages` branch exists. Gitea itself does not
provide a built-in Pages server; this setup uses the instances separate Pages
Server and Actions deployment path.
the repositorys `pages` branch exists. Gitea itself does not provide a built-in
Pages server; this setup uses the instances separate Pages Server and Actions
deployment path.
For the complete authoring, verification, publication, rollback, worktree, and
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
`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
own pod, one vote per visitor enforced server-side by IP (a MAC address is
never visible to a server across the internet, so it cannot be used). See
calls a separate stateful service — `vote-service/`, a small Go API on its own
pod, one vote per visitor enforced server-side by IP (a MAC address is never
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
design, and the build/push/deploy steps; `skills-review/index.html` sets
`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
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)
bundles 12 verified articles and guides, including Medium and practitioner sources.
See [model routing](docs/references/model-routing.md) for current provider controls
and [verified skill sources](docs/references/skill-sources.md) for commit-pinned provenance.
Codex, and Git documentation. The
[additional reading path](docs/references/additional-reading.md) bundles 12
verified articles and guides, including Medium and practitioner sources. See
[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
Open `/rules/` for a concise walkthrough grounded in the
`netcracker/interview` repository. It shows how `AGENTS.md`, project-local
skills, machine-readable repo ledgers, a UI contract ratchet, lint-staged,
Husky, commitlint, specialist verifier agents, and PR review reinforce one
another. Every example links to its source file in Gitea, and the page includes
a copy-ready prompt for mapping the same layers in another repository.
Open `/rules/` for a concise walkthrough grounded in the `netcracker/interview`
repository. It shows how `AGENTS.md`, project-local skills, machine-readable
repo ledgers, a UI contract ratchet, lint-staged, Husky, commitlint, specialist
verifier agents, and PR review reinforce one another. Every example links to its
source file in Gitea, and the page includes a copy-ready prompt for mapping the
same layers in another repository.
The implementation patterns are also packaged as project-local skills in
[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` |
| Source branch | `main` |
| Published branch | `pages` |
| Local verification | `npm run verify` |
| Local verification | `pnpm run verify` |
| SilverBullet page | `Guides/AI For Dummies Presentation` |
| Skills-review vote API | `vote-service/` — separate pod, see `vote-service/README.md` |
@@ -51,7 +51,7 @@ migration task.
```mermaid
flowchart LR
E[Edit main] --> V[npm run gate]
E[Edit main] --> V[pnpm run gate]
V --> C[Commit]
C --> P[Push main]
P --> G[Gitea Actions: gate + build]
@@ -76,7 +76,7 @@ part of the published site.
### 2. Preview locally
```bash
npm run dev
pnpm run dev
```
Open
@@ -88,7 +88,7 @@ least one desktop and one mobile viewport. The production site is under the
### 3. Verify before committing
```bash
npm run gate
pnpm run gate
```
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:
```bash
npm install --package-lock=false --prefer-offline
npm run build
pnpm install --frozen-lockfile
pnpm run build
git worktree add /tmp/ai-for-dummies-pages --detach pages
git -C /tmp/ai-for-dummies-pages rm -rf .
cp -a dist/. /tmp/ai-for-dummies-pages/
@@ -523,7 +523,7 @@ ordinary content recovery.
- [ ] Portuguese static and dynamic copy is complete.
- [ ] Mouse and keyboard interactions work.
- [ ] 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.
- [ ] `pages` fast-forwards to the same commit.
- [ ] 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",
"typescript": "^5.8.3"
},
"overrides": {
"@astrojs/language-server": "2.15.0",
"defu": "6.1.7",
"vite": "6.2.1"
"packageManager": "pnpm@11.25.0",
"engines": {
"node": ">=22.13",
"pnpm": ">=11"
}
}
+31 -30
View File
@@ -7,29 +7,29 @@ merged into anything.
## Where the work is
| Branch | Head | Worktree | Gate |
| --- | --- | --- | --- |
| ----------------------------------- | --------- | --------------------------- | ----------- |
| `main` | `a45aa84` | `~/Projects/ai-for-dummies` | n/a |
| `refactor/task-01-scaffold` | `71021e6` | `~/Projects/af-task-01` | passed |
| `refactor/task-02-tokens` | `c9ec9e3` | `~/Projects/af-task-02` | passed |
| `refactor/task-03-verification-net` | `2e79aac` | `~/Projects/af-task-03` | passed |
| `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`.
Task 01 must merge to `main` first, then the other three.
02, 03 and 04 all branch from `refactor/task-01-scaffold`, not from `main`. Task
01 must merge to `main` first, then the other three.
## What each task actually produced
**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
configs, `.gitea/workflows/`, rewritten publishing section in
`docs/operations-guide.md`, `hands-on/` copied verbatim into `public/`.
Build passes, gate passes, hooks verified live, 42/42 assertions intact.
`docs/operations-guide.md`, `hands-on/` copied verbatim into `public/`. Build
passes, gate passes, hooks verified live, 42/42 assertions intact.
**02 — tokens (Gemini).** `src/styles/tokens.css` and `src/styles/base.css`
only — 43 lines. It did **not** touch the legacy CSS, deliberately: rewriting
**02 — tokens (Gemini).** `src/styles/tokens.css` and `src/styles/base.css` only
— 43 lines. It did **not** touch the legacy CSS, deliberately: rewriting
`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
site still runs on the old values. That is the correct scope, but it means the
drifting palettes are now resolved _in the new token layer_, while the live site
still runs on the old values. That is the correct scope, but it means the
consolidation is not proven visually yet.
**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).
**04 — content schema (MiniMax).** `src/content/config.ts` with eight empty
collections and a strict `localized({en, pt})` helper. No content moved; that
is tasks 05 and 06.
collections and a strict `localized({en, pt})` helper. No content moved; that is
tasks 05 and 06.
## Things I fixed that the plan got wrong
- `AGENTS.md` listed `package-lock.json` under **Never touch**, meaning never
hand-edit. Task 01 read it as never create and shipped with
`--package-lock=false`. `npm ci` — which is how every worktree spins up
cannot work without it. Wording corrected; lockfile committed.
`--package-lock=false`. `npm ci` — which is how every worktree spun up at the
time — cannot work without it. Wording corrected; lockfile committed.
- The first lockfile was reconstructed from a `node_modules` installed without
one, so its entries had no `resolved`/`integrity` and `npm ci` failed with
`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
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
downstream task. Reverted in `2e79aac`; the snapshot net was kept. **The
check should come back once the font decision is made.**
- **Task 03 rebased instead of branching**, flattening task 01's merge into
four duplicate commits with new SHAs — the same divergent-history trap that
broke the `pages` branch. Replayed onto the proper base; task 01 is an
ancestor again. Worth adding to `.agents/rules/git-worktrees.md`: **never
rebase a task branch onto anything.**
downstream task. Reverted in `2e79aac`; the snapshot net was kept. **The check
should come back once the font decision is made.**
- **Task 03 rebased instead of branching**, flattening task 01's merge into four
duplicate commits with new SHAs — the same divergent-history trap that broke
the `pages` branch. Replayed onto the proper base; task 01 is an ancestor
again. Worth adding to `.agents/rules/git-worktrees.md`: **never rebase a task
branch onto anything.**
- **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).
Its report claims the screenshot box is unticked while the files exist.
Delete them or move them; do not commit them where they are.
untracked, and in the wrong place (`.agents/snapshots/` is the right one). Its
report claims the screenshot box is unticked while the files exist. Delete
them or move them; do not commit them where they are.
## Still open from task 01's own report
@@ -84,15 +84,16 @@ is tasks 05 and 06.
## The decision that is still yours
`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
them during the migration would silently redesign the site. Task 02 defaulted
to matching what renders today. Decide explicitly, then task 19 can restore the
them during the migration would silently redesign the site. Task 02 defaulted to
matching what renders today. Decide explicitly, then task 19 can restore the
external-CSS assertion. See `.agents/context/design-system.md`.
## 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
different model than the one that wrote each — `MODEL-ROUTING.md` says never
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]`
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
with permission prompts disabled, because a blocked edit in an unattended run
just hangs. Logs land in `.agents/logs/` (gitignored).
02/18, `mm` (Claude Code against MiniMax-M3) for the rest. All three launch with
permission prompts disabled, because a blocked edit in an unattended run just
hangs. Logs land in `.agents/logs/` (gitignored).
**Task 07 is the routing calibration point.** It is small and easy to judge.
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
visitor to load.**
**Session state: see [`HANDOVER.md`](HANDOVER.md).** Phase 0 is done and
green on four branches; nothing is merged or pushed.
**Session state: see [`HANDOVER.md`](HANDOVER.md).** Phase 0 is done and green
on four branches; nothing is merged or pushed.
## Read before starting anything
| File | Why |
| --- | --- |
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| [`../../AGENTS.md`](../../AGENTS.md) | entry point |
| [`../../.agents/context/design-system.md`](../../.agents/context/design-system.md) | three drifting palettes, a font that has never rendered |
| [`../../.agents/context/verification.md`](../../.agents/context/verification.md) | 42 assertions that will all break, and must not be deleted |
@@ -23,12 +23,13 @@ green on four branches; nothing is merged or pushed.
## The three things most likely to go wrong
1. **Content loss that nobody notices.** 50 KB of bilingual copy moves between
files. Snapshot every route *before* migrating it — task 03 exists to make
files. Snapshot every route _before_ migrating it — task 03 exists to make
that possible and blocks all page work.
2. **Assertions deleted to make a red suite green.** That converts a content-loss
bug into a passing build. `gate.sh` refuses a coverage drop.
2. **Assertions deleted to make a red suite green.** That converts a
content-loss bug into a passing build. `gate.sh` refuses a coverage drop.
3. **Base-path bugs.** The site lives at `/ai-for-dummies/`. It will work
perfectly in `npm run preview` and 404 in production. Verify on the real host.
perfectly in `pnpm run preview` and 404 in production. Verify on the real
host.
## Phases
@@ -41,7 +42,7 @@ Phase 4 polish 18 ∥ 19, then 20
```
| # | Task | Agent | Depends on | Parallel with |
| --- | --- | --- | --- | --- |
| --- | ------------------------------------------------ | --------------------- | ---------- | ------------- |
| 01 | [scaffold + gates](task-01-scaffold.md) | astro-architect | — | — |
| 02 | [design tokens](task-02-tokens.md) | design-system-keeper | 01 | 03, 04 |
| 03 | [verification net](task-03-verification-net.md) | verification-engineer | 01 | 02, 04 |
@@ -75,14 +76,14 @@ cd ../af-task-08
# .agents/agents/component-builder.md (+ the skills it names)
```
The script runs `npm ci` and `verify-hooks.sh` for you. That matters: `.husky/_`
is generated, not committed, so a hand-made worktree has hooks configured but
**silently not running**.
The script runs `pnpm install --frozen-lockfile` and `verify-hooks.sh` for you.
That matters: `.husky/_` is generated, not committed, so a hand-made worktree
has hooks configured but **silently not running**.
Finishing:
```bash
npm run gate # tier 2, same as pre-push
pnpm run gate # tier 2, same as pre-push
# reviewer agent reads the diff against .agents/checklists/before-merge.md
.agents/scripts/worktree.sh finish 08 route-cards
```
@@ -94,4 +95,5 @@ See [`MODEL-ROUTING.md`](MODEL-ROUTING.md).
## These files must be committed
Worktrees check out tracked files. If this plan stays untracked, every worktree
you create will be missing it. Commit `plans/` and `.agents/` before fanning out.
you create will be missing it. Commit `plans/` and `.agents/` before fanning
out.
+18 -14
View File
@@ -1,7 +1,8 @@
# Task 01 — Astro scaffold, gates, and the publishing decision
**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
@@ -10,16 +11,16 @@ host under `/ai-for-dummies/`, and has all three gate tiers live.
## Scope
`astro.config.mjs`, `package.json`, `tsconfig.json`, `src/layouts/BaseLayout.astro`,
`.husky/`, `.lintstagedrc.json`, lint configs, `.gitea/workflows/verify.yml`,
`docs/operations-guide.md`.
`astro.config.mjs`, `package.json`, `tsconfig.json`,
`src/layouts/BaseLayout.astro`, `.husky/`, `.lintstagedrc.json`, lint configs,
`.gitea/workflows/verify.yml`, `docs/operations-guide.md`.
Copy the configs from `.agents/templates/config/` — they are written for this
project (correct ignores for `hands-on/`, `submitted-skills/`, `vote-service/`).
## 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**.
2. Set `base: '/ai-for-dummies'`. Every internal link goes through
`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`.
**`"prepare": "husky"` is what makes hooks exist** — without it every hook is
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
`.agents/scripts/verify-hooks.sh`.
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).
9. **Make the publishing decision** per `.agents/context/publishing.md`. Recommended:
Gitea Actions builds `dist/` and pushes `pages`. Copy
8. Copy `hands-on/` into `public/` (task 17 does this properly; a stub is fine
here).
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`.
10. Rewrite the publishing section of `docs/operations-guide.md` to match.
## 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
- [ ] 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
## Do not
@@ -54,6 +58,6 @@ project (correct ignores for `hands-on/`, `submitted-skills/`, `vote-service/`).
## Watch for
The base path is the #1 production-only failure in this migration. `npm run
preview` will lie to you. Deploy the smoke-test page and curl it with a
`?v=<sha>` cache-buster.
The base path is the #1 production-only failure in this migration.
`pnpm run preview` will lie to you. Deploy the smoke-test page and curl it with
a `?v=<sha>` cache-buster.
@@ -1,8 +1,8 @@
# Task 03 — Verification net
**Agent**: `verification-engineer` · **Model**: Codex
**Depends on**: 01 · **Parallel with**: 02, 04 · **Blocks**: 1216
**Worktree**: `.agents/scripts/worktree.sh start 03 verification-net`
**Agent**: `verification-engineer` · **Model**: Codex **Depends on**: 01 ·
**Parallel with**: 02, 04 · **Blocks**: 1216 **Worktree**:
`.agents/scripts/worktree.sh start 03 verification-net`
## Goal
@@ -14,11 +14,12 @@ This task is on the critical path. Do it early.
## Scope
`.agents/snapshots/`, `scripts/audit-ui.mjs`, `.agents/scripts/visual-regression.mjs`.
`.agents/snapshots/`, `scripts/audit-ui.mjs`,
`.agents/scripts/visual-regression.mjs`.
## Steps
1. `npm run serve` against the **current, unmigrated** site.
1. `pnpm run serve` against the **current, unmigrated** site.
2. Snapshot all ten routes:
```bash
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
- [ ] 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
- [ ] 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
- [ ] `npm run gate` green
- [ ] 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
- [ ] `pnpm run gate` green
## Do not
+4 -4
View File
@@ -1,9 +1,9 @@
# Task 07 — Primitives
**Agent**: `component-builder` · **Model**: MiniMax-M3 — **use this task to
calibrate the model routing before fanning out**
**Depends on**: 02 · **Blocks**: 0811
**Worktree**: `.agents/scripts/worktree.sh start 07 primitives`
calibrate the model routing before fanning out** **Depends on**: 02 ·
**Blocks**: 0811 **Worktree**:
`.agents/scripts/worktree.sh start 07 primitives`
## Goal
@@ -36,7 +36,7 @@ you have found two components, not one.
- [ ] `.agents/checklists/before-component.md` complete for all four
- [ ] 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.
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
**Agent**: `component-builder` · **Model**: MiniMax-M3
**Depends on**: 07 · **Parallel with**: 09, 10, 11 · **Blocks**: 12
**Worktree**: `.agents/scripts/worktree.sh start 08 route-cards`
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Parallel with**: 09, 10, 11 · **Blocks**: 12 **Worktree**:
`.agents/scripts/worktree.sh start 08 route-cards`
## Goal
@@ -10,7 +10,8 @@ The landing page's six chapter cards become one component over data.
## Scope
`src/components/blocks/RouteCard.astro`, `src/components/blocks/GridGroup.astro`.
`src/components/blocks/RouteCard.astro`,
`src/components/blocks/GridGroup.astro`.
## Source
@@ -30,7 +31,7 @@ title, summary, and href. Textbook extraction. Styles in `landing.css` +
- [ ] Six cards render identically to `index.html` today
- [ ] Screenshots at 560/800/1100/1600 match
- [ ] Zero JS
- [ ] `.agents/checklists/before-component.md` complete; `npm run gate` green
- [ ] `.agents/checklists/before-component.md` complete; `pnpm run gate` green
## Do not
@@ -1,8 +1,8 @@
# Task 09 — Chapter blocks
**Agent**: `component-builder` · **Model**: MiniMax-M3
**Depends on**: 07 · **Parallel with**: 08, 10, 11 · **Blocks**: 13, 14
**Worktree**: `.agents/scripts/worktree.sh start 09 chapter-blocks`
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Parallel with**: 08, 10, 11 · **Blocks**: 13, 14 **Worktree**:
`.agents/scripts/worktree.sh start 09 chapter-blocks`
## Goal
@@ -23,8 +23,8 @@ is the component.
## Watch for
- `TopBar` carries `aria-current="page"`. Preserve it; it is the only
indication of location for assistive tech.
- `TopBar` carries `aria-current="page"`. Preserve it; it is the only indication
of location for assistive tech.
- The tables in `/models/` and `/rules/` scroll horizontally on narrow screens
(`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
@@ -35,4 +35,4 @@ is the component.
- [ ] All five pages' markup expressible with these components
- [ ] Rendered text identical to current pages
- [ ] 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
**Agent**: `component-builder` · **Model**: MiniMax-M3
**Depends on**: 07 · **Parallel with**: 08, 09, 11 · **Blocks**: 15
**Worktree**: `.agents/scripts/worktree.sh start 10 guide-blocks`
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Parallel with**: 08, 09, 11 · **Blocks**: 15 **Worktree**:
`.agents/scripts/worktree.sh start 10 guide-blocks`
## Goal
@@ -25,7 +25,8 @@ Task 15 wires the interactivity.
`data-tree="main|ui"`, `data-worker="ui"`, `data-route="plan"`,
`data-model-provider="openai|claude|gemini"`, `data-effort="low|medium|high"`,
`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.
@@ -41,4 +42,4 @@ screenshots agree.
- [ ] Every `data-*` hook above present in rendered output
- [ ] Components take props and render static markup; **no `client:*` yet**
- [ ] 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
**Agent**: `component-builder` · **Model**: MiniMax-M3
**Depends on**: 07 · **Parallel with**: 08, 09, 10 · **Blocks**: 16
**Worktree**: `.agents/scripts/worktree.sh start 11 review-blocks`
**Agent**: `component-builder` · **Model**: MiniMax-M3 **Depends on**: 07 ·
**Parallel with**: 08, 09, 10 · **Blocks**: 16 **Worktree**:
`.agents/scripts/worktree.sh start 11 review-blocks`
## Goal
@@ -46,4 +46,4 @@ assistive tech. Colour alone is not enough. It is also asserted.
- [ ] All listed CSS hooks present
- [ ] `aria-pressed`, `role="group"`, `aria-label` preserved
- [ ] 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
**Agent**: `page-migrator` · **Model**: MiniMax-M3
**Depends on**: 03, 08 · **Parallel with**: 13, 14, 17
**Worktree**: `.agents/scripts/worktree.sh start 12 page-landing`
**Agent**: `page-migrator` · **Model**: MiniMax-M3 **Depends on**: 03, 08 ·
**Parallel with**: 13, 14, 17 **Worktree**:
`.agents/scripts/worktree.sh start 12 page-landing`
## 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
- [ ] Screenshots match at four widths
- [ ] 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
**Agent**: `page-migrator` · **Model**: MiniMax-M3
**Depends on**: 03, 09 · **Parallel with**: 12, 14, 17 · **Blocks**: 15, 16
**Worktree**: `.agents/scripts/worktree.sh start 13 page-chapters`
**Agent**: `page-migrator` · **Model**: MiniMax-M3 **Depends on**: 03, 09 ·
**Parallel with**: 12, 14, 17 · **Blocks**: 15, 16 **Worktree**:
`.agents/scripts/worktree.sh start 13 page-chapters`
## Goal
@@ -25,7 +25,7 @@ Blocks 15 and 16 because those pages link here and share the layout.
- [ ] Four snapshot diffs empty
- [ ] Three pages ship 0 bytes JS; `/skills/` ships only its island
- [ ] 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
+5 -5
View File
@@ -1,8 +1,8 @@
# Task 14 — Rules page
**Agent**: `page-migrator` · **Model**: MiniMax-M3
**Depends on**: 03, 09 · **Parallel with**: 12, 13, 17
**Worktree**: `.agents/scripts/worktree.sh start 14 page-rules`
**Agent**: `page-migrator` · **Model**: MiniMax-M3 **Depends on**: 03, 09 ·
**Parallel with**: 12, 13, 17 **Worktree**:
`.agents/scripts/worktree.sh start 14 page-rules`
## Goal
@@ -24,10 +24,10 @@ deleted. Screenshot at the old widths (900, 600, 2200) to prove equivalence.
## Watch for
`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
- [ ] Snapshot diff empty; bilingual toggle works, `<html lang>` follows
- [ ] 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
**Agent**: `page-migrator` · **Model**: **Codex** — hardest task in the plan
**Depends on**: 05, 10, 13 · **Parallel with**: 16
**Worktree**: `.agents/scripts/worktree.sh start 15 page-full-guide`
**Depends on**: 05, 10, 13 · **Parallel with**: 16 **Worktree**:
`.agents/scripts/worktree.sh start 15 page-full-guide`
## Goal
@@ -21,7 +21,7 @@ toggle.
Only these hydrate. Everything else is server-rendered.
| Island | Directive | Why |
| --- | --- | --- |
| -------------------------------------------------------- | ---------------- | -------------------------------------------------- |
| Phase tabs | `client:visible` | click-driven panel swap |
| Tree / worker / route / model / effort / skill selectors | `client:visible` | same pattern; consider one generic selector island |
| 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
`const phases`, `const handsOnPrompts`, `const modelGuide`, `const skillSources`,
`const skillInstallPrompts`, `render('plan')`, `renderTree`, `renderWorker`,
`renderRoute`, `renderModelProvider`, `renderEffort`, `renderSkillFile`,
`renderSkillWorkflow`, `renderCommonSkill`, `renderHandsOn`, `copyPrompt`.
`const phases`, `const handsOnPrompts`, `const modelGuide`,
`const skillSources`, `const skillInstallPrompts`, `render('plan')`,
`renderTree`, `renderWorker`, `renderRoute`, `renderModelProvider`,
`renderEffort`, `renderSkillFile`, `renderSkillWorkflow`, `renderCommonSkill`,
`renderHandsOn`, `copyPrompt`.
These are **implementation-detail assertions** — they look deletable and are
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`
fallback. Keep both — the fallback exists for non-secure contexts.
- The hands-on prompt strings are copy-pasted by attendees into an agent.
Exact whitespace and line breaks matter.
- The hands-on prompt strings are copy-pasted by attendees into an agent. Exact
whitespace and line breaks matter.
- `responsive.css` (30 KB) mostly serves this page. Port what is needed, prove
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
- [ ] Keyboard: arrow keys move between tabs; focus visible throughout
- [ ] 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
**Agent**: `page-migrator` · **Model**: **Codex** — highest defect risk
**Depends on**: 06, 11, 13 · **Parallel with**: 15
**Worktree**: `.agents/scripts/worktree.sh start 16 page-review-desk`
**Depends on**: 06, 11, 13 · **Parallel with**: 15 **Worktree**:
`.agents/scripts/worktree.sh start 16 page-review-desk`
## Goal
`/skills-review/` → Astro. The most interactive page: search, filtering,
lazy file fetching, markdown rendering, a client-side diff view, six URL params,
and a live API call to `vote-service/`.
`/skills-review/` → Astro. The most interactive page: search, filtering, lazy
file fetching, markdown rendering, a client-side diff view, six URL params, and
a live API call to `vote-service/`.
## 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`,
`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.
@@ -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
- [ ] Vote widget reaches the live API; CORS preflight succeeds
- [ ] 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
**Agent**: `motion-designer` · **Model**: Gemini (perceptual judgement)
**Depends on**: 15, 16 · **Parallel with**: 19
**Worktree**: `.agents/scripts/worktree.sh start 18 motion`
**Depends on**: 15, 16 · **Parallel with**: 19 **Worktree**:
`.agents/scripts/worktree.sh start 18 motion`
## 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.
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`,
`margin`). Those force reflow every frame and fail the 200ms INP budget. The
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
- [ ] Every animation has a one-line written purpose
- [ ] 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
**Agent**: `verification-engineer` · **Model**: Codex
**Depends on**: 15, 16 · **Parallel with**: 18
**Worktree**: `.agents/scripts/worktree.sh start 19 verify-repoint`
**Agent**: `verification-engineer` · **Model**: Codex **Depends on**: 15, 16 ·
**Parallel with**: 18 **Worktree**:
`.agents/scripts/worktree.sh start 19 verify-repoint`
## Goal
@@ -15,27 +15,28 @@ a written reason.
## The three kinds
| 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** |
| 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 |
## 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?
Yes → re-point. No → content was lost; escalate.
3. Add rendered-text snapshot assertions for all ten routes so this class of
regression is caught structurally, not by string luck.
4. Confirm `check-tokens.mjs` and the extended `audit-ui.mjs` are in
`npm run verify`.
`pnpm run verify`.
## 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
- [ ] 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)
## Do not
+7 -6
View File
@@ -1,7 +1,8 @@
# Task 20 — Cutover and cleanup
**Agent**: `astro-architect`, **with a human watching** · **Model**: MiniMax-M3 assisting
**Depends on**: all · **Worktree**: `.agents/scripts/worktree.sh start 20 cutover`
**Agent**: `astro-architect`, **with a human watching** · **Model**: MiniMax-M3
assisting **Depends on**: all · **Worktree**:
`.agents/scripts/worktree.sh start 20 cutover`
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`,
`rules/app.js`, `rules/styles.css`, `skills/app.js`, `skills/styles.css`,
`skills-review/*.js`, `skills-review/*.css`, and the ten old `index.html`
files. `git rm`, one commit, reviewable.
**Keep**: `hands-on/**` (now under `public/`), `submitted-skills/**`,
`skill-reviews/**`, `docs/**`, `vote-service/**`.
files. `git rm`, one commit, reviewable. **Keep**: `hands-on/**` (now under
`public/`), `submitted-skills/**`, `skill-reviews/**`, `docs/**`,
`vote-service/**`.
3. **Publish** via the mechanism chosen in task 01.
4. **Verify on the real host** with a cache-buster:
```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
- [ ] Vote widget works end-to-end from the published origin (CORS is
origin-sensitive — `ALLOWED_ORIGIN` must still match)
- [ ] Old files deleted; `npm run gate` green
- [ ] Old files deleted; `pnpm run gate` green
- [ ] Docs match reality
- [ ] 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