# Context: architecture ## Current (Astro, static output) Ten routes, one `src/pages/` entry each, built to `dist/`: | Route | Page | Islands | | -------------------- | ------------------------------- | ----------------------------------------------- | | `/` | `src/pages/index.astro` | — | | `/full-guide/` | `src/pages/full-guide.astro` | `GuideSelector`, `LanguageToggle`, `CopyPrompt` | | `/summary/` | `src/pages/summary.astro` | — | | `/models/` | `src/pages/models.astro` | — | | `/agents/` | `src/pages/agents.astro` | — | | `/skills/` | `src/pages/skills.astro` | `SkillPackageExplorer` | | `/rules/` | `src/pages/rules.astro` | `RulesInteractive` | | `/skills-review/` | `src/pages/skills-review.astro` | `legacy/skills-review/app.js` | | `/hands-on/starter/` | `public/` lab fixture | own | | `/hands-on/rules/` | `public/` lab fixture | own | ## What is still unmigrated `legacy/` holds the parts the migration did not componentize. They are not dead files — the pages listed above import them, and the build fails without them. - **`legacy/styles/guide.css`** (was `styles.css`) — the editorial visual system, imported by `full-guide.astro`. - **`legacy/styles/audit.css`** (was `full-guide/audit.css`) — responsive audit overrides, imported by `full-guide.astro`. - **`legacy/styles/chapters.css`** — imported by `ChapterLayout.astro`. - **`legacy/styles/skills-chapter.css`**, **`skills-review.css`**, **`change-lens.css`** — imported by their respective pages. `skills-chapter.css` is imported for its side effect, not through the `?url` + `` pattern the layout uses: inside a **page**, `?url` on a stylesheet resolves to that page's own CSS chunk rather than to the imported file, so the link points at the wrong asset and the sheet is emitted but never loaded. It was named `skills.css` and silently unloaded that way until 2026-09-06. - **`legacy/skills-review/`** — `app.js` and the module graph under it (`catalog.js`, `submitted-catalog.js`, `files.js`, `submitted-files.js`, `vote.js`). `catalog.js` + `submitted-catalog.js` are the review desk's real data model, 24 entries; they are a content collection in all but name. These sit outside `src/` deliberately: `check-tokens.mjs` sweeps `src`, and these files are full of raw hex and unnamed breakpoints. Moving one into `src/` means migrating it to tokens in the same change, not adding an exclusion. `responsive.css`, `landing.css`, `app.js`, `rules/app.js`, `rules/styles.css`, and `skills/app.js` were deleted at cutover: their content lives in components. ## Layout ``` src/ content/ catalog entries, chapter copy, EN/PT strings (typed collections) layouts/ BaseLayout, ChapterLayout, GuideLayout components/ .astro by default; islands only where marked styles/ tokens.css, base.css, then per-component styles pages/ routes mirroring today's URLs exactly public/ hands-on/ lab fixtures copied verbatim, never processed ``` ### Non-negotiables - **URLs do not change.** `/full-guide/`, `/skills-review/`, `/hands-on/starter/` and the rest must resolve exactly as they do now, trailing slash included. Existing links (including `docs/`, SilverBullet, and shared URLs with `?author=…&skill=…&view=…` query params) must keep working. - **Zero JS by default.** Seven of the ten pages ship no JavaScript. They must still ship none. Islands are opt-in, per component, and justified. - **`hands-on/` stays vanilla.** It lives in `public/` untouched. It is a lab fixture, not a component. - **No external runtime requests.** `audit-ui.mjs` enforces this and it is part of the site's thesis. Self-host anything you add. - **The review desk's query-param deep links keep working** — `?author=`, `?skill=`, `?view=`, `?file=`, `?compare=`, `?render=`. They are documented in the page footer and shared externally. ## Companion service The vote API is a Go service on its own Kubernetes deploy cycle, reached by the review desk over `window.SKILLS_REVIEW_VOTE_API`. Its source left this repository on 2026-09-06; the deployed service is unchanged, and the review desk still calls it. Keep the global, or replace it with a build-time `PUBLIC_VOTE_API` env var — but if you do, update the service's own README in the same change. Its one-vote-per-IP assertion left `verify.mjs` with it. See [`assertion-removals.md`](assertion-removals.md).