`styles.css` line 1 carried a malformed rule for the life of the site:
@font-face{font-family:Manrope;src:url('https://fonts.googleapis.com/css2?...')}
`src:` in an @font-face must point at a font binary. That URL returns a CSS
stylesheet, so no browser could ever load a face from it. Every
`font-family:Manrope,Arial,sans-serif` fell through to Arial, and 'DM Mono' was
never declared as a family at all, so it fell through to generic monospace. The
intended typography has never once been seen.
Task 02 spotted this and was told to default to deleting the dead rule and
declaring the stacks that actually render. It recorded that decision, deferred
the deletion to "future component tasks", and nothing picked it up. The human
has now chosen the other branch: the real fonts.
Self-hosted rather than linked from fonts.googleapis.com because
scripts/audit-ui.mjs rejects any external <link>/<script>, and because the site
is presented in workshop rooms with unreliable networks. Latin and latin-ext
subsets only — the site is EN and PT-BR, so the cyrillic, greek and vietnamese
subsets Google also serves are dropped. Manrope ships as one variable file
covering 400-800. 89 KB total across six faces, all SIL OFL.
One public/fonts/fonts.css serves both trees, with relative url()s that each
consumer resolves against that file's own location: BaseLayout.astro links it
for Astro pages, the legacy root styles.css @imports it.
This changes how every page renders. That is the point, and it is the one
sanctioned visual change in the migration — screenshots taken before today show
Arial and are no longer a valid baseline. The three governing documents that
said "do not add a webfont" are updated so the next design-system-keeper does
not undo this.
Adds .stylelintignore, mirroring .prettierignore's legacy list for the same
reason: staging the minified styles.css to change one declaration produced ~180
declaration-block-single-line-max-declarations errors and blocked the commit.
public/fonts/fonts.css is deliberately excluded from that ignore list.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Adds the agent-facing workspace and a 20-task plan for migrating the site
to Astro. Nothing here implements the refactor; these are briefs, rules and
templates that the task agents read.
- .agents/ holds context, rules, checklists, skills, specialist agents,
component/page/config templates and gate scripts. It is vendor-neutral so
MiniMax, Gemini and Codex can all read it; CLAUDE.md just points at
AGENTS.md.
- .husky/ plus .lintstagedrc.json wire the three gate tiers. gate.sh locks on
the shared git-common-dir so parallel worktrees serialise, and guards the
assertion count in scripts/verify.mjs against a coverage drop.
- plans/astro-refactor/ carries the phase graph, per-task briefs and the
model-routing recommendation.
These files must be tracked before fanning out: a worktree only checks out
tracked files, so an untracked plan is invisible to every agent working in one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>