From 580293867dfa194f0113fd031359b7149cf40849 Mon Sep 17 00:00:00 2001 From: Marcos Paulo Date: Sun, 6 Sep 2026 08:30:27 +0000 Subject: [PATCH] refactor: retire the hand-written site Deletes the pre-Astro pages, scripts, and stylesheets that the migration replaced, and moves the ones it did not replace out of the way. Deleted (32 files): app.js, responsive.css, landing.css, rules/app.js, rules/styles.css, skills/app.js, the ten route index.html files, and the root hands-on/ copy, which is byte-identical to public/hands-on/ -- the one the build actually ships. Moved to legacy/ (12 files): styles.css, full-guide/audit.css, chapters.css, skills/styles.css, skills-review/styles.css, skills-review/change-lens.css, and the skills-review/app.js module graph. These are not dead. The Astro pages import them and the build fails without them, which the plan had not accounted for. They go to legacy/ rather than src/ because check-tokens.mjs sweeps src, and these files are full of raw hex and unnamed breakpoints: moving one into src/ should mean migrating it to tokens in the same change, not adding a scan exclusion. The prettier, stylelint, and eslint ignore lists that already named these files at their old paths now name legacy/ instead. verify.mjs no longer reads app.js. The 102 Portuguese strings were extracted from its translations.pt object before deletion into .agents/snapshots/full-guide-pt.json -- a legacy capture, not a snapshot of the Astro build, so the assertion still compares against an independent source. The brace-matching helper's assertion is replaced by one that rejects an empty snapshot entry, without which trimming the snapshot would make the presence check pass vacuously. Count stays at 84. audit-ui.mjs reads the ten pages from dist/ and resolves Astro's base-absolute hrefs against it. Before deleting anything, rendered-text-diff was run across all ten routes plus both Portuguese pages: every one at parity, 0 missing and 0 extra. That comparison is not repeatable once the legacy files are gone. computed-style-diff on /full-guide/ stays at 32 differences, so the moves are style-neutral. Docs updated to match: README, AGENTS.md, GATES.md, the architecture context, the operations guide's lab instructions, and the three skills that told you to serve the vanilla site. Publishing is not part of this commit. Co-Authored-By: Claude Opus 5 --- .agents/context/architecture.md | 80 ++-- .agents/scripts/computed-style-diff.mjs | 2 + .agents/scripts/rendered-text-diff.mjs | 4 + .agents/skills/astro-page/SKILL.md | 6 +- .agents/skills/design-tokens/SKILL.md | 6 +- .agents/skills/visual-regression/SKILL.md | 5 +- .agents/snapshots/full-guide-pt.json | 104 +++++ .prettierignore | 22 +- .stylelintignore | 19 +- AGENTS.md | 41 +- GATES.md | 6 +- README.md | 55 ++- agents/index.html | 1 - app.js | 411 ------------------ docs/operations-guide.md | 24 +- eslint.config.js | 6 +- full-guide/index.html | 37 -- hands-on/rules/README.md | 28 -- hands-on/rules/app.js | 124 ------ hands-on/rules/index.html | 43 -- hands-on/rules/styles.css | 1 - hands-on/starter/README.md | 13 - hands-on/starter/app.js | 15 - hands-on/starter/index.html | 22 - hands-on/starter/styles.css | 1 - index.html | 27 -- landing.css | 1 - .../skills-review}/app.js | 0 .../skills-review}/catalog.js | 0 .../skills-review}/files.js | 0 .../skills-review}/submitted-catalog.js | 0 .../skills-review}/submitted-files.js | 0 .../skills-review}/vote.js | 0 {full-guide => legacy/styles}/audit.css | 0 .../styles}/change-lens.css | 0 chapters.css => legacy/styles/chapters.css | 0 styles.css => legacy/styles/guide.css | 0 .../styles/skills-review.css | 0 skills/styles.css => legacy/styles/skills.css | 0 models/index.html | 1 - package.json | 1 - plans/astro-refactor/task-20-cutover.md | 78 ++++ responsive.css | 79 ---- rules/app.js | 92 ---- rules/index.html | 102 ----- rules/styles.css | 6 - scripts/audit-ui.mjs | 33 +- scripts/verify.mjs | 36 +- skills-review/index.html | 45 -- skills/app.js | 23 - skills/index.html | 33 -- src/components/blocks/WorktreeMap.astro | 2 +- src/layouts/ChapterLayout.astro | 2 +- src/pages/full-guide.astro | 8 +- src/pages/skills-review.astro | 6 +- src/pages/skills.astro | 2 +- summary/index.html | 1 - 57 files changed, 367 insertions(+), 1287 deletions(-) create mode 100644 .agents/snapshots/full-guide-pt.json delete mode 100644 agents/index.html delete mode 100644 app.js delete mode 100644 full-guide/index.html delete mode 100644 hands-on/rules/README.md delete mode 100644 hands-on/rules/app.js delete mode 100644 hands-on/rules/index.html delete mode 100644 hands-on/rules/styles.css delete mode 100644 hands-on/starter/README.md delete mode 100644 hands-on/starter/app.js delete mode 100644 hands-on/starter/index.html delete mode 100644 hands-on/starter/styles.css delete mode 100644 index.html delete mode 100644 landing.css rename {skills-review => legacy/skills-review}/app.js (100%) rename {skills-review => legacy/skills-review}/catalog.js (100%) rename {skills-review => legacy/skills-review}/files.js (100%) rename {skills-review => legacy/skills-review}/submitted-catalog.js (100%) rename {skills-review => legacy/skills-review}/submitted-files.js (100%) rename {skills-review => legacy/skills-review}/vote.js (100%) rename {full-guide => legacy/styles}/audit.css (100%) rename {skills-review => legacy/styles}/change-lens.css (100%) rename chapters.css => legacy/styles/chapters.css (100%) rename styles.css => legacy/styles/guide.css (100%) rename skills-review/styles.css => legacy/styles/skills-review.css (100%) rename skills/styles.css => legacy/styles/skills.css (100%) delete mode 100644 models/index.html delete mode 100644 responsive.css delete mode 100644 rules/app.js delete mode 100644 rules/index.html delete mode 100644 rules/styles.css delete mode 100644 skills-review/index.html delete mode 100644 skills/app.js delete mode 100644 skills/index.html delete mode 100644 summary/index.html diff --git a/.agents/context/architecture.md b/.agents/context/architecture.md index 33b0252..ca10a9f 100644 --- a/.agents/context/architecture.md +++ b/.agents/context/architecture.md @@ -1,45 +1,47 @@ -# Context: architecture, current and target +# Context: architecture -## Current (no build step) +## Current (Astro, static output) -Ten hand-written HTML pages, each linking its own CSS and one ES module: +Ten routes, one `src/pages/` entry each, built to `dist/`: -| Route | Page | Script | Stylesheets | -| -------------------- | -------------------------- | ---------------------- | --------------------------------------------- | -| `/` | `index.html` | — | `chapters.css`, `landing.css` | -| `/full-guide/` | `full-guide/index.html` | `app.js` (50 KB) | `styles.css`, `responsive.css`, `audit.css` | -| `/summary/` | `summary/index.html` | — | `chapters.css` | -| `/models/` | `models/index.html` | — | `chapters.css` | -| `/agents/` | `agents/index.html` | — | `chapters.css` | -| `/skills/` | `skills/index.html` | `skills/app.js` | `skills/styles.css` | -| `/rules/` | `rules/index.html` | `rules/app.js` | `rules/styles.css` | -| `/skills-review/` | `skills-review/index.html` | `skills-review/app.js` | `skills-review/styles.css`, `change-lens.css` | -| `/hands-on/starter/` | lab fixture | own | own | -| `/hands-on/rules/` | lab fixture | own | own | +| 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 | -Weight is concentrated: `app.js` 50 KB, `responsive.css` 30 KB, -`skills-review/catalog.js` 27 KB, `skills-review/submitted-catalog.js` 18 KB. +## What is still unmigrated -### What each big file actually is +`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. -- **`app.js`** — not really application code. It is a **bilingual content - database** (`phases`, `handsOnPrompts`, `modelGuide`, `skillSources`, - `skillInstallPrompts`, each keyed `{en, pt}`) plus ~12 small `render*` - functions that swap `innerHTML` on tab clicks. ~50 `en:` keys. The content - should become data; only the tab behaviour is interactive. -- **`responsive.css`** — a 30 KB append-only layer of overrides bolted on top of - `styles.css`. Expect large parts to be dead once layout moves into components. - Do not port it verbatim. -- **`skills-review/catalog.js`** — the real data model of the review desk: one - entry per submitted skill with `id`, `author`, `title`, `status`, `focus`, - `wins[]`, `improve[]`, `extras`, `improved` (full markdown). 24 entries across - `catalog.js` + `submitted-catalog.js`. This is already a content collection in - all but name. -- **`skills-review/files.js` / `submitted-files.js`** — generated file - manifests. -- **`vote.js`** — the vote widget island; talks to `vote-service/`. +- **`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.css`**, **`skills-review.css`**, **`change-lens.css`** + — imported by their respective pages. +- **`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. -## Target (Astro) +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/ @@ -52,15 +54,15 @@ public/ hands-on/ lab fixtures copied verbatim, never processed ``` -### Non-negotiables for the target +### 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 today. They - must still ship none. Islands are opt-in, per component, and justified. -- **`hands-on/` stays vanilla.** It goes in `public/` untouched. It is a lab +- **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. diff --git a/.agents/scripts/computed-style-diff.mjs b/.agents/scripts/computed-style-diff.mjs index 4a47a1e..fc4b034 100644 --- a/.agents/scripts/computed-style-diff.mjs +++ b/.agents/scripts/computed-style-diff.mjs @@ -73,6 +73,8 @@ const PROPERTIES = [ 'overflow', ]; +// The legacy pages were deleted at cutover; run this from a pre-cutover +// worktree, or the legacy side will 404. const legacyPath = route === 'index' ? 'index.html' : `${route}/index.html`; const astroPath = route === 'index' ? '' : `${route}/`; diff --git a/.agents/scripts/rendered-text-diff.mjs b/.agents/scripts/rendered-text-diff.mjs index 6e239e4..c8989bc 100644 --- a/.agents/scripts/rendered-text-diff.mjs +++ b/.agents/scripts/rendered-text-diff.mjs @@ -17,6 +17,10 @@ // bilingual pair, so the two sides line up. // // Requires playwright (devDependency) and two static servers; it starts both. +// +// The legacy pages were deleted at cutover, so this needs a pre-cutover tree: +// git worktree add /tmp/vanilla +// and run from there, or run it from a checkout that still has them. import { spawn } from 'node:child_process'; import { cpSync, mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; diff --git a/.agents/skills/astro-page/SKILL.md b/.agents/skills/astro-page/SKILL.md index 46bacd3..a1f7c8d 100644 --- a/.agents/skills/astro-page/SKILL.md +++ b/.agents/skills/astro-page/SKILL.md @@ -12,8 +12,12 @@ description: The snapshot is the only objective evidence that no content was lost. +The vanilla site was deleted at cutover. To compare against it, check the +pre-cutover tree out into a scratch worktree first: + ```bash -pnpm run serve & # vanilla site on :4173 +git worktree add /tmp/vanilla +(cd /tmp/vanilla && python3 -m http.server 4173) & node .agents/scripts/snapshot-route.mjs http://localhost:4173/models/ \ > .agents/snapshots/models.txt ``` diff --git a/.agents/skills/design-tokens/SKILL.md b/.agents/skills/design-tokens/SKILL.md index 5e93ce3..44a88f0 100644 --- a/.agents/skills/design-tokens/SKILL.md +++ b/.agents/skills/design-tokens/SKILL.md @@ -21,8 +21,10 @@ a bug. ```bash python3 - <<'PY' import re -files=['styles.css','chapters.css','landing.css','rules/styles.css','skills/styles.css', - 'skills-review/styles.css','hands-on/starter/styles.css','hands-on/rules/styles.css'] +files=['legacy/styles/guide.css','legacy/styles/chapters.css','legacy/styles/skills.css', + 'legacy/styles/skills-review.css','legacy/styles/change-lens.css', + 'legacy/styles/audit.css','public/hands-on/starter/styles.css', + 'public/hands-on/rules/styles.css'] seen={} for f in files: for m in re.finditer(r'--([a-z-]+):\s*([^;}]+)', open(f).read()): diff --git a/.agents/skills/visual-regression/SKILL.md b/.agents/skills/visual-regression/SKILL.md index c8a5042..e711c8d 100644 --- a/.agents/skills/visual-regression/SKILL.md +++ b/.agents/skills/visual-regression/SKILL.md @@ -33,8 +33,9 @@ with sync_playwright() as p: browser.close() ``` -Run once against the vanilla site (`pnpm run serve`), once against -`pnpm run preview`. Keep both sets. +Run once against `pnpm run preview`. To compare against the vanilla site, serve +a pre-cutover worktree on :4173 first — those files are no longer on `main`. +Keep both sets. ## Compare diff --git a/.agents/snapshots/full-guide-pt.json b/.agents/snapshots/full-guide-pt.json new file mode 100644 index 0000000..eddbe5a --- /dev/null +++ b/.agents/snapshots/full-guide-pt.json @@ -0,0 +1,104 @@ +[ + "01 frota", + "02 worktrees", + "03 modelos", + "04 skills", + "05 criar", + "06 kit de campo", + "07 prática", + "ENGENHARIA DE IA 01 / 2026", + "Uma apresentação para quem entrega software", + "Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.", + "NOTA DE CAMPO / 001", + "Entregue o
sistema.", + "Skills · agentes · worktrees · evidências", + "modelo forte
para ambiguidade", + "workers delimitados
em paralelo", + "iterações
com evidências", + "Leia isto como um mapa de rota, não como uma receita de prompt.", + "REGRA ZERO", + "Modelo forte para ambiguidade.
Modelo leve para trabalho delimitado.", + "Uma pequena frota", + "coordenação antes do paralelismo", + "ORQUESTRADOR", + "Decide o que
precisa acontecer.", + "Componentes e estados visuais", + "Casos de aceitação", + "Guia e exemplos", + "O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados verificáveis. Ele não precisa digitar cada linha.", + "Por que a fronteira importa", + "uma tarefa vaga / três falhas previsíveis", + "Sopa de contexto", + "Cada worker lê tudo. Ninguém sabe quais fatos são essenciais.", + "Colisão de branches", + "Dois agentes usam o mesmo checkout. O caminho mais rápido vira resolução de conflitos.", + "Desvio confiante", + "O diff parece ótimo, mas ninguém verifica se resolveu o problema original.", + "O ciclo de subagentes", + "Clique em uma fase.
Veja a passagem.", + "Delegar é mover uma tarefa delimitada para um contexto menor — não abrir mão da responsabilidade.", + "O que atravessa contextos", + "brief → diff → evidência", + "Pacote", + "Contém", + "Por que importa", + "Git worktrees", + "Uma branch
por mão.", + "Um worktree é outro diretório ligado ao mesmo repositório. Cada agente recebe seu próprio checkout e índice; o histórico continua compartilhado.", + "Selecione um nó para inspecionar checkout, responsável e próxima ação.", + "topologia do repositório", + " 4 checkouts", + "RAIZ", + "AGENTE DE UI", + "AGENTE DE TESTES", + "AGENTE DE DOCS", + "● limpo", + "3 arquivos · trabalhando", + "8 verificações · pronto", + "2 páginas · revisão", + "Roteamento de modelos", + "Não pague por
raciocínio onde precisa
de ritmo.", + "Escolha um trabalho para entender por que o perfil do modelo muda.", + "Trabalho", + "Perfil", + "Formato do prompt", + "Planejar", + "Construir", + "Explorar", + "Revisar", + "Skills", + "Escreva do jeito certo
uma vez.", + "Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências, scripts e assets. Não é memória mágica e não substitui critérios de aceitação.", + "01 / defina o gatilho", + "02 / carregue detalhes sob demanda", + "03 / devolva evidências", + "PACOTE DE SKILL", + "Skills comuns", + "escolha o comportamento antes do modelo", + "O kit de campo", + "Trabalhos diferentes.
Instintos diferentes.", + "Uma skill muda como o agente aborda o trabalho. Algumas moldam a comunicação. Outras impõem pesquisa, diagnóstico, revisão ou disciplina de conclusão. Selecione uma para inspecionar sua regra operacional.", + "SIMPLIFICAR", + "código mínimo que funciona", + "COMUNICAR", + "sinal sem excesso", + "CONCLUIR", + "gates e evidências", + "INVESTIGAR", + "fontes primárias primeiro", + "DIAGNOSTICAR", + "ciclo curto de feedback", + "REVISAR", + "padrões × especificação", + "ECONOMIZAR", + "comprima saídas ruidosas", + "UM LOADOUT PRÁTICO", + "PLANEJAR unlazy CONSTRUIR ponytail-lite DIAGNOSTICAR diagnosing-bugs REPORTAR caveman", + "O PAPEL HUMANO", + "O agente pode ser autônomo na execução. Intenção, limites e evidências continuam sendo seus.", + "COMECE AQUI", + "Comece com um agente e uma skill. Adicione paralelismo apenas quando as tarefas forem realmente independentes.", + "Continue aprendendo", + "12 novas leituras + documentação primária", + "Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes. Estudo de caso sobre regras e enforcement → Referências primárias → Trilha com 12 leituras →" +] diff --git a/.prettierignore b/.prettierignore index 04913b6..a60d932 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,21 +9,9 @@ vote-service pnpm-lock.yaml public/submitted-skills -# Legacy site sources, slated for deletion at cutover (task 20). These are +# Unmigrated legacy sources, kept verbatim under `legacy/`. These are # hand-written files with very long lines; prettier re-wraps them into hundreds -# of changed lines the moment any agent stages one. Task 15 touched app.js to -# add four lines and produced an 829-line diff. verify.mjs asserts substrings -# against several of these, so a reformat is churn at best and a broken -# assertion at worst. -# -# Root-anchored on purpose: a bare `rules` would also swallow .agents/rules/, -# whose markdown we do want formatted. -/app.js -/styles.css -/landing.css -/chapters.css -/responsive.css -/skills-review/ -/rules/ -/skills/ -/full-guide/ +# of changed lines the moment any agent stages one. Task 15 touched the old +# app.js to add four lines and produced an 829-line diff. A reformat here is +# churn at best. +/legacy/ diff --git a/.stylelintignore b/.stylelintignore index d111dd6..0750930 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -7,23 +7,16 @@ public/submitted-skills skill-reviews vote-service -# Legacy site sources, slated for deletion at cutover (task 20). Same list and +# Unmigrated legacy stylesheets, kept verbatim under `legacy/`. Same list and # same reasoning as .prettierignore: these are minified, single-line # stylesheets. stylelint's `declaration-block-single-line-max-declarations` -# fires once per rule in them — ~180 errors for `styles.css` alone — so staging +# fires once per rule in them — ~180 errors for `guide.css` alone — so staging # one to change a single declaration blocks the commit outright. The rule is # about hand-written source readability and says nothing useful about minified -# output that is about to be deleted. +# legacy output. Migrating one of these into `src/` means bringing it up to the +# design system in the same change, at which point it gets linted like any +# other source file. # # `public/fonts/fonts.css` is deliberately NOT here: it is new, hand-written, # and must stay linted. -# -# Root-anchored on purpose: a bare `rules` would also swallow .agents/rules/. -/styles.css -/landing.css -/chapters.css -/responsive.css -/skills-review/ -/rules/ -/skills/ -/full-guide/ +/legacy/ diff --git a/AGENTS.md b/AGENTS.md index 7231a4d..6b1f90a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,11 +12,11 @@ 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 +- **Stack**: Astro, static output, no runtime dependencies. The migration + recorded in [`plans/astro-refactor/`](plans/astro-refactor/README.md) is + complete; the hand-written pages it replaced are gone. What remains unmigrated + is the editorial CSS and the review-desk modules under `legacy/`, still + imported by the pages that need them. - **Languages**: English and Brazilian Portuguese, toggled client-side - **Companion service**: `vote-service/` (Go + Kubernetes) — separate lifecycle, see its own README @@ -24,33 +24,34 @@ labs are dependency-free HTML/CSS/JS that workshop attendees point an agent at. ## Essential commands ```bash -pnpm run verify # content + interaction contracts (scripts/verify.mjs) — the gate +pnpm run dev # http://localhost:4321/ai-for-dummies/ +pnpm run build # writes dist/ — every check below reads it +bash .agents/scripts/gate.sh # the full gate: check, build, verify, audit, tokens +pnpm run verify # content + interaction contracts (scripts/verify.mjs) node scripts/audit-ui.mjs # responsive / no-external-dependency audit node scripts/build-skill-review.mjs # regenerate skill-reviews/improved/ from src/content/reviews/ -pnpm run serve # python3 -m http.server 4173 ``` -`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 +`pnpm run verify` is not a formality. It is a set of 84 string-token assertions +that pin the site's real content and interactions, read from the built output. +**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 -[`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. +actually serves, and it now carries **build output**, not a copy of `main`'s +tree. The publish job force-pushes `dist/` over it. The full procedure is in +[`docs/operations-guide.md`](docs/operations-guide.md); read +[`.agents/context/publishing.md`](.agents/context/publishing.md) before changing +it. ## 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. +- `public/hands-on/starter/` and `public/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 `src/content/reviews/*.md` instead - `vote-service/` — separate deploy lifecycle; do not fold into the site build diff --git a/GATES.md b/GATES.md index 0487e81..5299470 100644 --- a/GATES.md +++ b/GATES.md @@ -1,8 +1,12 @@ # Gates: review desk privacy and improved-draft audit -OWNS: skills-review/**, submitted-skills/Anonymous Operational Submission/**, +OWNS: src/pages/skills-review.astro, src/components/blocks/{ReviewDetail, +ChangeLens,VoteWidget,PreviewPane,FileTabs}.astro, legacy/skills-review/**, +submitted-skills/Anonymous Operational Submission/**, skill-reviews/improved/ndo-repro/**, scripts/verify.mjs +The gate commands below now read `dist/`; run `pnpm run build` before them. + Scope: Redact the operational submission's identity and URLs from the published review desk, keep package files usable in either preview mode, and explain each improved draft as a concrete diff. diff --git a/README.md b/README.md index c27acba..65235d9 100644 --- a/README.md +++ b/README.md @@ -15,39 +15,50 @@ skill links to a pinned source with an approval-first installation prompt. ## Run locally -This is a dependency-free static site: +This is an Astro static site. It builds to `dist/` and ships no runtime +dependencies. ```bash -python3 -m http.server 4173 +pnpm install +pnpm run dev # http://localhost:4321/ai-for-dummies/ +pnpm run build # writes dist/ +pnpm run preview # serves the built output ``` -Then open . - Verify the content and interaction contracts with: ```bash -pnpm run verify +pnpm run verify # reads dist/, so build first +``` + +The full gate — `astro check`, `astro build`, `verify.mjs`, `audit-ui.mjs`, +`check-tokens.mjs`, and the assertion-count floor — runs as: + +```bash +bash .agents/scripts/gate.sh ``` ## 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 -- `responsive.css` — interactive diagrams and Full HD-to-4K adaptations +- `src/pages/` — one file per route: the landing route map, the complete + bilingual `full-guide`, the chapter pages, `rules`, `skills`, and + `skills-review` +- `src/components/` — blocks and islands; the interactive diagrams, selectors, + and the language toggle +- `src/content/` — the content collections every page renders from +- `src/styles/tokens.css` — the design tokens +- `legacy/` — the editorial visual system and the review-desk modules, not yet + migrated into components. Still imported by the pages that need them; see + `.agents/context/architecture.md` +- `public/` — assets copied to the site root verbatim: fonts, the hands-on labs, + and `submitted-skills/` - `docs/references/` — bundled research sources and notes - `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 -- `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` +- `public/hands-on/starter/` — dependency-free Tiny Tasks exercise +- `public/hands-on/rules/` — dependency-free Guardrails lab; toggles rule + sources into the prompt +- `skills/` — reusable design and rules-case-study skills - `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 @@ -55,7 +66,9 @@ pnpm run verify ## Publishing The Gitea instance has a Pages Server configured to publish a repository’s -`pages` branch under `pages.marcospaulo.dev.br`. The intended site address is: +`pages` branch under `pages.marcospaulo.dev.br`. `pages` now carries the +**built** site — the contents of `dist/` — not a copy of `main`. The intended +site address is: @@ -74,7 +87,7 @@ 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 +design, and the build/push/deploy steps; `src/pages/skills-review.astro` sets `window.SKILLS_REVIEW_VOTE_API` to point at it once deployed. ## Research diff --git a/agents/index.html b/agents/index.html deleted file mode 100644 index ce8da42..0000000 --- a/agents/index.html +++ /dev/null @@ -1 +0,0 @@ -AI For Dummies — Agents and trees
← ROUTE MAP02 / AGENTS & TREESfield guide ↗

Subagent workflow

One branch
per hand.

Agents work when roles, files, and evidence are bounded. A worktree gives each worker its own checkout while the orchestrator protects intent.

The tree

Split at
the seam.

MAIN / ORCHESTRATOR├── agent/ui → components + visual states · ├── agent/tests → acceptance + regressions · └── agent/docs → guide + examples · merge after each leaf returns a diff and evidence
FRAME

Orchestrator

Owns scope, task graph, boundaries, and integration.

HAND OFF

Worker

Owns one coherent slice and one worktree.

PROVE

Verifier

Re-runs gates and reports remaining gaps.

Handoff

Context that
can travel.

01
BriefGoal, owned files, dependencies, non-goals, acceptance.
02
IsolationOne branch and worktree per independent change.
03
EvidenceCommands, result, changed files, screenshots, gaps.
diff --git a/app.js b/app.js deleted file mode 100644 index 919de04..0000000 --- a/app.js +++ /dev/null @@ -1,411 +0,0 @@ -const phases = { - plan: { model: { en: 'OPUS / REASONING', pt: 'OPUS / RACIOCÍNIO' }, title: { en: 'Turn ambiguity into work', pt: 'Transforme ambiguidade em trabalho' }, copy: { en: 'Inspect the repository, choose the architecture, split the request, and write acceptance criteria.', pt: 'Inspecione o repositório, escolha a arquitetura, divida o pedido e escreva critérios de aceitação.' }, code: { en: 'plan → decompose → define acceptance', pt: 'planejar → decompor → definir aceitação' } }, - build: { model: { en: 'SONNET, HAIKU, OR EQUIVALENT', pt: 'SONNET, HAIKU OU EQUIVALENTE' }, title: { en: 'Execute one bounded slice', pt: 'Execute uma fatia delimitada' }, copy: { en: 'Give each worker enough context, one responsibility, and its own worktree. Less context; less collision.', pt: 'Dê a cada worker contexto suficiente, uma responsabilidade e seu próprio worktree. Menos contexto; menos colisões.' }, code: { en: 'brief + worktree → implement → test', pt: 'brief + worktree → implementar → testar' } }, - review: { model: { en: 'STRONG MODEL OR HUMAN', pt: 'MODELO FORTE OU HUMANO' }, title: { en: 'Reconnect result to intent', pt: 'Reconecte o resultado à intenção' }, copy: { en: 'Check the diff against the original brief, run the checks, then merge, request changes, or discard.', pt: 'Compare o diff com o brief original, execute as verificações e então faça merge, peça mudanças ou descarte.' }, code: { en: 'diff + checks → review → merge / iterate', pt: 'diff + verificações → revisar → merge / iterar' } } -}; - -const handsOnPrompts = { - en: { - basic: [ - 'Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.', - '', - 'Add an All / Open / Done filter to Tiny Tasks.', - '', - 'Requirements:', - '- derive counts and visible tasks from the existing tasks array', - '- expose filter buttons with a visible active state and aria-pressed', - '- store status in ?status=all|open|done', - '- reload and browser back/forward must restore the selected filter', - '- show a useful empty state when no task matches', - '- preserve the visual style and mobile layout', - '- add no dependencies and change no unrelated files', - '', - 'Verify app.js syntax and exercise every filter plus URL navigation.', - 'Return changed files, checks run, results, and remaining risk.' - ].join('\n'), - skills: [ - 'Use $ponytail-lite and $webapp-testing.', - 'Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.', - '', - 'Add an All / Open / Done filter to Tiny Tasks.', - '', - 'Apply $ponytail-lite: inspect first, reuse the current render flow, prefer native URL and button APIs, and avoid dependencies or abstractions.', - 'Apply $webapp-testing: verify all filters, aria-pressed, reload, browser back/forward, empty state, and one mobile viewport.', - '', - 'Acceptance:', - '- counts and visible tasks come from the existing tasks array', - '- ?status=all|open|done is the source of truth', - '- invalid status falls back safely to all', - '- style remains consistent; unrelated files remain untouched', - '', - 'Return the smallest working diff and concrete verification evidence.' - ].join('\n') - }, - pt: { - basic: [ - 'Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências.', - '', - 'Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks.', - '', - 'Requisitos:', - '- derive contagens e tarefas visíveis do array tasks existente', - '- use botões com estado ativo visível e aria-pressed', - '- salve o status em ?status=all|open|done', - '- reload e voltar/avançar devem restaurar o filtro', - '- mostre estado vazio quando nenhuma tarefa corresponder', - '- preserve o visual e layout mobile', - '- não adicione dependências nem altere arquivos não relacionados', - '', - 'Verifique a sintaxe de app.js e teste filtros e navegação por URL.', - 'Retorne arquivos alterados, checks, resultados e risco restante.' - ].join('\n'), - skills: [ - 'Use $ponytail-lite e $webapp-testing.', - 'Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências.', - '', - 'Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks.', - '', - 'Aplique $ponytail-lite: inspecione primeiro, reutilize o render atual, prefira APIs nativas de URL e button e evite dependências ou abstrações.', - 'Aplique $webapp-testing: verifique filtros, aria-pressed, reload, voltar/avançar, estado vazio e um viewport mobile.', - '', - 'Aceitação:', - '- contagens e tarefas visíveis vêm do array tasks existente', - '- ?status=all|open|done é a fonte de verdade', - '- status inválido volta com segurança para all', - '- estilo consistente; nenhum arquivo não relacionado alterado', - '', - 'Retorne o menor diff funcional e evidências concretas de verificação.' - ].join('\n') - } -}; - -const modelGuide = { - providers: { - openai: { - label: 'OpenAI', source: 'https://developers.openai.com/api/docs/guides/latest-model', - title: { en: 'Sol · Terra · Luna', pt: 'Sol · Terra · Luna' }, - copy: { en: 'GPT-5.6 separates capability tier from reasoning effort. Sol is flagship, Terra balances performance and cost, and Luna targets efficient high-volume work.', pt: 'O GPT-5.6 separa o nível de capacidade do esforço de raciocínio. Sol é flagship, Terra equilibra desempenho e custo, e Luna atende trabalho eficiente em alto volume.' }, - tiers: [ - ['STRONG', 'Sol', { en: 'orchestration + hard judgment', pt: 'orquestração + julgamento difícil' }], - ['BALANCED', 'Terra', { en: 'normal implementation', pt: 'implementação normal' }], - ['FAST', 'Luna', { en: 'bounded, high-volume work', pt: 'trabalho delimitado e volumoso' }] - ], - config: 'reasoning: { effort: "medium" }' - }, - claude: { - label: 'Claude', source: 'https://docs.anthropic.com/en/docs/claude-code/model-config', - title: { en: 'Opus · Sonnet · Haiku', pt: 'Opus · Sonnet · Haiku' }, - copy: { en: 'Claude Code exposes memorable aliases. Opus handles complex reasoning, Sonnet everyday coding, and Haiku simple fast work. The opusplan alias can plan with Opus and execute with Sonnet.', pt: 'Claude Code oferece aliases fáceis de lembrar. Opus cuida de raciocínio complexo, Sonnet do código cotidiano e Haiku de trabalho simples e rápido. O alias opusplan pode planejar com Opus e executar com Sonnet.' }, - tiers: [ - ['STRONG', 'Opus', { en: 'planning + architecture', pt: 'planejamento + arquitetura' }], - ['BALANCED', 'Sonnet', { en: 'everyday coding', pt: 'código cotidiano' }], - ['FAST', 'Haiku', { en: 'simple, fast tasks', pt: 'tarefas simples e rápidas' }] - ], - config: '/model opus · /model sonnet · /model haiku' - }, - gemini: { - label: 'Gemini', source: 'https://ai.google.dev/gemini-api/docs/thinking', - title: { en: 'Pro · Flash · Flash-Lite', pt: 'Pro · Flash · Flash-Lite' }, - copy: { en: 'Gemini uses model families rather than interchangeable aliases. Pro targets complex reasoning, Flash balances capability and throughput, and Flash-Lite prioritizes latency and cost.', pt: 'Gemini usa famílias de modelos, não aliases intercambiáveis. Pro mira raciocínio complexo, Flash equilibra capacidade e throughput, e Flash-Lite prioriza latência e custo.' }, - tiers: [ - ['STRONG', 'Pro', { en: 'complex reasoning', pt: 'raciocínio complexo' }], - ['BALANCED', 'Flash', { en: 'capability + throughput', pt: 'capacidade + throughput' }], - ['FAST', 'Flash-Lite', { en: 'latency + cost', pt: 'latência + custo' }] - ], - config: 'thinkingConfig: { thinkingLevel: "MEDIUM" }' - } - }, - efforts: { - low: { en: ['LOW', 'Use for formatting, lookup, narrow edits, and well-specified worker tasks. Optimize for fast feedback.', 'bounded task → low'], pt: ['BAIXO', 'Use para formatação, consulta, edições estreitas e tarefas de worker bem especificadas. Otimize para feedback rápido.', 'tarefa delimitada → baixo'] }, - medium: { en: ['MEDIUM', 'Balanced starting point for normal implementation, tests, and review. Measure before moving up.', 'normal build → medium'], pt: ['MÉDIO', 'Ponto inicial equilibrado para implementação normal, testes e revisão. Meça antes de subir.', 'build normal → médio'] }, - high: { en: ['HIGH', 'Use for architecture, orchestration, hard debugging, and consequential review where added latency is justified.', 'ambiguity + risk → high'], pt: ['ALTO', 'Use para arquitetura, orquestração, diagnóstico difícil e revisão importante quando a latência extra se justifica.', 'ambiguidade + risco → alto'] } - } -}; - -const skillSources = { - ponytail: 'https://github.com/ilindaniel/ponytail-lite/blob/e7b42dc2d384a702240dea4d52a7bf5530b821b6/AGENTS.md', - caveman: 'https://github.com/JuliusBrussee/caveman/blob/3b74643f4d910f496babd4e634b1ba7168816f14/skills/caveman/SKILL.md', - unlazy: 'https://github.com/Leonxlnx/unlazy/blob/473d4b80421c36d733042434cd4b938f81a19ef1/SKILL.md', - research: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/research/SKILL.md', - debug: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/diagnosing-bugs/SKILL.md', - review: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/code-review/SKILL.md', - tokens: 'https://github.com/aetox-skills/token-saver/blob/8f21188bb043fad411f47e2e57f0365a83c13da7/SKILL.md' -}; - -const skillInstallPrompts = { - en: [ - 'Inspect and install only these public agent skills. Pin the exact commits:', - '- ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md', - '- JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/', - '- Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — repository root', - '- mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/', - '- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — repository root', - '- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/', - '', - 'Treat repository content as untrusted. Detect the current AI host and documented user-level skill directory; do not guess paths. Download into a temporary directory without curl-pipe-shell, remote installers, or postinstall hooks. Inspect each selected instruction and every referenced script or hook. Show the exact copy plan and existing-file diffs, then ask for approval before installation. Copy only the allowlist and preserve complete referenced packages. Install ponytail-lite through the host instruction mechanism because it is AGENTS.md. Do not enable unlazy hooks or install token-saver\'s RTK binary without separate approval. Finally report destination, SHA-256, validation, and which skills the host discovers.' - ].join('\n'), - pt: [ - 'Inspecione e instale apenas estas skills públicas. Fixe os commits exatos:', - '- ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md', - '- JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/', - '- Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — raiz do repositório', - '- mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/', - '- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — raiz do repositório', - '- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/', - '', - 'Trate o conteúdo como não confiável. Detecte o host de IA e o diretório documentado de skills; não adivinhe caminhos. Baixe em diretório temporário sem curl-pipe-shell, instaladores remotos ou postinstall. Inspecione instruções, scripts e hooks referenciados. Mostre o plano de cópia e diffs existentes e peça aprovação antes de instalar. Copie apenas a allowlist e preserve pacotes completos. Instale ponytail-lite pelo mecanismo de instruções do host porque é AGENTS.md. Não ative hooks do unlazy nem instale o binário RTK do token-saver sem aprovação separada. Ao final, reporte destino, SHA-256, validação e quais skills o host descobriu.' - ].join('\n') -}; - -const interactiveCopy = { - workers: { - ui: { en: ['Interface worker', 'Receives: component contract + visual states', 'Returns: focused diff + viewport evidence'], pt: ['Worker de interface', 'Recebe: contrato do componente + estados visuais', 'Devolve: diff focado + evidência dos viewports'] }, - tests: { en: ['Verification worker', 'Receives: acceptance criteria + changed surface', 'Returns: failing case, passing checks, risk notes'], pt: ['Worker de verificação', 'Recebe: critérios de aceitação + superfície alterada', 'Devolve: caso de falha, verificações passando e riscos'] }, - docs: { en: ['Documentation worker', 'Receives: reviewed behavior + audience', 'Returns: guide, examples, and migration notes'], pt: ['Worker de documentação', 'Recebe: comportamento revisado + público', 'Devolve: guia, exemplos e notas de migração'] } - }, - trees: { - main: { status: 'clean', owner: { en: 'Orchestrator', pt: 'Orquestrador' }, path: './project', command: 'git worktree list', note: { en: 'Shared history and integration point. Workers never edit here.', pt: 'Histórico compartilhado e ponto de integração. Workers nunca editam aqui.' } }, - ui: { status: 'working', owner: { en: 'UI worker', pt: 'Worker de UI' }, path: '../task-ui', command: 'git worktree add ../task-ui -b agent/ui', note: { en: 'Own checkout and index. Safe to change presentation files in parallel.', pt: 'Checkout e índice próprios. Seguro para alterar a apresentação em paralelo.' } }, - tests: { status: 'ready', owner: { en: 'Test worker', pt: 'Worker de testes' }, path: '../task-tests', command: 'git diff main...agent/tests', note: { en: 'Checks are green. Review the diff before merging into main.', pt: 'Verificações passaram. Revise o diff antes do merge em main.' } }, - docs: { status: 'review', owner: { en: 'Docs worker', pt: 'Worker de docs' }, path: '../task-docs', command: 'git merge --no-ff agent/docs', note: { en: 'Review requested. Merge, request changes, or discard without touching another checkout.', pt: 'Revisão solicitada. Faça merge, peça mudanças ou descarte sem tocar em outro checkout.' } } - }, - routes: { - plan: { score: 92, label: { en: 'High ambiguity', pt: 'Alta ambiguidade' }, why: { en: 'Architecture and decomposition have a wide error surface. Spend reasoning here.', pt: 'Arquitetura e decomposição têm grande superfície de erro. Invista raciocínio aqui.' } }, - build: { score: 38, label: { en: 'Bounded execution', pt: 'Execução delimitada' }, why: { en: 'The brief already removed ambiguity. Optimize for speed and tight feedback.', pt: 'O brief já removeu a ambiguidade. Otimize para velocidade e feedback curto.' } }, - explore: { score: 22, label: { en: 'Read-only discovery', pt: 'Descoberta somente leitura' }, why: { en: 'Search, map, and report. A lightweight model can return facts without editing.', pt: 'Busque, mapeie e reporte. Um modelo leve devolve fatos sem editar.' } }, - review: { score: 74, label: { en: 'Independent judgment', pt: 'Julgamento independente' }, why: { en: 'Reconnect the diff to intent with fresh context and adversarial attention.', pt: 'Reconecte o diff à intenção com contexto novo e atenção crítica.' } } - }, - skillFiles: { - skill: { icon: '◇', title: 'SKILL.md', en: 'Trigger, procedure, constraints, and the exact evidence the agent must return.', pt: 'Gatilho, procedimento, restrições e a evidência exata que o agente deve devolver.' }, - references: { icon: '≡', title: 'references/', en: 'Stable facts loaded only when the procedure needs them. Keep the main instruction lean.', pt: 'Fatos estáveis carregados apenas quando o procedimento precisa. Mantenha a instrução principal enxuta.' }, - scripts: { icon: '›_', title: 'scripts/', en: 'Deterministic checks and repeated operations. Prefer executable proof over prose.', pt: 'Verificações determinísticas e operações repetidas. Prefira prova executável a prosa.' }, - assets: { icon: '▧', title: 'assets/', en: 'Templates and examples the agent can copy without reinventing the expected shape.', pt: 'Templates e exemplos que o agente pode copiar sem reinventar o formato esperado.' } - }, - skillWorkflow: { - observe: { number: '01', title: { en: 'Start from repeated friction', pt: 'Comece pelo atrito repetido' }, question: { en: 'Which non-obvious decision keeps being rediscovered?', pt: 'Qual decisão não óbvia continua sendo redescoberta?' }, action: { en: 'Collect two or three realistic requests. Separate durable judgment from one project’s temporary details.', pt: 'Colete dois ou três pedidos realistas. Separe julgamento durável dos detalhes temporários de um projeto.' }, output: { en: 'A narrow capability and concrete examples.', pt: 'Uma capacidade estreita e exemplos concretos.' }, proof: { en: 'Without the skill, agents repeatedly make the same avoidable mistake.', pt: 'Sem a skill, agentes repetem o mesmo erro evitável.' } }, - trigger: { number: '02', title: { en: 'Make discovery precise', pt: 'Torne a descoberta precisa' }, question: { en: 'When should this load—and when should it stay out?', pt: 'Quando isto deve carregar — e quando deve ficar de fora?' }, action: { en: 'Choose a short action-oriented name. Write a discriminating description that names the task and meaningful boundary.', pt: 'Escolha um nome curto orientado à ação. Escreva uma descrição discriminante que nomeie a tarefa e seu limite.' }, output: { en: 'YAML name + description in SKILL.md.', pt: 'Nome + descrição YAML em SKILL.md.' }, proof: { en: 'Relevant prompts select it; nearby unrelated prompts do not.', pt: 'Prompts relevantes selecionam; prompts próximos mas não relacionados, não.' } }, - scaffold: { number: '03', title: { en: 'Choose only useful anatomy', pt: 'Escolha apenas a anatomia útil' }, question: { en: 'What must be instructions, executable, consulted, or copied?', pt: 'O que deve ser instrução, executável, consultado ou copiado?' }, action: { en: 'Keep shared guidance in SKILL.md. Add scripts for repeated deterministic work, references for conditional facts, and assets for generated output.', pt: 'Mantenha orientação comum em SKILL.md. Adicione scripts para trabalho determinístico, referências para fatos condicionais e assets para saída.' }, output: { en: 'Smallest folder structure that supports the workflow.', pt: 'A menor estrutura de pastas que sustenta o fluxo.' }, proof: { en: 'Every file has a real caller; no placeholder directories.', pt: 'Cada arquivo tem um consumidor real; nenhuma pasta placeholder.' } }, - write: { number: '04', title: { en: 'Write what changes decisions', pt: 'Escreva o que muda decisões' }, question: { en: 'What would a capable agent still get wrong?', pt: 'O que um agente capaz ainda erraria?' }, action: { en: 'State outcome, non-obvious constraints, routing, and stopping conditions. Remove generic advice, duplicate facts, and speculative rules.', pt: 'Declare resultado, restrições não óbvias, roteamento e condições de parada. Remova conselhos genéricos, fatos duplicados e regras especulativas.' }, output: { en: 'Lean SKILL.md with progressive links.', pt: 'SKILL.md enxuto com links progressivos.' }, proof: { en: 'Another agent can act correctly without loading irrelevant detail.', pt: 'Outro agente consegue agir corretamente sem carregar detalhes irrelevantes.' } }, - validate: { number: '05', title: { en: 'Test behavior, then sharpen', pt: 'Teste comportamento, depois refine' }, question: { en: 'Did the skill improve a realistic outcome?', pt: 'A skill melhorou um resultado realista?' }, action: { en: 'Run structural validation, execute every new script, and forward-test realistic requests. Fix observed failures with the narrowest rule.', pt: 'Execute validação estrutural, rode cada script novo e teste pedidos realistas. Corrija falhas observadas com a regra mais estreita.' }, output: { en: 'Validated package plus evidence from real use.', pt: 'Pacote validado mais evidência de uso real.' }, proof: { en: 'quick_validate passes and behavior improves without unrelated side effects.', pt: 'quick_validate passa e o comportamento melhora sem efeitos colaterais.' } } - }, - commonSkills: { - ponytail: { number: '01', kind: { en: 'SIMPLIFICATION INSTINCT', pt: 'INSTINTO DE SIMPLIFICAÇÃO' }, title: 'ponytail-lite', rule: { en: 'Stop at the first rung that holds.', pt: 'Pare no primeiro degrau que sustenta.' }, use: { en: 'Use when a request invites frameworks, dependencies, abstractions, or speculative scaffolding. It checks reuse, standard library, and native platform features before adding code.', pt: 'Use quando um pedido convida frameworks, dependências, abstrações ou scaffolding especulativo. Verifica reúso, biblioteca padrão e recursos nativos antes de adicionar código.' }, example: { en: 'Date picker? Start with <input type="date">.', pt: 'Seletor de data? Comece com <input type="date">.' }, caution: { en: 'Never simplify away security, accessibility, validation, or real edge cases.', pt: 'Nunca simplifique segurança, acessibilidade, validação ou casos extremos reais.' } }, - caveman: { number: '02', kind: { en: 'COMMUNICATION STYLE', pt: 'ESTILO DE COMUNICAÇÃO' }, title: 'caveman', rule: { en: 'Signal first. Drop filler.', pt: 'Sinal primeiro. Corte o excesso.' }, use: { en: 'Use for routine status, handoffs, and technical summaries where speed matters. Short fragments make actions and evidence easy to scan.', pt: 'Use em status, handoffs e resumos técnicos rotineiros onde velocidade importa. Fragmentos curtos facilitam localizar ações e evidências.' }, example: { en: 'Built. Tests pass. Published.', pt: 'Feito. Testes passaram. Publicado.' }, caution: { en: 'Drop the style for security warnings, irreversible actions, and sequences where terse wording can be misread.', pt: 'Abandone o estilo em alertas de segurança, ações irreversíveis e sequências onde concisão pode causar erro.' } }, - unlazy: { number: '03', kind: { en: 'COMPLETION DISCIPLINE', pt: 'DISCIPLINA DE CONCLUSÃO' }, title: 'unlazy', rule: { en: 'Define observable gates. Finish against evidence.', pt: 'Defina gates observáveis. Termine com evidências.' }, use: { en: 'Use for substantial autonomous builds, audits, and parallel work where quiet omissions are expensive. It turns “done” into runnable acceptance checks.', pt: 'Use em builds autônomos grandes, auditorias e trabalho paralelo onde omissões custam caro. Transforma “pronto” em verificações executáveis.' }, example: { en: 'Gate: language toggle persists. Check: browser reload. Expect: pt-BR.', pt: 'Gate: idioma persiste. Check: recarregar navegador. Esperado: pt-BR.' }, caution: { en: 'Too heavy for trivial edits or factual answers.', pt: 'Pesado demais para edições triviais ou respostas factuais.' } }, - research: { number: '04', kind: { en: 'SOURCE DISCIPLINE', pt: 'DISCIPLINA DE FONTES' }, title: 'research', rule: { en: 'Trace claims to owners.', pt: 'Leve afirmações até suas fontes.' }, use: { en: 'Use when APIs, standards, architecture facts, or current behavior must be verified. Capture findings in a cited note, prioritizing primary sources.', pt: 'Use quando APIs, padrões, fatos de arquitetura ou comportamento atual precisam ser verificados. Registre achados citados, priorizando fontes primárias.' }, example: { en: 'Git behavior → git-scm.com docs, not a remembered blog summary.', pt: 'Comportamento do Git → documentação git-scm.com, não memória de um blog.' }, caution: { en: 'Practitioner articles add context; they do not override official behavior.', pt: 'Artigos de praticantes dão contexto; não substituem comportamento oficial.' } }, - debug: { number: '05', kind: { en: 'DIAGNOSTIC LOOP', pt: 'CICLO DE DIAGNÓSTICO' }, title: 'diagnosing-bugs', rule: { en: 'No red-capable loop, no theory.', pt: 'Sem ciclo capaz de falhar, sem teoria.' }, use: { en: 'Use for hard bugs, flakes, and regressions. First build a fast deterministic reproduction, then minimize, rank hypotheses, instrument, and fix the root cause.', pt: 'Use para bugs difíceis, flakes e regressões. Primeiro crie reprodução rápida e determinística; depois minimize, ranqueie hipóteses, instrumente e corrija a causa raiz.' }, example: { en: 'One command reproduces the exact symptom before any fix.', pt: 'Um comando reproduz o sintoma exato antes de qualquer correção.' }, caution: { en: 'Do not jump from error message straight to a patch.', pt: 'Não pule da mensagem de erro direto para um patch.' } }, - review: { number: '06', kind: { en: 'INDEPENDENT REVIEW', pt: 'REVISÃO INDEPENDENTE' }, title: 'code-review', rule: { en: 'Check standards and intent separately.', pt: 'Verifique padrões e intenção separadamente.' }, use: { en: 'Use on a branch or PR. One axis checks repository standards; another checks whether the change actually satisfies its originating specification.', pt: 'Use em branch ou PR. Um eixo verifica padrões do repositório; outro verifica se a mudança realmente satisfaz a especificação original.' }, example: { en: 'Clean code can still solve the wrong problem.', pt: 'Código limpo ainda pode resolver o problema errado.' }, caution: { en: 'Pin the comparison point and source specification before reviewing.', pt: 'Fixe o ponto de comparação e a especificação antes de revisar.' } }, - tokens: { number: '07', kind: { en: 'CONTEXT ECONOMY', pt: 'ECONOMIA DE CONTEXTO' }, title: 'token-saver', rule: { en: 'Keep signal. Strip command noise.', pt: 'Mantenha sinal. Corte ruído de comandos.' }, use: { en: 'Use around verbose tests, builds, Git output, and logs. Filtering preserves context for reasoning while retaining full failure output for recovery.', pt: 'Use em testes, builds, saídas Git e logs verbosos. Filtragem preserva contexto para raciocínio e mantém falhas completas para recuperação.' }, example: { en: '200 passing-test lines → one result; failures keep their trace.', pt: '200 linhas de testes passando → um resultado; falhas mantêm o trace.' }, caution: { en: 'Read raw output when exact wording or full diffs matter.', pt: 'Leia saída bruta quando texto exato ou diffs completos importarem.' } } - } -}; - -const translations = { - pt: { - '.chapter-links a:nth-child(1)': '01 frota', '.chapter-links a:nth-child(2)': '02 worktrees', '.chapter-links a:nth-child(3)': '03 modelos', '.chapter-links a:nth-child(4)': '04 skills', '.chapter-links a:nth-child(5)': '05 criar', '.chapter-links a:nth-child(6)': '06 kit de campo', '.chapter-links a:nth-child(7)': '07 prática', '.edition': 'ENGENHARIA DE IA 01 / 2026', - '.hero .eyebrow': 'Uma apresentação para quem entrega software', '.lede': 'Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.', '.hero-index span': 'NOTA DE CAMPO / 001', '.hero-index strong': 'Entregue o
sistema.', '.hero-index small': 'Skills · agentes · worktrees · evidências', - '.hero-stats div:nth-child(1) span': 'modelo forte
para ambiguidade', '.hero-stats div:nth-child(2) span': 'workers delimitados
em paralelo', '.hero-stats div:nth-child(3) span': 'iterações
com evidências', '.hero-stats p': 'Leia isto como um mapa de rota, não como uma receita de prompt.', - '.thesis span': 'REGRA ZERO', '.thesis strong': 'Modelo forte para ambiguidade.
Modelo leve para trabalho delimitado.', '.fleet .section-label span:nth-child(1)': 'Uma pequena frota', '.fleet .section-label span:nth-child(2)': 'coordenação antes do paralelismo', '.captain span': 'ORQUESTRADOR', '.captain h2': 'Decide o que
precisa acontecer.', '.worker-card[data-worker="ui"] strong': 'Componentes e estados visuais', '.worker-card[data-worker="tests"] strong': 'Casos de aceitação', '.worker-card[data-worker="docs"] strong': 'Guia e exemplos', '.caption': 'O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados verificáveis. Ele não precisa digitar cada linha.', - '.failure-map .section-label span:nth-child(1)': 'Por que a fronteira importa', '.failure-map .section-label span:nth-child(2)': 'uma tarefa vaga / três falhas previsíveis', '.failure-grid article:nth-child(1) strong': 'Sopa de contexto', '.failure-grid article:nth-child(1) p': 'Cada worker lê tudo. Ninguém sabe quais fatos são essenciais.', '.failure-grid article:nth-child(2) strong': 'Colisão de branches', '.failure-grid article:nth-child(2) p': 'Dois agentes usam o mesmo checkout. O caminho mais rápido vira resolução de conflitos.', '.failure-grid article:nth-child(3) strong': 'Desvio confiante', '.failure-grid article:nth-child(3) p': 'O diff parece ótimo, mas ninguém verifica se resolveu o problema original.', - '.workflow .eyebrow': 'O ciclo de subagentes', '#workflow-title': 'Clique em uma fase.
Veja a passagem.', '.workflow .copy > p:last-child': 'Delegar é mover uma tarefa delimitada para um contexto menor — não abrir mão da responsabilidade.', '.handoff .section-label span:nth-child(1)': 'O que atravessa contextos', '.handoff .section-label span:nth-child(2)': 'brief → diff → evidência', '.handoff thead th:nth-child(1)': 'Pacote', '.handoff thead th:nth-child(2)': 'Contém', '.handoff thead th:nth-child(3)': 'Por que importa', - '.worktrees .eyebrow': 'Git worktrees', '.worktrees h2': 'Uma branch
por mão.', '.worktree-intro > p:nth-of-type(2)': 'Um worktree é outro diretório ligado ao mesmo repositório. Cada agente recebe seu próprio checkout e índice; o histórico continua compartilhado.', '.worktree-intro .interaction-hint': 'Selecione um nó para inspecionar checkout, responsável e próxima ação.', '.tree-toolbar > span:first-child': 'topologia do repositório', '.tree-live': ' 4 checkouts', '.tree-node.root span': 'RAIZ', '.tree-node.ui span': 'AGENTE DE UI', '.tree-node.tests span': 'AGENTE DE TESTES', '.tree-node.docs span': 'AGENTE DE DOCS', '.tree-node.root small': '● limpo', '.tree-node.ui small': '3 arquivos · trabalhando', '.tree-node.tests small': '8 verificações · pronto', '.tree-node.docs small': '2 páginas · revisão', '.routing .eyebrow': 'Roteamento de modelos', '.routing h2': 'Não pague por
raciocínio onde precisa
de ritmo.', '.routing .interaction-hint': 'Escolha um trabalho para entender por que o perfil do modelo muda.', '.route-table .head span:nth-child(1)': 'Trabalho', '.route-table .head span:nth-child(2)': 'Perfil', '.route-table .head span:nth-child(3)': 'Formato do prompt', '.route-table [data-route="plan"] strong': 'Planejar', '.route-table [data-route="build"] strong': 'Construir', '.route-table [data-route="explore"] strong': 'Explorar', '.route-table [data-route="review"] strong': 'Revisar', '.skills .eyebrow': 'Skills', '.skills h2': 'Escreva do jeito certo
uma vez.', '.skills > div:first-child > p': 'Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências, scripts e assets. Não é memória mágica e não substitui critérios de aceitação.', - '.skill-principles span:nth-child(1)': '01 / defina o gatilho', '.skill-principles span:nth-child(2)': '02 / carregue detalhes sob demanda', '.skill-principles span:nth-child(3)': '03 / devolva evidências', '.skill-package > span': 'PACOTE DE SKILL', '.skill-catalog .section-label span:nth-child(1)': 'Skills comuns', '.skill-catalog .section-label span:nth-child(2)': 'escolha o comportamento antes do modelo', '.catalog-intro .eyebrow': 'O kit de campo', '.catalog-intro h2': 'Trabalhos diferentes.
Instintos diferentes.', '.catalog-intro > p': 'Uma skill muda como o agente aborda o trabalho. Algumas moldam a comunicação. Outras impõem pesquisa, diagnóstico, revisão ou disciplina de conclusão. Selecione uma para inspecionar sua regra operacional.', '[data-common-skill="ponytail"] span': 'SIMPLIFICAR', '[data-common-skill="ponytail"] small': 'código mínimo que funciona', '[data-common-skill="caveman"] span': 'COMUNICAR', '[data-common-skill="caveman"] small': 'sinal sem excesso', '[data-common-skill="unlazy"] span': 'CONCLUIR', '[data-common-skill="unlazy"] small': 'gates e evidências', '[data-common-skill="research"] span': 'INVESTIGAR', '[data-common-skill="research"] small': 'fontes primárias primeiro', '[data-common-skill="debug"] span': 'DIAGNOSTICAR', '[data-common-skill="debug"] small': 'ciclo curto de feedback', '[data-common-skill="review"] span': 'REVISAR', '[data-common-skill="review"] small': 'padrões × especificação', '[data-common-skill="tokens"] span': 'ECONOMIZAR', '[data-common-skill="tokens"] small': 'comprima saídas ruidosas', '.skill-loadout > span': 'UM LOADOUT PRÁTICO', '.skill-loadout > div': 'PLANEJAR unlazy CONSTRUIR ponytail-lite DIAGNOSTICAR diagnosing-bugs REPORTAR caveman', '.rule span': 'O PAPEL HUMANO', '.rule strong': 'O agente pode ser autônomo na execução. Intenção, limites e evidências continuam sendo seus.', '.callout span': 'COMECE AQUI', '.callout strong': 'Comece com um agente e uma skill. Adicione paralelismo apenas quando as tarefas forem realmente independentes.', '.sources .section-label span:nth-child(1)': 'Continue aprendendo', '.sources .section-label span:nth-child(2)': '12 novas leituras + documentação primária', '.sources p': 'Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes. Estudo de caso sobre regras e enforcement → Referências primárias → Trilha com 12 leituras →' - } -}; - -Object.assign(translations.pt, { - '.model-gearbox .section-label span:nth-child(1)': 'Câmbio de modelos', '.model-gearbox .section-label span:nth-child(2)': 'nível de capacidade × esforço de raciocínio', - '.gearbox-intro .eyebrow': 'Dois controles separados', '.gearbox-intro h2': 'Escolha o motor.
Depois escolha a marcha.', - '.gearbox-intro > p': 'Um modelo mais forte muda o teto de capacidade. Mais esforço de raciocínio dá mais espaço para esse modelo trabalhar. Comece com a combinação mais leve que passa seus checks e mova um controle por vez.', - '.effort-rail > span': 'RACIOCÍNIO / PENSAMENTO', '[data-effort="low"] b': 'BAIXO', '[data-effort="low"] small': 'delimitado + rápido', '[data-effort="medium"] b': 'MÉDIO', '[data-effort="medium"] small': 'ponto inicial', '[data-effort="high"] b': 'ALTO', '[data-effort="high"] small': 'complexo + custoso', - '.gearbox-rule span': 'REGRA DE ROTEAMENTO', '.gearbox-rule strong': 'Use modelos fortes para ambiguidade e julgamento. Use modelos leves para execução delimitada. Aumente o esforço apenas quando a avaliação mostrar ganho.', - '.skill-builder .section-label span:nth-child(1)': 'Criar uma skill', - '.skill-builder .section-label span:nth-child(2)': 'atrito repetido → julgamento reutilizável', - '.builder-intro .eyebrow': 'A forja de skills', - '.builder-intro h2': 'Ensine a decisão.
Mantenha o contexto leve.', - '.builder-intro > p': 'Não empacote tudo o que você sabe. Capture as escolhas não óbvias que melhoram resultados repetidamente e prove que a skill muda o comportamento.', - '[data-skill-step="observe"] span': 'Observar', '[data-skill-step="observe"] small': 'encontre atrito repetido', - '[data-skill-step="trigger"] span': 'Definir gatilho', '[data-skill-step="trigger"] small': 'roteie com precisão', - '[data-skill-step="scaffold"] span': 'Escolher anatomia', '[data-skill-step="scaffold"] small': 'apenas arquivos necessários', - '[data-skill-step="write"] span': 'Escrever orientação', '[data-skill-step="write"] small': 'decisões, não trivialidades', - '[data-skill-step="validate"] span': 'Validar', '[data-skill-step="validate"] small': 'teste comportamento real', - '.artifact-head span': 'SAÍDA / PACOTE DE SKILL', '.artifact-command span': 'VALIDAR', - '.builder-loop > span': 'APÓS USO REAL', - '.builder-loop > div': 'observar falharefinar uma regraretestar comportamentomanter estreita', - '.install-skills header span': 'PACOTE DE INSTALAÇÃO', '.install-skills header strong': 'Peça ao seu agente para verificar, instalar e validar as skills.', - '.install-skills footer': 'Revise cada fonte antes da instalação. Skills locais existentes devem ser preservadas.', - '.hands-on .section-label span:nth-child(1)': 'Prática', '.hands-on .section-label span:nth-child(2)': '10 minutos / uma feature ausente', - '.hands-intro .eyebrow': 'Laboratório Tiny Tasks', '.hands-intro h2': 'Mesma tarefa.
Melhor sistema operacional.', - '.hands-intro > div:last-child > p': 'Comece com um quadro estático propositalmente incompleto. Execute um prompt, restaure e execute a versão com skills. Compare tamanho do diff, evidências e complexidade desnecessária.', - '.starter-link': 'Abrir o projeto inicial →', '.exercise-brief > span': 'A FEATURE AUSENTE', - '.exercise-brief > strong': 'Adicione filtros Todos / Abertos / Concluídos que sobrevivem reload e navegação.', - '.exercise-brief > div': 'STACK HTML · CSS · JavaScript DEPENDÊNCIAS nenhuma ARQUIVOS 3', - '.prompt-card:first-child header strong': 'Bom prompt', '.prompt-card.enhanced header strong': 'Bom prompt + skills', - '.prompt-card:first-child footer': 'Contexto claro · restrições · aceitação · evidência', '.prompt-card.enhanced footer': 'Mesmo contrato · métodos explícitos · prova mais forte', - '.comparison-strip > span': 'COMPARE AS EXECUÇÕES', '.comparison-strip > div:nth-child(2)': '01 Arquivos alterados', '.comparison-strip > div:nth-child(3)': '02 Novas dependências', '.comparison-strip > div:nth-child(4)': '03 Checks executados', '.comparison-strip > div:nth-child(5)': '04 Evidências retornadas' -}); - -const panel = document.querySelector('#phase-panel'); -const buttons = document.querySelectorAll('[data-phase]'); -const originals = new Map(); -let currentLanguage = 'en'; - -function setText(selector, value) { - const nodes = document.querySelectorAll(selector); - if (!nodes.length) return; - if (!originals.has(selector)) originals.set(selector, [...nodes].map((node) => node.innerHTML)); - nodes.forEach((node) => { node.innerHTML = value; }); -} - -function render(id) { - const phase = phases[id]; - panel.innerHTML = `
${phase.model[currentLanguage]}${currentLanguage === 'pt' ? 'contexto: isolado' : 'context: isolated'}

${phase.title[currentLanguage]}

${phase.copy[currentLanguage]}

${phase.code[currentLanguage]}`; - buttons.forEach((button) => { const active = button.dataset.phase === id; button.classList.toggle('active', active); button.setAttribute('aria-selected', String(active)); }); -} - -function selectButtons(selector, activeValue, key) { - document.querySelectorAll(selector).forEach((button) => { - const active = button.dataset[key] === activeValue; - button.classList.toggle('active', active); - button.setAttribute(button.hasAttribute('aria-selected') ? 'aria-selected' : 'aria-pressed', String(active)); - }); -} - -function renderWorker(id) { - const item = interactiveCopy.workers[id][currentLanguage]; - document.querySelector('#worker-detail').innerHTML = `${item[0]}${item[1]}${item[2]}`; - selectButtons('[data-worker]', id, 'worker'); -} - -function renderTree(id) { - const item = interactiveCopy.trees[id]; - const language = currentLanguage; - document.querySelector('#tree-detail').innerHTML = `
${language === 'pt' ? 'RESPONSÁVEL' : 'OWNER'}${item.owner[language]}
CHECKOUT${item.path}

${item.note[language]}

${item.command}`; - selectButtons('[data-tree]', id, 'tree'); -} - -function renderRoute(id) { - const item = interactiveCopy.routes[id]; - const language = currentLanguage; - document.querySelector('#route-detail').innerHTML = `
${language === 'pt' ? 'CARGA DE RACIOCÍNIO' : 'REASONING LOAD'} · ${item.score}${item.label[language]}

${item.why[language]}

`; - selectButtons('[data-route]', id, 'route'); -} - -function renderModelProvider(id) { - const item = modelGuide.providers[id]; - const language = currentLanguage; - const sourceLabel = language === 'pt' ? 'FONTE OFICIAL ↗' : 'OFFICIAL SOURCE ↗'; - const kindLabels = language === 'pt' ? { STRONG: 'FORTE', BALANCED: 'EQUILÍBRIO', FAST: 'RÁPIDO' } : {}; - const tiers = item.tiers.map(([kind, name, note]) => `
${kindLabels[kind] || kind}${name}${note[language]}
`).join(''); - document.querySelector('#provider-detail').innerHTML = `
${item.label}${sourceLabel}

${item.title[language]}

${item.copy[language]}

${tiers}
`; - selectButtons('[data-model-provider]', id, 'modelProvider'); -} - -function renderEffort(id) { - const item = modelGuide.efforts[id][currentLanguage]; - const provider = modelGuide.providers[document.querySelector('[data-model-provider].active')?.dataset.modelProvider || 'openai']; - document.querySelector('#effort-detail').innerHTML = `${item[0]}

${item[1]}

${provider.config}`; - selectButtons('[data-effort]', id, 'effort'); -} - -function renderSkillFile(id) { - const item = interactiveCopy.skillFiles[id]; - document.querySelector('#skill-detail').innerHTML = `${item.icon}
${item.title}

${item[currentLanguage]}

${currentLanguage === 'pt' ? 'clique em outro arquivo para explorar' : 'select another file to explore'}
`; - selectButtons('[data-skill-file]', id, 'skillFile'); -} - -function renderSkillWorkflow(id) { - const item = interactiveCopy.skillWorkflow[id]; - const language = currentLanguage; - const labels = language === 'pt' - ? ['PERGUNTA', 'AÇÃO', 'ARTEFATO', 'PROVA'] - : ['QUESTION', 'ACTION', 'ARTIFACT', 'PROOF']; - document.querySelector('#builder-detail').innerHTML = `
${item.number}${labels[0]}

${item.title[language]}

${item.question[language]}
${labels[1]}

${item.action[language]}

${labels[2]}${item.output[language]}
${labels[3]}${item.proof[language]}
`; - selectButtons('[data-skill-step]', id, 'skillStep'); -} - -function renderCommonSkill(id) { - const item = interactiveCopy.commonSkills[id]; - const language = currentLanguage; - const labels = language === 'pt' - ? ['QUANDO USAR', 'EXEMPLO', 'CUIDADO'] - : ['WHEN TO USE', 'EXAMPLE', 'WATCH OUT']; - const sourceLabel = language === 'pt' ? 'FONTE NO GITHUB ↗' : 'GITHUB SOURCE ↗'; - document.querySelector('#common-skill-detail').innerHTML = `
${item.number}${item.kind[language]}

${item.title}

${item.rule[language]}
${labels[0]}

${item.use[language]}

${labels[1]}

${item.example[language]}

${labels[2]}

${item.caution[language]}

${sourceLabel}`; - selectButtons('[data-common-skill]', id, 'commonSkill'); -} - -function renderHandsOn() { - document.querySelector('#prompt-basic').textContent = handsOnPrompts[currentLanguage].basic; - document.querySelector('#prompt-skills').textContent = handsOnPrompts[currentLanguage].skills; - document.querySelector('#prompt-install-skills').textContent = skillInstallPrompts[currentLanguage]; - document.querySelectorAll('[data-copy-target] span').forEach((label) => { label.textContent = currentLanguage === 'pt' ? 'COPIAR' : 'COPY'; }); -} - -async function copyPrompt(button) { - const text = document.querySelector(`#${button.dataset.copyTarget}`).textContent; - let copied = false; - try { - await navigator.clipboard.writeText(text); - copied = true; - } catch (error) { - const helper = document.createElement('textarea'); - helper.value = text; - helper.setAttribute('readonly', ''); - helper.style.position = 'fixed'; - helper.style.opacity = '0'; - document.body.appendChild(helper); - helper.select(); - copied = document.execCommand('copy'); - helper.remove(); - } - const status = document.querySelector('#copy-status'); - status.textContent = copied - ? (currentLanguage === 'pt' ? 'Prompt copiado. Cole em uma nova sessão de agente.' : 'Prompt copied. Paste it into a fresh agent session.') - : (currentLanguage === 'pt' ? 'Não foi possível copiar. Selecione o texto manualmente.' : 'Copy unavailable. Select the text manually.'); - if (copied) { - button.classList.add('copied'); - button.querySelector('span').textContent = currentLanguage === 'pt' ? 'COPIADO' : 'COPIED'; - window.setTimeout(() => { button.classList.remove('copied'); button.querySelector('span').textContent = currentLanguage === 'pt' ? 'COPIAR' : 'COPY'; }, 1800); - } -} - -function renderInteractive() { - renderWorker(document.querySelector('[data-worker].active')?.dataset.worker || 'ui'); - renderTree(document.querySelector('[data-tree].active')?.dataset.tree || 'main'); - renderRoute(document.querySelector('[data-route].active')?.dataset.route || 'plan'); - renderModelProvider(document.querySelector('[data-model-provider].active')?.dataset.modelProvider || 'openai'); - renderEffort(document.querySelector('[data-effort].active')?.dataset.effort || 'medium'); - renderSkillFile(document.querySelector('[data-skill-file].active')?.dataset.skillFile || 'skill'); - renderSkillWorkflow(document.querySelector('[data-skill-step].active')?.dataset.skillStep || 'observe'); - renderCommonSkill(document.querySelector('[data-common-skill].active')?.dataset.commonSkill || 'ponytail'); - renderHandsOn(); -} - -function applyLanguage(language) { - currentLanguage = language === 'pt' ? 'pt' : 'en'; - document.documentElement.lang = currentLanguage === 'pt' ? 'pt-BR' : 'en'; - if (currentLanguage === 'pt') Object.entries(translations.pt).forEach(([selector, value]) => setText(selector, value)); - else originals.forEach((values, selector) => document.querySelectorAll(selector).forEach((node, index) => { node.innerHTML = values[index]; })); - document.querySelectorAll('[data-lang]').forEach((button) => { const active = button.dataset.lang === currentLanguage; button.classList.toggle('active', active); button.setAttribute('aria-pressed', String(active)); }); - render(document.querySelector('[data-phase].active')?.dataset.phase || 'plan'); - renderInteractive(); - try { localStorage.setItem('ai-for-dummies-language', currentLanguage); } catch (error) { /* previews may disable storage */ } -} - -buttons.forEach((button) => button.addEventListener('click', () => render(button.dataset.phase))); -document.querySelectorAll('[data-lang]').forEach((button) => button.addEventListener('click', () => applyLanguage(button.dataset.lang))); -document.querySelectorAll('[data-worker]').forEach((button) => button.addEventListener('click', () => renderWorker(button.dataset.worker))); -document.querySelectorAll('[data-tree]').forEach((button) => button.addEventListener('click', () => renderTree(button.dataset.tree))); -document.querySelectorAll('[data-route]').forEach((button) => button.addEventListener('click', () => renderRoute(button.dataset.route))); -document.querySelectorAll('[data-model-provider]').forEach((button) => button.addEventListener('click', () => { renderModelProvider(button.dataset.modelProvider); renderEffort(document.querySelector('[data-effort].active')?.dataset.effort || 'medium'); })); -document.querySelectorAll('[data-effort]').forEach((button) => button.addEventListener('click', () => renderEffort(button.dataset.effort))); -document.querySelectorAll('[data-skill-file]').forEach((button) => button.addEventListener('click', () => renderSkillFile(button.dataset.skillFile))); -document.querySelectorAll('[data-skill-step]').forEach((button) => button.addEventListener('click', () => renderSkillWorkflow(button.dataset.skillStep))); -document.querySelectorAll('[data-common-skill]').forEach((button) => button.addEventListener('click', () => renderCommonSkill(button.dataset.commonSkill))); -document.querySelectorAll('[data-copy-target]').forEach((button) => button.addEventListener('click', () => copyPrompt(button))); -window.addEventListener('scroll', () => { const height = document.documentElement.scrollHeight - window.innerHeight; document.querySelector('.reading-progress span').style.width = `${height > 0 ? (window.scrollY / height) * 100 : 0}%`; }, { passive: true }); - -let savedLanguage = 'en'; -try { savedLanguage = localStorage.getItem('ai-for-dummies-language') || 'en'; } catch (error) { /* previews may disable storage */ } -render('plan'); -applyLanguage(savedLanguage); diff --git a/docs/operations-guide.md b/docs/operations-guide.md index 8fae0cf..0ca7037 100644 --- a/docs/operations-guide.md +++ b/docs/operations-guide.md @@ -427,17 +427,19 @@ paths, and confidence notes. ## Hands-on lab -The presentation includes a dependency-free starter at `hands-on/starter/`. It -renders a small task board but intentionally omits the All / Open / Done filter. +The presentation includes a dependency-free starter at +`public/hands-on/starter/`. It renders a small task board but intentionally +omits the All / Open / Done filter. Attendees clone it from the `pages` branch, +where the build places it at `hands-on/starter/`. Run it from the repository root: ```bash -python3 -m http.server 4173 +pnpm run dev ``` Open -[http://localhost:4173/hands-on/starter/](http://localhost:4173/hands-on/starter/). +[http://localhost:4321/ai-for-dummies/hands-on/starter/](http://localhost:4321/ai-for-dummies/hands-on/starter/). In a fresh coding-agent session, copy **Run A — Good prompt** from the presentation. Record changed files, dependencies, checks, and evidence. Restore the starter, then repeat with **Run B — Good prompt + skills**. @@ -464,20 +466,20 @@ Compare: ### Hands-on rules lab -A second lab at `hands-on/rules/` mirrors the starter's visual system and runs -the same exercise against rule sources. It lists five toggleable rule sources — -`AGENTS.md`, the `gate-discipline` skill body, the Husky `pre-commit` hook, the -`check-ui-contract.mjs` enforcer, and `commitlint` — and rebuilds the **ruled** -prompt live as each toggle flips. +A second lab at `public/hands-on/rules/` mirrors the starter's visual system and +runs the same exercise against rule sources. It lists five toggleable rule +sources — `AGENTS.md`, the `gate-discipline` skill body, the Husky `pre-commit` +hook, the `check-ui-contract.mjs` enforcer, and `commitlint` — and rebuilds the +**ruled** prompt live as each toggle flips. Run it: ```bash -python3 -m http.server 4173 +pnpm run dev ``` Open -[http://localhost:4173/hands-on/rules/](http://localhost:4173/hands-on/rules/). +[http://localhost:4321/ai-for-dummies/hands-on/rules/](http://localhost:4321/ai-for-dummies/hands-on/rules/). Compare the **naive** and **ruled** prompt panels. Toggle rules off to shrink the prompt; toggle them on to add more guards. Copy the final prompt and run it against a real coding agent. diff --git a/eslint.config.js b/eslint.config.js index 2dffe80..5995ec3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,15 +6,12 @@ export default [ ignores: [ '.agents/**', '.astro/**', - 'app.js', 'dist/**', - 'full-guide/**', 'hands-on/**', + 'legacy/**', 'public/hands-on/**', - 'rules/**', 'scripts/**', 'skill-reviews/**', - 'skills-review/**', 'skills/**', 'submitted-skills/**', 'vote-service/**', @@ -26,6 +23,7 @@ export default [ ignores: [ 'dist/**', 'hands-on/**', + 'legacy/**', 'public/hands-on/**', 'submitted-skills/**', 'skill-reviews/**', diff --git a/full-guide/index.html b/full-guide/index.html deleted file mode 100644 index af74658..0000000 --- a/full-guide/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - AI For Dummies — Field Guide - - - - - -
-
A field guide
review submissions ↗
/
AI ENGINEERING 01 / 2026
-

A presentation for humans who ship

AI for
dummies.

You do not need an army of models. You need a system: one mind to frame the work, several hands to execute it, and a clean boundary between every task.

-
01strong model
for ambiguity
03bounded workers
in parallel
iterations
with evidence

Read this as a route map, not a prompt recipe.

-
RULE ZEROStrong model for ambiguity.
Light model for bounded work.
-
ORCHESTRATOR

Decides what
needs to happen.

Opus / reasoning

The orchestrator preserves intent, writes small contracts, and gathers results that can be verified. It does not need to type every line.

-
01Context soup

Every worker reads everything. Nobody knows which facts are load-bearing.

02Branch collision

Two agents touch the same checkout. The fastest path becomes conflict resolution.

03Confident drift

The diff is polished, but no one checks whether it solved the original problem.

-

The subagent loop

Click a phase.
See the handoff.

Delegation means moving one bounded task into a smaller context—not giving away responsibility.

-
PackageContainsWhy it matters
Briefgoal, files, boundariesstops the worker inventing the problem
Worktreebranch and isolated checkoutparallel edits do not collide
Checkstests, build, criteriaturns “looks good” into evidence
Diffsmall, reviewable changeintegration and discard stay cheap
-

Git worktrees

One branch
per hand.

A worktree is another directory linked to the same repository. Each agent gets its own checkout and index; history remains shared.

Select a node to inspect its checkout, owner, and next action.

repository topology 4 checkouts
-

Model routing

Do not pay for
reasoning where
you need rhythm.

Choose a job to see why the model profile changes.

WorkProfilePrompt shape
-

Two separate knobs

Choose the engine.
Then choose the gear.

A stronger model changes the capability ceiling. Higher reasoning effort gives that model more room to work. Start with the lightest combination that passes your real checks, then move one knob at a time.

REASONING / THINKING
ROUTING RULEUse strong models for ambiguity and judgment. Use lighter models for bounded execution. Raise effort only when evaluation shows a gain.
-

Skills

Write the right way
once.

A skill is a reusable procedure. It can carry instructions, references, scripts, and assets. It is not magical memory, and it does not replace acceptance criteria.

01 / trigger clearly02 / load detail on demand03 / return evidence
SKILL PACKAGE
name: review-ui · check focus, mobile, reduced motion · run verification · return evidence
-

The skill forge

Teach the decision.
Keep the context light.

Do not package everything you know. Capture the non-obvious choices that repeatedly improve an outcome, then prove the skill changes behavior.

AFTER REAL USE
observe failuresharpen one ruleretest behaviorkeep it narrow
-

The field kit

Different jobs.
Different instincts.

A skill changes how an agent approaches work. Some shape communication. Others enforce research, debugging, review, or completion discipline. Select one to inspect its operating rule and verified source.

ONE PRACTICAL LOADOUT
PLAN unlazy BUILD ponytail-lite DEBUG diagnosing-bugs REPORT caveman
INSTALL PACKAsk your coding agent to verify, install, and validate the skills.
Review every source before installation. Existing local skills must be preserved.
-

Tiny Tasks lab

Same task.
Better operating system.

Start with a deliberately incomplete static task board. Run one prompt as written, reset, then run the skill-enabled version. Compare diff size, verification evidence, and unnecessary complexity.

THE MISSING FEATUREAdd All / Open / Done filters that survive reload and browser navigation.
STACK HTML · CSS · JavaScript DEPENDENCIES none FILES 3
RUN AGood prompt
Clear context · constraints · acceptance · evidence
RUN BGood prompt + skills
Same contract · explicit working methods · stronger proof
COMPARE THE RUNS
01 Files changed
02 New dependencies
03 Checks actually run
04 Evidence returned

- -

Checks become evidence

Three layers.
Run each one alone.

Run a gate on its own line, print its exit code, attach the output. The result is the deliverable.

01 · STATIC

Lint and types

Format, lint, type-check. Fast and scoped to one file. Run on every save.

pnpm lint; echo "lint=$?" -pnpm typecheck; echo "typecheck=$?"
02 · BEHAVIOR

Unit and contract

Tests that repeat. Run before claiming done.

pnpm test; echo "test=$?" -cd services/api && go test ./...
03 · INTEGRATION

Real UI and API

Drive the actual UI, API, or browser. Slower and flakier — only this catches mobile overflow and a missing 404.

pnpm check:ui; echo "ui=$?" -TURBO_FORCE=true pnpm e2e
FOUR WAYS A GREEN REPORT IS FALSE
1
Pipe a gate

tail, grep, or head hide the real exit code — a pipeline returns the last command's status.

2
Swallow a rejection

A silent .catch(() => {}) hides a panic, an upstream limit, or a partial failure.

3
Trust the cache

Turbo caches results. A gate that "passes" may not have run — use TURBO_FORCE=true.

4
Skip the third layer

Lint and unit can both be green while the page breaks on mobile and the API never returns 404.

RUN IT YOURSELF · two labs, under 10 minutes each
-

Go deeper with official documentation, production case studies, Medium, and practitioner workflows. Rules and enforcement case study → Skills review desk → Primary references → 12-part reading path →

-

Prefer a focused chapter? Start with the route map, then jump directly to models, agents and worktrees, skill creation, rules, or the skills review desk.

- diff --git a/hands-on/rules/README.md b/hands-on/rules/README.md deleted file mode 100644 index d5cc0e2..0000000 --- a/hands-on/rules/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Hands-on · Rules - -A tiny, zero-dependency demo showing how rule sources reshape the same prompt. - -## Run - -Open `index.html` directly. No build, no server, no `npm install`. - -## What it shows - -- Five rule sources, taken from a real monorepo (`netcracker/interview`): - - `AGENTS.md` — repo-wide instruction file. - - `.agents/skills/gate-discipline/SKILL.md` — skill body loaded on demand. - - `.husky/pre-commit` — git hook that runs other enforcers. - - `scripts/check-ui-contract.mjs` — custom CLI enforcer (ratchet). - - `commitlint.config.cjs` — commit-msg linter. -- Each rule has an on/off switch. Toggling a rule prepends its body to the **ruled** prompt. -- EN ↔ PT toggle keeps both languages useful. -- "Copy ruled prompt" copies the current ruled-prompt text to clipboard. -- Responsive on mobile, Full HD, and 4K (one column under 720 px). - -## Mirror of `/hands-on/starter` - -Same visual system as the starter (`--paper`, `--ink`, `--blue`, `--gold`). Drop-in replacement under `hands-on/rules/`. - -## Token budget - -Page weight: ~5 KB total, no framework, no fetch. diff --git a/hands-on/rules/app.js b/hands-on/rules/app.js deleted file mode 100644 index 06f1904..0000000 --- a/hands-on/rules/app.js +++ /dev/null @@ -1,124 +0,0 @@ -// Five rule sources lifted from netcracker/interview. -// Toggling a rule injects its body into the ruled prompt. -const RULES = [ - { - id: 'agents', - name: 'AGENTS.md', - kind: 'Repo-wide instruction', - path: 'AGENTS.md', - en: 'Read AGENTS.md before touching this repo. Stack: pnpm + turbo monorepo, Go API, Next.js apps. Gates run from the monorepo root: pnpm lint, typecheck, test.', - pt: 'Leia AGENTS.md antes de tocar neste repo. Stack: pnpm + turbo monorepo, API em Go, apps Next.js. Gates rodam da raiz: pnpm lint, typecheck, test.' - }, - { - id: 'skill', - name: 'gate-discipline skill', - kind: 'Skill body', - path: '.agents/skills/gate-discipline/SKILL.md', - en: 'Skill `gate-discipline`: every gate runs separately with `$?`. No `| tail`. `TURBO_FORCE=true` if a pass looks too cheap. Generated code is regenerated, never hand-edited.', - pt: 'Skill `gate-discipline`: cada gate roda separado com `$?`. Nada de `| tail`. `TURBO_FORCE=true` se o passar for bom demais. Código gerado se regenera, nunca se edita.' - }, - { - id: 'husky', - name: 'Husky pre-commit', - kind: 'Git hook (commit time)', - path: '.husky/pre-commit', - en: 'Pre-commit runs `pnpm exec lint-staged`, then `node scripts/check-ui-contract.mjs` (UI ratchet), then commitlint. Counts in the baseline may only go DOWN.', - pt: 'Pre-commit roda `pnpm exec lint-staged`, depois `node scripts/check-ui-contract.mjs` (ratchet de UI), depois commitlint. Contadores do baseline só podem DIMINUIR.' - }, - { - id: 'enforcer', - name: 'check-ui-contract.mjs', - kind: 'Custom enforcer (CLI)', - path: 'scripts/check-ui-contract.mjs', - en: 'Enforcer scans for raw - - `).join(''); - ruleCount.textContent = `${state.enabled.size} / ${RULES.length} active`; -} - -function renderPrompts() { - const lang = state.lang; - promptNaive.textContent = NAIVE[lang]; - - const active = RULES.filter((r) => state.enabled.has(r.id)); - const blocks = active.map((r) => `# ${r.name} (${r.path})\n${r[lang]}`).join('\n\n'); - const head = `Task: ${TASK[lang].goal}\nFile: ${TASK[lang].ctx}`; - const tail = lang === 'en' - ? '\n\nRun each gate separately and print $? before claiming done.' - : '\n\nRode cada gate separado e imprima $? antes de dizer que terminou.'; - promptRuled.textContent = blocks ? `${head}\n\n${blocks}${tail}` : head + tail; -} - -function bind() { - ruleList.addEventListener('click', (e) => { - const btn = e.target.closest('.toggle'); - if (!btn) return; - const id = btn.dataset.rule; - if (state.enabled.has(id)) state.enabled.delete(id); else state.enabled.add(id); - btn.setAttribute('aria-pressed', String(state.enabled.has(id))); - ruleCount.textContent = `${state.enabled.size} / ${RULES.length} active`; - renderPrompts(); - }); - - langButtons.forEach((btn) => btn.addEventListener('click', () => { - state.lang = btn.dataset.lang; - langButtons.forEach((b) => b.setAttribute('aria-pressed', String(b === btn))); - renderPrompts(); - })); - - copyBtn.addEventListener('click', async () => { - try { - await navigator.clipboard.writeText(promptRuled.textContent); - copyBtn.dataset.copied = 'true'; - copyBtn.textContent = 'Copied'; - setTimeout(() => { copyBtn.dataset.copied = 'false'; copyBtn.textContent = 'Copy ruled prompt'; }, 1400); - } catch { - copyBtn.textContent = 'Copy failed — select and copy manually'; - } - }); -} - -renderRules(); -renderPrompts(); -bind(); diff --git a/hands-on/rules/index.html b/hands-on/rules/index.html deleted file mode 100644 index 8d3976f..0000000 --- a/hands-on/rules/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Guardrails — Hands-on Rules - - - -
-
-
HANDS-ON / RULES

Guardrails

-

Toggle rules. Same task, different coverage.

-
- -
-

Rule sources

0 / 5 active
-
-
- -
-

Prompt diff

-
- - -
-
-
-
-
NAIVE

Plain prompt

-

-        
-
-
RULED

With guardrails

-

-          
-        
-
-
-
- - - diff --git a/hands-on/rules/styles.css b/hands-on/rules/styles.css deleted file mode 100644 index 898f13e..0000000 --- a/hands-on/rules/styles.css +++ /dev/null @@ -1 +0,0 @@ -:root{--paper:#f4f3ef;--ink:#173044;--muted:#687d8c;--blue:#5683a1;--gold:#efc86d;--line:#d5dde1}*{box-sizing:border-box}body{margin:0;min-width:320px;background:var(--paper);color:var(--ink);font-family:Arial,sans-serif}main{width:min(880px,calc(100% - 40px));margin:0 auto;padding:70px 0}header,.section-head{display:flex;justify-content:space-between;gap:30px;align-items:end}header{padding-bottom:50px;border-bottom:1px solid var(--line)}header span,.prompt-card>header span{font:10px monospace;letter-spacing:.08em}h1{margin:12px 0 0;font-size:clamp(48px,10vw,100px);letter-spacing:-.08em}header p{max-width:220px;color:var(--muted);line-height:1.5}section{padding-top:45px}h2{font-size:24px}.section-head>span{color:var(--blue);font:11px monospace}.rule-list,.prompt-grid{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}.rule{display:grid;grid-template-columns:1fr auto auto;gap:20px;padding:22px;background:var(--paper);align-items:center}.rule h3{margin:0 0 6px;font-size:17px}.rule p{margin:0;color:var(--muted);font-size:13px;line-height:1.45}.rule code{font:11px ui-monospace,monospace;color:var(--ink);background:var(--paper);padding:1px 5px;border:1px solid var(--line)}.rule-tag{font:10px monospace;letter-spacing:.08em;color:var(--muted);text-transform:uppercase}.toggle{appearance:none;width:44px;height:24px;border:1px solid var(--line);background:var(--paper);border-radius:12px;position:relative;cursor:pointer;transition:background .15s ease}.toggle::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;background:var(--ink);border-radius:50%;transition:transform .15s ease,background .15s ease}.toggle[aria-pressed="true"]{background:var(--blue);border-color:var(--blue)}.toggle[aria-pressed="true"]::after{transform:translateX(20px);background:var(--gold)}.prompt-grid{grid-template-columns:1fr 1fr;margin-top:18px}.prompt-card{background:var(--paper);padding:22px;display:flex;flex-direction:column;gap:14px}.prompt-card>header{display:flex;justify-content:space-between;align-items:end;padding-bottom:0;border-bottom:0}.prompt-card h3{margin:0;font-size:17px}.prompt-card pre{margin:0;font:12px ui-monospace,monospace;white-space:pre-wrap;word-break:break-word;color:var(--ink);background:var(--paper);border:1px solid var(--line);padding:14px;min-height:160px;line-height:1.5}#copy-btn{align-self:flex-start;appearance:none;border:1px solid var(--ink);background:var(--ink);color:var(--paper);font:11px monospace;letter-spacing:.08em;padding:9px 14px;cursor:pointer;text-transform:uppercase}#copy-btn[data-copied="true"]{background:var(--blue);border-color:var(--blue)}.lang-switch{display:flex;gap:1px;border:1px solid var(--line)}.lang-switch button{appearance:none;border:0;background:var(--paper);color:var(--muted);font:11px monospace;letter-spacing:.08em;padding:6px 10px;cursor:pointer;text-transform:uppercase}.lang-switch button[aria-pressed="true"]{background:var(--ink);color:var(--paper)}@media(max-width:720px){.prompt-grid{grid-template-columns:1fr}.rule{grid-template-columns:1fr}.rule-tag{display:none}}@media(max-width:560px){header{display:block}header p{margin-top:24px}.lang-switch{flex:1}.lang-switch button{flex:1}} diff --git a/hands-on/starter/README.md b/hands-on/starter/README.md deleted file mode 100644 index b1358d4..0000000 --- a/hands-on/starter/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Tiny Tasks hands-on starter - -A dependency-free HTML/CSS/JavaScript exercise used by the AI For Dummies -presentation. The task list renders; status filtering is intentionally absent. - -Run from the repository root: - -```bash -python3 -m http.server 4173 -``` - -Open and paste either prompt from the -presentation into a fresh coding-agent session rooted at this repository. diff --git a/hands-on/starter/app.js b/hands-on/starter/app.js deleted file mode 100644 index c744210..0000000 --- a/hands-on/starter/app.js +++ /dev/null @@ -1,15 +0,0 @@ -const tasks = [ - { title: 'Review pull request', owner: 'Maya', status: 'open' }, - { title: 'Write release notes', owner: 'Theo', status: 'done' }, - { title: 'Check mobile layout', owner: 'Lina', status: 'open' } -]; - -const list = document.querySelector('#task-list'); -const count = document.querySelector('#task-count'); - -function renderTasks() { - list.innerHTML = tasks.map((task) => `

${task.title}

Owner: ${task.owner}

${task.status}
`).join(''); - count.textContent = `${tasks.length} tasks`; -} - -renderTasks(); diff --git a/hands-on/starter/index.html b/hands-on/starter/index.html deleted file mode 100644 index 6543a3a..0000000 --- a/hands-on/starter/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - Tiny Tasks — Hands-on Starter - - - -
-
-
HANDS-ON / STARTER

Tiny Tasks

-

Three tasks. One missing filter.

-
-
-

Today

-
-
-
- - - diff --git a/hands-on/starter/styles.css b/hands-on/starter/styles.css deleted file mode 100644 index b00a2bf..0000000 --- a/hands-on/starter/styles.css +++ /dev/null @@ -1 +0,0 @@ -:root{--paper:#f4f3ef;--ink:#173044;--muted:#687d8c;--blue:#5683a1;--gold:#efc86d;--line:#d5dde1}*{box-sizing:border-box}body{margin:0;min-width:320px;background:var(--paper);color:var(--ink);font-family:Arial,sans-serif}main{width:min(880px,calc(100% - 40px));margin:0 auto;padding:70px 0}header,.section-head{display:flex;justify-content:space-between;gap:30px;align-items:end}header{padding-bottom:50px;border-bottom:1px solid var(--line)}header span,.task-meta{font:10px monospace;letter-spacing:.08em}h1{margin:12px 0 0;font-size:clamp(48px,10vw,100px);letter-spacing:-.08em}header p{max-width:220px;color:var(--muted);line-height:1.5}section{padding-top:45px}h2{font-size:24px}.section-head>span{color:var(--blue);font:11px monospace}.task-list{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}.task{display:grid;grid-template-columns:1fr auto;gap:20px;padding:22px;background:var(--paper)}.task h3{margin:0 0 8px;font-size:17px}.task p{margin:0;color:var(--muted);font-size:13px}.task-meta{align-self:center;padding:7px 9px;color:var(--ink);background:var(--gold)}.task[data-status="done"] .task-meta{color:var(--paper);background:var(--blue)}@media(max-width:560px){header{display:block}header p{margin-top:24px}.task{grid-template-columns:1fr}.task-meta{justify-self:start}} diff --git a/index.html b/index.html deleted file mode 100644 index 42934ea..0000000 --- a/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - AI For Dummies — Start here - - - - -
-
AI FOR DUMMIES00 / START HEREreview desk ↗
-

The short route

Ship the
system.

Start with the map. Then open the one chapter that matches the decision in front of you: model, agent, worktree, skill, rule, or proof.

Take the full field guide
-
- - - - -
05

Hands-on

Compare a strong prompt with skill-enabled work.

Open lab →
-
06

Review desk

Browse original packages, references, scripts, and improvements.

Open desk →
-
-
THE THREADFrame uncertainty → isolate execution → preserve judgment → verify the change.
-
The route map is now the default entry. The full guide remains available whenever you want the whole narrative.
-
- - diff --git a/landing.css b/landing.css deleted file mode 100644 index befaa59..0000000 --- a/landing.css +++ /dev/null @@ -1 +0,0 @@ -.guide-launch{display:inline-flex;gap:14px;align-items:center;margin-top:20px;padding:12px 16px;background:var(--ink);color:var(--paper);font:700 12px var(--font-mono);letter-spacing:.06em;text-decoration:none;text-transform:uppercase;transition:transform .2s ease,background .2s ease}.guide-launch:hover{transform:translateY(-3px);background:var(--blue)}.guide-launch span{color:var(--gold);font-size:22px;line-height:0}.route-grid .card{display:flex;min-width:0;flex-direction:column}.route-grid .card a{margin-top:auto}.landing-note{display:grid;grid-template-columns:170px minmax(0,1fr);gap:30px;margin:0 0 70px;padding:25px 0;border-top:1px solid var(--ink);border-bottom:1px solid var(--ink)}.landing-note span{color:var(--red);font:700 11px var(--font-mono);letter-spacing:.1em}.landing-note strong{font-size:clamp(22px,3.2vw,42px);line-height:1.08;letter-spacing:-.04em}@media(max-width:520px){.landing-note{grid-template-columns:1fr;gap:9px;margin-bottom:45px}}@media(prefers-reduced-motion:reduce){.guide-launch{transition:none}.guide-launch:hover{transform:none}} diff --git a/skills-review/app.js b/legacy/skills-review/app.js similarity index 100% rename from skills-review/app.js rename to legacy/skills-review/app.js diff --git a/skills-review/catalog.js b/legacy/skills-review/catalog.js similarity index 100% rename from skills-review/catalog.js rename to legacy/skills-review/catalog.js diff --git a/skills-review/files.js b/legacy/skills-review/files.js similarity index 100% rename from skills-review/files.js rename to legacy/skills-review/files.js diff --git a/skills-review/submitted-catalog.js b/legacy/skills-review/submitted-catalog.js similarity index 100% rename from skills-review/submitted-catalog.js rename to legacy/skills-review/submitted-catalog.js diff --git a/skills-review/submitted-files.js b/legacy/skills-review/submitted-files.js similarity index 100% rename from skills-review/submitted-files.js rename to legacy/skills-review/submitted-files.js diff --git a/skills-review/vote.js b/legacy/skills-review/vote.js similarity index 100% rename from skills-review/vote.js rename to legacy/skills-review/vote.js diff --git a/full-guide/audit.css b/legacy/styles/audit.css similarity index 100% rename from full-guide/audit.css rename to legacy/styles/audit.css diff --git a/skills-review/change-lens.css b/legacy/styles/change-lens.css similarity index 100% rename from skills-review/change-lens.css rename to legacy/styles/change-lens.css diff --git a/chapters.css b/legacy/styles/chapters.css similarity index 100% rename from chapters.css rename to legacy/styles/chapters.css diff --git a/styles.css b/legacy/styles/guide.css similarity index 100% rename from styles.css rename to legacy/styles/guide.css diff --git a/skills-review/styles.css b/legacy/styles/skills-review.css similarity index 100% rename from skills-review/styles.css rename to legacy/styles/skills-review.css diff --git a/skills/styles.css b/legacy/styles/skills.css similarity index 100% rename from skills/styles.css rename to legacy/styles/skills.css diff --git a/models/index.html b/models/index.html deleted file mode 100644 index e6ba8fc..0000000 --- a/models/index.html +++ /dev/null @@ -1 +0,0 @@ -AI For Dummies — Models
← ROUTE MAP01 / MODELSfield guide ↗

Model routing

Choose the
engine.

A model has a capability ceiling. Effort controls how much room it gets to reason. Route by uncertainty and verification cost.

LOW

Bounded rhythm

Lookup, small edits, formatting, and transformations with clear checks.

MEDIUM

Default work

Normal implementation where the contract is clear but context matters.

HIGH

Ambiguity

Planning, architecture, security judgment, and hard failures.

Two knobs

Capability
× effort

ROUTING RULEstrong model + high effort → frame ambiguity · light model + low effort → bounded execution · raise one knob at a time → compare evidence

Sequence

Spend judgment
where it compounds.

01
PlanStrong model: scope, risks, acceptance, and worktree split.
02
BuildFocused worker: smallest context and lightest model that can pass.
03
ReviewIndependent pass when missed issues cost more than the call.
diff --git a/package.json b/package.json index efc334b..73ea234 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "verify": "node scripts/verify.mjs && node scripts/audit-ui.mjs && node .agents/scripts/check-tokens.mjs", "gate": "./.agents/scripts/gate.sh", "snapshot": "node .agents/scripts/snapshot-route.mjs", - "serve": "python3 -m http.server 4173", "prepare": "husky" }, "devDependencies": { diff --git a/plans/astro-refactor/task-20-cutover.md b/plans/astro-refactor/task-20-cutover.md index 0a958d8..b23d098 100644 --- a/plans/astro-refactor/task-20-cutover.md +++ b/plans/astro-refactor/task-20-cutover.md @@ -57,3 +57,81 @@ git rev-parse origin/pages # write it down - [ ] Old files deleted; `pnpm run gate` green - [ ] Docs match reality - [ ] Previous `pages` SHA recorded for rollback + +--- + +## Cutover record + +`origin/pages` before cutover: **`37a1e480c6bffb0bff77ad3854ec068688c17d7b`** +("Merge branch 'main' into pages"). Reset `pages` to this SHA to roll back. + +### Step 1 — verification before deletion + +`rendered-text-diff.mjs` compares the _rendered_ DOM of each legacy page against +its Astro replacement. Deleting the legacy files makes that comparison +impossible, so the full sweep was taken first, against the post-cutover build: + +| Route | Legacy spans | Astro spans | Missing | Extra | +| ----------------- | -----------: | ----------: | ------: | ----: | +| `/` | 36 | 36 | 0 | 0 | +| `/full-guide/` | 432 | 432 | 0 | 0 | +| `/summary/` | 34 | 34 | 0 | 0 | +| `/models/` | 36 | 36 | 0 | 0 | +| `/agents/` | 39 | 39 | 0 | 0 | +| `/skills/` | 48 | 48 | 0 | 0 | +| `/rules/` | 119 | 119 | 0 | 0 | +| `/skills-review/` | 178 | 178 | 0 | 0 | +| `/full-guide/` pt | 431 | 431 | 0 | 0 | +| `/rules/` pt | 119 | 119 | 0 | 0 | + +`computed-style-diff.mjs full-guide --widths 880,1050` → 32 differences, +unchanged from the task 15e result, so the file moves below are style-neutral. + +### Step 2 — what could not be deleted + +The brief assumed every legacy file had been superseded. Seven had not: the +Astro build still imports them, and `astro build` fails outright without them. + +| File | Imported by | +| ------------------------------- | --------------------------------- | +| `styles.css` | `src/pages/full-guide.astro` | +| `full-guide/audit.css` | `src/pages/full-guide.astro` | +| `chapters.css` | `src/layouts/ChapterLayout.astro` | +| `skills/styles.css` | `src/pages/skills.astro` | +| `skills-review/styles.css` | `src/pages/skills-review.astro` | +| `skills-review/change-lens.css` | `src/pages/skills-review.astro` | +| `skills-review/app.js` | `src/pages/skills-review.astro` | + +`app.js` pulls in `catalog.js`, `files.js`, and `vote.js`, which pull in +`submitted-catalog.js` and `submitted-files.js` — twelve files in all. + +They were moved to `legacy/`, not into `src/`. `check-tokens.mjs` sweeps `src`, +and these files are full of raw hex and unnamed breakpoints; moving one into +`src/` should mean migrating it to tokens in the same change, never adding a +scan exclusion. `legacy/` keeps the checker's scope honest and stops the files +sitting at route-shaped paths next to the routes they no longer serve. + +Deleted outright (32 files): `app.js`, `responsive.css`, `landing.css`, +`rules/app.js`, `rules/styles.css`, `skills/app.js`, all ten `index.html` files, +and the root `hands-on/` copy — byte-identical to `public/hands-on/`, which is +what the build ships. + +### Step 3 — verify.mjs off the legacy source + +Two assertions read `app.js` to parse `translations.pt`. The 102 strings were +extracted verbatim before deletion into `.agents/snapshots/full-guide-pt.json` — +a legacy capture, not a snapshot of the Astro build, so the check still asserts +against an independent source. + +The brace-matching helper went with the file, taking one assertion. It was +replaced by a check that no snapshot entry is empty: without it, trimming the +snapshot would make the "every string is present" assertion pass vacuously. +Count stays at 84. + +`audit-ui.mjs` now reads the ten pages from `dist/`, resolving Astro's +base-absolute `/ai-for-dummies/...` hrefs against `dist/`. + +### Not done here + +Steps 3, 4, and 6 — publish, verify on the real host, fast-forward `pages` — are +untouched. They need a human present. diff --git a/responsive.css b/responsive.css deleted file mode 100644 index b8561e8..0000000 --- a/responsive.css +++ /dev/null @@ -1,79 +0,0 @@ -.topbar-tools{display:flex;align-items:center;gap:24px} -.skills-review-link{color:var(--blue);font:700 9px var(--font-mono);letter-spacing:.06em;text-decoration:none;text-transform:uppercase;white-space:nowrap}.skills-review-link:hover{color:var(--accent)} -.lang-switch{display:flex;align-items:center;gap:6px;color:var(--muted);font:500 10px var(--font-mono);letter-spacing:.1em} -.lang-switch button{padding:0;border:0;color:inherit;background:transparent;font:inherit;cursor:pointer} -.lang-switch button.active{color:var(--ink);font-weight:700} -.lang-switch button:focus-visible{outline:2px solid var(--accent);outline-offset:4px} -@media(max-width:800px){.topbar-tools{margin-left:auto}} -@media(max-width:600px){.topbar-tools .edition{display:none}} - -/* Interactive operations map */ -.interaction-hint{margin-top:24px!important;padding-left:18px;border-left:3px solid var(--gold);font:500 11px/1.6 var(--font-mono)!important;color:var(--muted)!important} -button{font-family:inherit} -.worker-card{display:grid;align-content:space-between;gap:20px;min-height:180px;padding:22px;border:0;color:var(--paper);background:var(--ink);text-align:left;cursor:pointer;transition:background .2s ease,transform .2s ease} -.worker-card span{color:#9eabb4;font:500 10px var(--font-mono);letter-spacing:.1em} -.worker-card strong{font-size:16px;line-height:1.2}.worker-card code{color:var(--gold);font:11px var(--font-mono)} -.worker-card:hover,.worker-card.active{background:#244760}.worker-card.active{box-shadow:inset 0 -4px 0 var(--gold)}.worker-card:active{transform:translateY(2px)} -.worker-detail{display:grid;grid-template-columns:.55fr 1.1fr 1.2fr;gap:1px;margin-top:1px;background:var(--line)} -.worker-detail>*{margin:0;padding:16px 20px;background:#edf0f1}.worker-detail span,.worker-detail small{font:500 10px/1.5 var(--font-mono);letter-spacing:.06em;text-transform:uppercase}.worker-detail span{color:var(--accent)}.worker-detail strong{font-size:13px}.worker-detail small{color:var(--muted);text-transform:none} - -.tree-lab{min-width:0;border:1px solid #41596b;background:#0b1b27;box-shadow:18px 18px 0 #081621} -.tree-toolbar{display:flex;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #41596b;color:#9eabb4;font:500 9px var(--font-mono);letter-spacing:.1em;text-transform:uppercase} -.tree-live{display:flex;align-items:center;gap:8px}.tree-live i{display:block;width:7px;height:7px;border-radius:50%;background:#80c69a;box-shadow:0 0 0 4px #80c69a22} -.tree-stage{position:relative;height:330px;overflow:hidden;background-image:linear-gradient(#ffffff06 1px,transparent 1px),linear-gradient(90deg,#ffffff06 1px,transparent 1px);background-size:24px 24px} -.tree-stage svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}.tree-edge{fill:none;stroke:#527f9f;stroke-width:2;stroke-dasharray:5 5;vector-effect:non-scaling-stroke}.tree-edge.trunk{stroke:var(--gold);stroke-dasharray:none;stroke-width:3} -.tree-node{position:absolute;z-index:2;display:grid;gap:6px;width:164px;padding:13px 15px;border:1px solid #527085;color:var(--paper);background:#112a3b;text-align:left;cursor:pointer;transition:border-color .2s ease,background .2s ease,transform .2s ease,box-shadow .2s ease} -.tree-node span,.tree-node small{font:500 8px var(--font-mono);letter-spacing:.08em}.tree-node span{color:#8ca1af}.tree-node strong{font:600 12px var(--font-mono)}.tree-node small{color:#a9b6be} -.tree-node.root{top:25px;left:50%;transform:translateX(-50%);border-color:var(--gold)}.tree-node.branch{top:220px}.tree-node.ui{left:3%}.tree-node.tests{left:50%;transform:translateX(-50%)}.tree-node.docs{right:3%} -.tree-node:hover,.tree-node.active{border-color:var(--gold);background:#1c425a;box-shadow:0 0 0 4px #efc76b18}.tree-node.root:hover,.tree-node.root.active{transform:translateX(-50%) translateY(-3px)}.tree-node.tests:hover,.tree-node.tests.active{transform:translateX(-50%) translateY(-3px)}.tree-node.ui:hover,.tree-node.ui.active,.tree-node.docs:hover,.tree-node.docs.active{transform:translateY(-3px)} -.tree-detail{display:grid;grid-template-columns:.7fr 1fr 1.6fr;gap:1px;border-top:1px solid #41596b;background:#41596b}.tree-detail>div,.tree-detail>p,.tree-detail>code{margin:0;padding:18px;background:#102536}.tree-detail div{display:grid;gap:8px}.tree-detail span{color:var(--accent);font:500 8px var(--font-mono);letter-spacing:.1em}.tree-detail strong{color:var(--paper);font:500 11px var(--font-mono)}.tree-detail p{color:#aebbc3;font-size:11px;line-height:1.55}.tree-detail code{grid-column:1/-1;color:var(--gold);font:11px var(--font-mono)} - -.route-console{display:grid;gap:14px}.route-table button{display:grid;grid-template-columns:.8fr .9fr 1.4fr;width:100%;padding:0;border:0;color:inherit;background:transparent;text-align:left;cursor:pointer}.route-table button>*{padding:15px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.route-table button:hover,.route-table button.active{background:#e8ecee}.route-table button.active strong{box-shadow:inset 4px 0 0 var(--gold)} -.route-detail{display:grid;grid-template-columns:100px 1fr;gap:22px;align-items:center;padding:22px;color:var(--paper);background:var(--deep)}.route-meter{display:grid;align-items:end;width:76px;height:76px;padding:7px;border:1px solid #496274}.route-meter span{display:block;width:100%;height:var(--score);background:var(--gold);transition:height .35s ease}.route-detail>div:last-child{display:grid;gap:7px}.route-detail small{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.08em}.route-detail strong{font-size:17px}.route-detail p{margin:0;color:#b5c0c7;font-size:12px;line-height:1.5} - -.skill-explorer{display:grid;grid-template-columns:.9fr 1.1fr;min-height:290px;background:var(--blue)}.skill-package button{display:grid;grid-template-columns:1fr 1fr;gap:15px;padding:17px 20px;border:0;border-bottom:1px solid #ffffff40;color:var(--paper);background:transparent;text-align:left;cursor:pointer}.skill-package button:hover,.skill-package button.active{background:#315f80}.skill-package button.active{box-shadow:inset 4px 0 0 var(--gold)}.skill-package button code{font:12px var(--font-mono)}.skill-package button small{opacity:.7} -.skill-detail{display:grid;grid-template-columns:auto 1fr;gap:18px;align-content:center;padding:28px;color:var(--paper);background:#244760}.skill-detail>span{color:var(--gold);font:42px Georgia,serif}.skill-detail>div{display:grid;gap:13px}.skill-detail strong{font:600 15px var(--font-mono)}.skill-detail p{margin:0;color:#c4cdd3;font-size:12px;line-height:1.65}.skill-detail small{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.06em;text-transform:uppercase} - -.worker-card:focus-visible,.tree-node:focus-visible,.route-table button:focus-visible,.skill-package button:focus-visible{outline:3px solid var(--gold);outline-offset:-3px} - -/* Model and effort gearbox */ -.model-gearbox{margin-bottom:150px;padding-top:80px;border-top:1px solid var(--line)} -.gearbox-intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.gearbox-intro h2{margin-bottom:0}.gearbox-intro>p{max-width:650px;margin:0;color:var(--muted);font-size:14px;line-height:1.75} -.gearbox{display:grid;grid-template-columns:150px minmax(0,1.35fr) minmax(230px,.65fr);grid-template-rows:minmax(410px,auto) auto;border:1px solid var(--line);background:var(--line);gap:1px} -.provider-tabs{display:grid;grid-template-rows:repeat(3,1fr);gap:1px;background:var(--line)}.provider-tabs button{border:0;padding:18px;color:var(--ink);background:var(--paper);font:700 10px var(--font-mono);letter-spacing:.08em;cursor:pointer;writing-mode:vertical-rl;transform:rotate(180deg)}.provider-tabs button:hover{background:#eceff0}.provider-tabs button.active{color:var(--paper);background:var(--blue);box-shadow:inset -5px 0 0 var(--gold)} -.provider-detail{display:grid;align-content:start;padding:42px clamp(28px,4vw,58px);color:var(--paper);background:var(--deep)}.provider-detail header{display:flex;justify-content:space-between;gap:20px;align-items:center}.provider-detail header span{color:var(--gold);font:500 9px var(--font-mono);letter-spacing:.09em}.provider-detail header a{color:#cbd9e1;font:500 9px var(--font-mono)}.provider-detail h3{margin:34px 0 12px;font-size:clamp(34px,4vw,62px);letter-spacing:-.06em}.provider-detail>p{max-width:730px;margin:0;color:#b7c7d1;font-size:13px;line-height:1.7}.model-ladder{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;margin-top:34px;background:#ffffff2b}.model-ladder div{padding:18px;background:#18364a}.model-ladder span{display:block;color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.08em}.model-ladder strong{display:block;margin-top:10px;font-size:13px}.model-ladder small{display:block;margin-top:7px;color:#aebfc9;font-size:10px;line-height:1.4} -.effort-rail{display:grid;grid-template-rows:auto repeat(3,1fr);background:#e9ecee}.effort-rail>span{padding:17px;color:var(--accent);font:700 8px var(--font-mono);letter-spacing:.08em}.effort-rail button{display:grid;align-content:center;gap:8px;padding:22px;border:0;border-top:1px solid var(--line);color:var(--ink);background:var(--paper);text-align:left;cursor:pointer}.effort-rail button b{font:700 18px var(--font-mono)}.effort-rail button small{color:var(--muted);font-size:10px}.effort-rail button:hover{background:#eceff0}.effort-rail button.active{color:var(--paper);background:var(--accent);box-shadow:inset 5px 0 0 var(--gold)}.effort-rail button.active small{color:#eeedf6} -.effort-detail{grid-column:1/-1;display:grid;grid-template-columns:150px 1fr auto;gap:25px;align-items:center;padding:22px 28px;color:var(--paper);background:#132b3b}.effort-detail>span{color:var(--gold);font:500 9px var(--font-mono);letter-spacing:.08em}.effort-detail p{margin:0;font-size:12px;line-height:1.55}.effort-detail code{padding:10px 12px;color:var(--gold);background:#081621;font:10px var(--font-mono)}.gearbox-rule{display:grid;grid-template-columns:150px 1fr;gap:25px;padding:24px 28px;color:var(--ink);background:var(--gold)}.gearbox-rule span{color:var(--accent);font:700 9px var(--font-mono);letter-spacing:.08em}.gearbox-rule strong{font-size:13px;line-height:1.5}.provider-tabs button:focus-visible,.effort-rail button:focus-visible,.provider-detail a:focus-visible{outline:3px solid var(--gold);outline-offset:-3px} - -/* Skill shelf */ -.skill-builder{margin-bottom:150px;padding-top:80px;border-top:1px solid var(--line)} -.builder-intro{display:grid;grid-template-columns:.95fr 1.05fr;gap:70px;align-items:end;margin:45px 0}.builder-intro h2{margin-bottom:0}.builder-intro>p{max-width:620px;margin:0;color:var(--muted);font-size:14px;line-height:1.75} -.builder-workbench{display:grid;grid-template-columns:minmax(220px,.65fr) minmax(380px,1.35fr) minmax(270px,.85fr);min-height:570px;border:1px solid var(--line);background:var(--line)} -.builder-steps{display:grid;grid-template-rows:repeat(5,1fr);gap:1px;background:var(--line)}.builder-steps button{display:grid;grid-template-columns:42px 1fr;grid-template-rows:auto auto;align-content:center;column-gap:13px;padding:18px;border:0;color:var(--ink);background:var(--paper);text-align:left;cursor:pointer}.builder-steps button b{grid-row:1/-1;align-self:center;color:var(--accent);font:500 11px var(--font-mono)}.builder-steps button span{font:700 12px var(--font-mono)}.builder-steps button small{margin-top:5px;color:var(--muted);font-size:10px}.builder-steps button:hover{background:#eceff0}.builder-steps button.active{color:var(--paper);background:var(--blue);box-shadow:inset 5px 0 0 var(--gold)}.builder-steps button.active b,.builder-steps button.active small{color:var(--gold)} -.builder-detail{display:grid;grid-template-rows:auto auto auto 1fr auto;align-content:start;padding:38px clamp(28px,4vw,58px);color:var(--paper);background:var(--deep)}.builder-detail header{display:flex;justify-content:space-between;align-items:center}.builder-detail header span{color:#ffffff30;font:500 54px var(--font-mono)}.builder-detail header small,.builder-action span,.builder-detail footer span{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.1em}.builder-detail h3{margin:27px 0 14px;font-size:clamp(26px,3vw,44px);line-height:1.05;letter-spacing:-.05em}.builder-detail blockquote{margin:0;padding:0;color:#a9bcc8;font:italic 20px/1.35 Georgia,serif}.builder-action{align-self:center;margin:34px 0}.builder-action p{margin:11px 0 0;color:#d5dde2;font-size:13px;line-height:1.7}.builder-detail footer{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:#ffffff2b}.builder-detail footer>div{display:grid;gap:10px;padding:17px;background:#18364a}.builder-detail footer strong{font-size:11px;line-height:1.5} -.builder-artifact{display:grid;grid-template-rows:auto 1fr auto;color:var(--paper);background:#0b1b27}.artifact-head{display:flex;justify-content:space-between;padding:17px;border-bottom:1px solid #344c5d;font:500 8px var(--font-mono);letter-spacing:.09em}.artifact-head i{width:8px;height:8px;border-radius:50%;background:#80c69a;box-shadow:0 0 0 4px #80c69a20}.builder-artifact pre{display:grid;align-items:center;margin:0;padding:28px;overflow:auto;color:#bed0dc;background-image:linear-gradient(#ffffff05 1px,transparent 1px),linear-gradient(90deg,#ffffff05 1px,transparent 1px);background-size:22px 22px}.builder-artifact pre code{font:12px/1.9 var(--font-mono)}.artifact-command{display:grid;gap:9px;padding:18px;border-top:1px solid #344c5d}.artifact-command span{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.09em}.artifact-command code{font:10px var(--font-mono)} -.builder-loop{display:grid;grid-template-columns:200px 1fr;gap:25px;padding:23px 28px;color:var(--ink);background:var(--gold)}.builder-loop>span{font:700 9px var(--font-mono);letter-spacing:.09em}.builder-loop>div{font:600 11px var(--font-mono)}.builder-loop i{margin:0 12px;color:var(--accent);font-style:normal}.builder-steps button:focus-visible{position:relative;z-index:2;outline:3px solid var(--gold);outline-offset:-3px} - -.skill-catalog{margin-bottom:130px;padding-top:80px;border-top:1px solid var(--line)}.verification{padding-top:80px;border-top:1px solid var(--line);margin-bottom:130px}.verify-intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.verify-intro h2{margin-bottom:0}.verify-intro>p{max-width:650px;margin:0;color:var(--muted);font-size:14px;line-height:1.75}.verify-layers{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;margin-top:1px;background:var(--line);border:1px solid var(--line)}.verify-layers article{background:var(--paper);padding:24px 22px;display:grid;gap:12px;grid-template-rows:auto auto 1fr auto}.verify-layers article>span{font:500 9px var(--font-mono);letter-spacing:.09em;color:var(--accent)}.verify-layers article h3{margin:0;font-size:18px;letter-spacing:-.02em}.verify-layers article p{margin:0;color:var(--muted);font-size:13px;line-height:1.55}.verify-layers article code{font:10.5px var(--font-mono);color:var(--ink);background:var(--paper);border:1px solid var(--line);padding:10px 12px;white-space:pre-wrap;word-break:break-word;line-height:1.55}.verify-antipatterns{margin-top:55px}.verify-antipatterns>span{font:500 9px var(--font-mono);letter-spacing:.09em;color:var(--muted)}.ap-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;margin-top:14px;background:var(--line);border:1px solid var(--line)}.ap-grid article{display:grid;grid-template-columns:auto 1fr;gap:8px 18px;align-items:start;background:var(--paper);padding:18px 20px}.ap-grid article b{font:500 22px var(--font-mono);color:var(--accent);line-height:1;align-self:center}.ap-grid article strong{font-size:15px;letter-spacing:-.01em}.ap-grid article p{margin:6px 0 0;color:var(--muted);font-size:13px;line-height:1.5}.ap-grid article code{font:11px var(--font-mono);color:var(--ink);background:var(--paper);padding:1px 4px;border:1px solid var(--line)}.verify-cta{margin-top:55px;padding:26px 28px;color:var(--paper);background:var(--ink)}.verify-cta>span{font:500 9px var(--font-mono);letter-spacing:.09em;color:var(--gold)}.verify-cta-grid{display:grid;grid-template-columns:1fr 1fr;gap:1px;margin-top:18px;background:#ffffff1f}.verify-card{display:grid;gap:6px;background:var(--ink);padding:20px 22px;color:var(--paper);text-decoration:none;border-bottom:2px solid var(--gold)}.verify-card strong{font-size:17px;letter-spacing:-.01em}.verify-card p{margin:0;color:#bfccd4;font-size:13px;line-height:1.55}.verify-card p code{font:11px var(--font-mono);color:var(--gold);background:#0f2230;padding:1px 5px;border:1px solid #2a4150}.verify-card small{font:500 10px var(--font-mono);color:var(--gold);letter-spacing:.09em}@media(max-width:880px){.verify-layers,.verify-cta-grid{grid-template-columns:1fr}.verify-intro{grid-template-columns:1fr}}@media(max-width:560px){.verify-intro{display:block}.verify-intro>p{margin-top:18px}.ap-grid{grid-template-columns:1fr}.verify-cta{padding:22px 20px}} -.catalog-intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.catalog-intro h2{margin-bottom:0}.catalog-intro>p{max-width:620px;margin:0;color:var(--muted);font-size:14px;line-height:1.75} -.skill-deck{display:grid;grid-template-columns:minmax(270px,.8fr) minmax(0,1.7fr);min-height:620px;border:1px solid var(--line);background:var(--line)} -.skill-index{display:grid;grid-template-rows:repeat(7,1fr);gap:1px;background:var(--line)} -.skill-index button{display:grid;grid-template-columns:95px 1fr;grid-template-rows:auto auto;align-content:center;column-gap:18px;padding:16px 20px;border:0;color:var(--ink);background:var(--paper);text-align:left;cursor:pointer;transition:background .2s ease,color .2s ease} -.skill-index button span{grid-row:1/-1;align-self:center;color:var(--accent);font:500 8px var(--font-mono);letter-spacing:.08em}.skill-index button strong{font:600 13px var(--font-mono)}.skill-index button small{margin-top:5px;color:var(--muted);font-size:10px}.skill-index button:hover{background:#eceff0}.skill-index button.active{color:var(--paper);background:var(--deep);box-shadow:inset 5px 0 0 var(--gold)}.skill-index button.active span,.skill-index button.active small{color:var(--gold)} -.common-skill-detail{display:grid;grid-template-rows:auto auto auto 1fr;align-content:start;padding:44px clamp(30px,5vw,78px);color:var(--paper);background:var(--accent);overflow:hidden}.common-skill-detail header{display:flex;justify-content:space-between;align-items:center;padding-bottom:18px;border-bottom:1px solid #ffffff42}.common-skill-detail header span{font:500 48px var(--font-mono);opacity:.34}.common-skill-detail header small{font:500 9px var(--font-mono);letter-spacing:.1em}.common-skill-detail h3{margin:38px 0 18px;font-size:clamp(30px,4vw,60px);letter-spacing:-.06em}.common-skill-detail blockquote{max-width:720px;margin:0 0 38px;padding:0;border:0;color:var(--gold);font:400 clamp(20px,2.5vw,34px)/1.15 Georgia,serif;font-style:italic}.common-skill-notes{display:grid;grid-template-columns:1.45fr .9fr .9fr;gap:1px;align-self:end;background:#ffffff42}.common-skill-notes>div{padding:20px;background:#6c6898}.common-skill-notes span{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.08em}.common-skill-notes p{margin:12px 0 0;color:#f1f0f7;font-size:12px;line-height:1.55}.common-skill-notes div:nth-child(2) p{font-family:var(--font-mono);font-size:10px} -.skill-loadout{display:grid;grid-template-columns:210px 1fr;gap:25px;padding:24px 28px;color:var(--paper);background:var(--ink)}.skill-loadout>span{color:var(--gold);font:500 9px var(--font-mono);letter-spacing:.09em}.skill-loadout>div{font:500 11px var(--font-mono)}.skill-loadout b{color:var(--accent);font-size:9px}.skill-loadout i{margin:0 10px;color:var(--gold);font-style:normal}.skill-index button:focus-visible{position:relative;z-index:2;outline:3px solid var(--gold);outline-offset:-3px} -.skill-source{display:inline-block;margin-top:22px;color:var(--gold);font:700 9px var(--font-mono);letter-spacing:.07em;text-decoration:none;border-bottom:1px solid currentColor}.skill-source:focus-visible{outline:3px solid var(--gold);outline-offset:4px} -.install-skills{display:grid;grid-template-rows:auto 1fr auto;margin-top:24px;color:var(--paper);background:var(--deep);border-left:7px solid var(--gold)}.install-skills header{display:flex;justify-content:space-between;align-items:center;gap:24px;padding:20px 24px;border-bottom:1px solid #ffffff2d}.install-skills header>div{display:grid;gap:7px}.install-skills header span,.install-skills footer{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.08em}.install-skills header strong{font-size:15px}.install-skills button{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid #ffffff50;color:var(--paper);background:transparent;cursor:pointer}.install-skills button:hover,.install-skills button.copied{color:var(--ink);border-color:var(--gold);background:var(--gold)}.install-skills button i{font-style:normal}.install-skills pre{max-height:360px;margin:0;padding:24px;overflow:auto;white-space:pre-wrap;background:#0b1b27}.install-skills pre code{font:10px/1.7 var(--font-mono)}.install-skills footer{padding:16px 24px;color:#b9c8d1;border-top:1px solid #ffffff2d}.install-skills button:focus-visible{outline:3px solid var(--gold);outline-offset:3px} - -/* Copy-ready hands-on lab */ -.hands-on{margin-bottom:130px;padding-top:80px;border-top:1px solid var(--line)}.hands-intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.hands-intro h2{margin-bottom:0}.hands-intro>div:last-child>p{max-width:650px;margin:0;color:var(--muted);font-size:14px;line-height:1.75}.starter-link{display:inline-block;margin-top:18px;color:var(--blue);font:700 11px var(--font-mono);text-decoration:none;border-bottom:2px solid var(--gold)}.starter-links{display:flex;flex-wrap:wrap;gap:14px 28px;margin-top:18px}.starter-links .starter-link{margin-top:0}.starter-link-group{display:flex;flex-wrap:wrap;gap:6px 18px;align-items:baseline}.starter-link-source{font-weight:500!important;color:var(--muted)!important;border-bottom-color:transparent!important}.verify-card-source{display:block;margin-top:8px;color:#bfccd4;font-size:10px!important;letter-spacing:.05em;text-transform:none;font-weight:500}.verify-card-source span{font-family:var(--font-mono);color:var(--gold)} -.exercise-brief{display:grid;grid-template-columns:190px 1fr;gap:22px;padding:26px 30px;color:var(--paper);background:var(--deep)}.exercise-brief>span{color:var(--gold);font:500 9px var(--font-mono);letter-spacing:.09em}.exercise-brief>strong{font-size:clamp(20px,2.6vw,34px);line-height:1.12}.exercise-brief>div{grid-column:2;color:#afbec7;font:500 9px var(--font-mono);letter-spacing:.05em}.exercise-brief b{margin-left:18px;color:var(--accent)}.exercise-brief b:first-child{margin-left:0} -.prompt-compare{display:grid;grid-template-columns:1fr 1fr;gap:1px;margin-top:1px;background:var(--line)}.prompt-card{display:grid;grid-template-rows:auto 1fr auto;min-width:0;min-height:600px;color:var(--paper);background:#19364a}.prompt-card.enhanced{background:#596f9a}.prompt-card header{display:flex;justify-content:space-between;align-items:center;padding:20px 22px;border-bottom:1px solid #ffffff32}.prompt-card header>div{display:grid;gap:6px}.prompt-card header span,.prompt-card footer{font:500 8px var(--font-mono);letter-spacing:.09em}.prompt-card header>div span{color:var(--gold)}.prompt-card header strong{font-size:17px}.prompt-card button{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid #ffffff50;color:var(--paper);background:transparent;cursor:pointer}.prompt-card button:hover,.prompt-card button.copied{color:var(--ink);border-color:var(--gold);background:var(--gold)}.prompt-card button i{font-style:normal}.prompt-card pre{margin:0;padding:25px;overflow:auto;white-space:pre-wrap}.prompt-card pre code{font:11px/1.72 var(--font-mono)}.prompt-card footer{padding:17px 22px;color:#bfccd4;border-top:1px solid #ffffff32}.prompt-card.enhanced footer{color:#e5e3ef}.prompt-card button:focus-visible,.starter-link:focus-visible{outline:3px solid var(--gold);outline-offset:3px} -.comparison-strip{display:grid;grid-template-columns:190px repeat(4,1fr);gap:1px;background:var(--line)}.comparison-strip>*{padding:18px;background:var(--paper)}.comparison-strip>span{color:var(--accent);font:700 9px var(--font-mono);letter-spacing:.08em}.comparison-strip>div{color:var(--muted);font:500 10px var(--font-mono)}.comparison-strip b{margin-right:8px;color:var(--blue)}.copy-status{min-height:20px;margin:15px 0 0;color:var(--blue);font:600 10px var(--font-mono);text-align:right} -@media(min-width:1600px){.tree-stage{height:390px}.tree-stage svg{height:330px;top:30px}.tree-node.root{top:45px}.tree-node.branch{top:255px}.tree-node{width:190px;padding:17px}.tree-detail{grid-template-columns:.6fr .8fr 1.8fr}.worker-card{min-height:210px;padding:28px}} -@media(min-width:2200px){main{max-width:2880px;padding-inline:clamp(140px,7vw,280px)}.hero{max-width:1420px}.hero h1{font-size:clamp(150px,7vw,220px)}.tree-stage{height:460px}.tree-stage svg{height:380px;top:45px}.tree-node.root{top:65px}.tree-node.branch{top:305px}.tree-node{width:240px;padding:22px}.tree-node strong{font-size:15px}.tree-detail>*{font-size:14px!important}.fleet,.failure-map{margin-bottom:170px}.workflow,.routing,.skills{margin-bottom:180px}} -@media(max-width:1100px){.chapter-links{display:none}.builder-workbench{grid-template-columns:minmax(210px,.65fr) minmax(0,1.35fr)}.builder-artifact{grid-column:1/-1;grid-template-columns:1fr 1fr;grid-template-rows:auto}.builder-artifact .artifact-head{grid-column:1/-1}.artifact-command{align-content:center;border-top:0;border-left:1px solid #344c5d}} -@media(max-width:1050px){.tree-node{width:145px}.tree-detail{grid-template-columns:1fr 1fr}.tree-detail p{grid-column:1/-1}.skill-explorer{grid-template-columns:1fr}} -@media(max-width:800px){.worker-detail{grid-template-columns:1fr}.worktrees{display:block}.worktree-intro{margin-bottom:40px}.tree-lab{box-shadow:9px 9px 0 #081621}.route-table button{min-width:620px}.gearbox-intro,.builder-intro,.catalog-intro,.hands-intro{grid-template-columns:1fr;gap:20px}.gearbox{grid-template-columns:1fr 210px;grid-template-rows:auto auto}.provider-tabs{grid-column:1/-1;grid-template-columns:repeat(3,1fr);grid-template-rows:none}.provider-tabs button{writing-mode:horizontal-tb;transform:none}.provider-tabs button.active{box-shadow:inset 0 -5px 0 var(--gold)}.effort-detail{grid-template-columns:110px 1fr}.effort-detail code{grid-column:2}.skill-explorer{grid-template-columns:1fr 1fr}.builder-workbench{grid-template-columns:1fr}.builder-steps{grid-template-columns:1fr 1fr;grid-template-rows:none}.builder-artifact{grid-column:auto}.skill-deck{grid-template-columns:1fr}.skill-index{grid-template-columns:1fr 1fr;grid-template-rows:none}.common-skill-detail{min-height:600px}.common-skill-notes{grid-template-columns:1fr 1fr}.common-skill-notes>div:first-child{grid-column:1/-1}.prompt-compare{grid-template-columns:1fr}.comparison-strip{grid-template-columns:1fr 1fr}.comparison-strip>span{grid-column:1/-1}.exercise-brief{grid-template-columns:1fr}.exercise-brief>div{grid-column:auto}} -@media(max-width:600px){.tree-stage{height:auto;min-height:560px;padding:24px}.tree-stage svg{display:none}.tree-node,.tree-node.root,.tree-node.branch,.tree-node.ui,.tree-node.tests,.tree-node.docs{position:relative;top:auto;right:auto;left:auto;width:100%;margin:0 0 34px;transform:none}.tree-node:not(:last-child)::after{content:'↓';position:absolute;left:50%;bottom:-28px;color:var(--gold)}.tree-node:hover,.tree-node.active,.tree-node.root:hover,.tree-node.root.active,.tree-node.tests:hover,.tree-node.tests.active{transform:translateY(-2px)}.tree-detail{grid-template-columns:1fr}.tree-detail p,.tree-detail code{grid-column:auto}.gearbox{grid-template-columns:1fr}.provider-detail,.effort-rail{grid-column:1}.model-ladder{grid-template-columns:1fr}.effort-rail{grid-template-columns:repeat(3,1fr);grid-template-rows:auto auto}.effort-rail>span{grid-column:1/-1}.effort-rail button{text-align:center;border-top:1px solid var(--line);border-left:1px solid var(--line)}.effort-rail button.active{box-shadow:inset 0 -5px 0 var(--gold)}.effort-detail{grid-template-columns:1fr}.effort-detail code{grid-column:auto;overflow:auto}.gearbox-rule{grid-template-columns:1fr}.skill-explorer{grid-template-columns:1fr}.route-detail{grid-template-columns:70px 1fr}.route-meter{width:56px;height:70px}.builder-steps{grid-template-columns:1fr}.builder-steps button{min-height:78px}.builder-detail{padding:28px 24px}.builder-detail footer{grid-template-columns:1fr}.builder-artifact{grid-template-columns:1fr}.builder-artifact .artifact-head{grid-column:auto}.artifact-command{border-left:0;border-top:1px solid #344c5d}.builder-loop{grid-template-columns:1fr}.builder-loop>div{line-height:2}.builder-loop i{margin-inline:4px}.skill-index{grid-template-columns:1fr}.skill-index button{min-height:78px}.common-skill-detail{min-height:0;padding:30px 24px}.common-skill-notes{grid-template-columns:1fr}.common-skill-notes>div:first-child{grid-column:auto}.skill-loadout{grid-template-columns:1fr}.skill-loadout>div{line-height:2}.skill-loadout i{margin-inline:4px}.install-skills header{align-items:flex-start}.install-skills header strong{font-size:12px}.install-skills pre{max-height:500px}.exercise-brief{padding:22px}.exercise-brief>div{line-height:2}.exercise-brief b{margin-left:7px}.prompt-card{min-height:0}.prompt-card pre{max-height:560px}.comparison-strip{grid-template-columns:1fr}.comparison-strip>span{grid-column:auto}.copy-status{text-align:left}} -@media(prefers-reduced-motion:reduce){.worker-card,.tree-node,.route-meter span{transition:none}.reading-progress span{transition:none}} diff --git a/rules/app.js b/rules/app.js deleted file mode 100644 index 3a01f39..0000000 --- a/rules/app.js +++ /dev/null @@ -1,92 +0,0 @@ -const languageCopy = { - en: { - back: 'field guide', navPipeline: 'Pipeline', navExamples: 'Examples', stageContext: 'CONTEXT', stageReview: 'REVIEW', heroEyebrow: 'A real repository case study', heroTitle: 'Rules that
survive the prompt.', - heroText: 'Prompts ask for behavior. Repositories preserve it. The interview project combines written context, reusable skills, executable checks, commit hooks, and independent review so the rule is still present when the conversation is gone.', - heroAside: '8 skills · 3 agents · 4 enforcement layers', thesisLabel: 'THE SHORT VERSION', thesis: 'A prompt is advice for one run. A repository rule is reusable context plus an executable boundary.', - pipelineLabel: 'Enforcement pipeline', pipelineMeta: 'select a checkpoint', pipelineEyebrow: 'From intent to evidence', pipelineTitle: 'Five places
a rule can hold.', pipelineText: 'Not every rule belongs in a hook. Put guidance where an agent can discover it, deterministic policy in a command, cheap checks at commit time, and independent judgment at review.', - skillsLabel: 'Project-local skills', skillsMeta: 'procedures born from repeated friction', skillsEyebrow: 'Small instruction packages', skillsTitle: 'Teach the trap.
Name the trigger.', skillsText: 'These skills are not downloaded magic. They are repository-specific procedures under .agents/skills/, distilled from mistakes, commands, and architectural decisions that kept recurring.', skillGate: 'prove green is real', skillParallel: 'worktree per task', skillRepo: 'query before crawling', skillDebt: 'separate line of work', skillWriter: 'repeat twice, encode once', skillArea: 'stack-specific traps', - examplesLabel: 'Concrete examples', examplesMeta: 'open the source, then adapt', ratchetLabel: 'CLI RATCHET', ratchetTitle: 'Debt may go down.
Never silently up.', hookTitle: 'Fast checks before history.', commitLabel: 'COMMIT MESSAGE', commitTitle: 'Intent has a grammar.', reviewLabel: 'INDEPENDENT REVIEW', reviewTitle: 'A second reader checks intent.', readChecker: 'Read the checker →', readHook: 'Read the hook →', readCommit: 'Read commitlint config →', readReview: 'Read review policy →', - copyLabel: 'COPY / ADAPT', copyTitle: 'Ask your agent to map the enforcement stack.', copyText: 'Use this in the interview repository or adapt the path names to another project.', copyButton: 'COPY PROMPT', deeperLabel: 'GO DEEPER', deeperTitle: 'Read the implementation, not just this summary.', deepContext: 'Repository context', deepSkills: 'Skill catalog', deepAgents: 'Specialist agents', deepPolicy: 'Staged-file policy' - }, - pt: { - back: 'guia de campo', navPipeline: 'Pipeline', navExamples: 'Exemplos', stageContext: 'CONTEXTO', stageReview: 'REVISÃO', heroEyebrow: 'Um estudo de caso de repositório real', heroTitle: 'Regras que
sobrevivem ao prompt.', - heroText: 'Prompts pedem comportamento. Repositórios o preservam. O projeto interview combina contexto escrito, skills reutilizáveis, verificações executáveis, hooks de commit e revisão independente para que a regra continue existindo quando a conversa terminar.', - heroAside: '8 skills · 3 agentes · 4 camadas de enforcement', thesisLabel: 'A VERSÃO CURTA', thesis: 'Um prompt orienta uma execução. Uma regra de repositório é contexto reutilizável mais uma fronteira executável.', - pipelineLabel: 'Pipeline de enforcement', pipelineMeta: 'selecione um checkpoint', pipelineEyebrow: 'Da intenção à evidência', pipelineTitle: 'Cinco lugares
onde a regra segura.', pipelineText: 'Nem toda regra pertence a um hook. Coloque orientação onde o agente descobre, política determinística em um comando, checks baratos no commit e julgamento independente na revisão.', - skillsLabel: 'Skills locais do projeto', skillsMeta: 'procedimentos nascidos de atrito repetido', skillsEyebrow: 'Pequenos pacotes de instrução', skillsTitle: 'Ensine a armadilha.
Nomeie o gatilho.', skillsText: 'Estas skills não são mágica baixada. São procedimentos específicos do repositório em .agents/skills/, extraídos de erros, comandos e decisões arquiteturais recorrentes.', skillGate: 'prove que o verde é real', skillParallel: 'um worktree por tarefa', skillRepo: 'consulte antes de explorar', skillDebt: 'linha de trabalho separada', skillWriter: 'repita duas vezes, codifique uma', skillArea: 'armadilhas da stack', - examplesLabel: 'Exemplos concretos', examplesMeta: 'abra a fonte, depois adapte', ratchetLabel: 'CATRACA CLI', ratchetTitle: 'A dívida pode cair.
Nunca subir em silêncio.', hookTitle: 'Checks rápidos antes do histórico.', commitLabel: 'MENSAGEM DE COMMIT', commitTitle: 'A intenção tem gramática.', reviewLabel: 'REVISÃO INDEPENDENTE', reviewTitle: 'Um segundo leitor verifica a intenção.', readChecker: 'Ler o checker →', readHook: 'Ler o hook →', readCommit: 'Ler config do commitlint →', readReview: 'Ler política de revisão →', - copyLabel: 'COPIAR / ADAPTAR', copyTitle: 'Peça ao agente para mapear o enforcement.', copyText: 'Use isto no repositório interview ou adapte os caminhos para outro projeto.', copyButton: 'COPIAR PROMPT', deeperLabel: 'APROFUNDE', deeperTitle: 'Leia a implementação, não apenas este resumo.', deepContext: 'Contexto do repositório', deepSkills: 'Catálogo de skills', deepAgents: 'Agentes especialistas', deepPolicy: 'Política dos arquivos staged' - } -}; - -const stages = { - context: { number: '01', file: 'AGENTS.md', title: { en: 'Give every agent the same map', pt: 'Dê o mesmo mapa a cada agente' }, text: { en: 'Stack, commands, product boundaries, and known traps load before implementation. This is discoverable guidance—not an executable guarantee.', pt: 'Stack, comandos, limites do produto e armadilhas conhecidas carregam antes da implementação. É orientação descobrível — não garantia executável.' }, code: 'Read AGENTS.md\n→ query .agents/db\n→ load area skill', link: 'https://git.marcospaulo.dev.br/netcracker/interview/src/branch/main/AGENTS.md' }, - skills: { number: '02', file: '.agents/skills/', title: { en: 'Load only the relevant procedure', pt: 'Carregue apenas o procedimento relevante' }, text: { en: 'Frontend, Go API, gate discipline, parallel agents, repo databases, issues, skill writing, and tech debt each have a narrow trigger.', pt: 'Frontend, API Go, disciplina de gates, agentes paralelos, bancos do repo, issues, escrita de skills e dívida técnica têm gatilhos estreitos.' }, code: 'request + description\n→ matching SKILL.md\n→ focused workflow', link: 'https://git.marcospaulo.dev.br/netcracker/interview/src/branch/main/.agents/skills' }, - cli: { number: '03', file: 'scripts/check-ui-contract.mjs', title: { en: 'Turn measurable policy into a ratchet', pt: 'Transforme política mensurável em catraca' }, text: { en: 'The UI checker counts known violations and fails only when a count rises. Existing debt can be reduced, but a new change cannot quietly increase it.', pt: 'O checker de UI conta violações conhecidas e falha quando o total sobe. Dívida existente pode cair, mas uma mudança não pode aumentá-la em silêncio.' }, code: 'pnpm check:ui\ncurrent ≤ baseline → pass\ncurrent > baseline → fail', link: 'https://git.marcospaulo.dev.br/netcracker/interview/src/branch/main/scripts/check-ui-contract.mjs' }, - commit: { number: '04', file: '.husky/pre-commit', title: { en: 'Block cheap mistakes at the boundary', pt: 'Bloqueie erros baratos na fronteira' }, text: { en: 'Husky runs lint-staged plus the whole-tree UI ratchet. Commitlint separately enforces Conventional Commit messages.', pt: 'Husky executa lint-staged mais a catraca de UI da árvore inteira. Commitlint aplica Conventional Commits separadamente.' }, code: 'git commit\n├─ lint-staged\n├─ check:ui\n└─ commitlint', link: 'https://git.marcospaulo.dev.br/netcracker/interview/src/branch/main/.husky/pre-commit' }, - review: { number: '05', file: '.pr-review.json', title: { en: 'Reserve judgment for the PR', pt: 'Reserve julgamento para o PR' }, text: { en: 'An AI reviewer checks security, generated-code discipline, test quality, scope, and repository-specific traps. A verifier independently reruns gates before merge.', pt: 'Um revisor de IA verifica segurança, código gerado, qualidade dos testes, escopo e armadilhas do repositório. Um verifier roda os gates novamente antes do merge.' }, code: 'diff + house rules + prior review\n→ findings + risks\n→ human merge decision', link: 'https://git.marcospaulo.dev.br/netcracker/interview/src/branch/main/.pr-review.json' } -}; - -const skills = { - gate: { name: 'gate-discipline', trigger: { en: 'Before commit, PR, or merge.', pt: 'Antes de commit, PR ou merge.' }, lesson: { en: 'Run canonical gates separately, inspect exit codes, distrust convenient cache hits, and prove generated code is current.', pt: 'Rode gates separadamente, leia exit codes, desconfie de cache conveniente e prove que código gerado está atual.' }, example: 'pnpm lint; echo "lint=$?"', path: '.agents/skills/gate-discipline/SKILL.md' }, - parallel: { name: 'parallel-agents', trigger: { en: 'When independent tasks can run concurrently.', pt: 'Quando tarefas independentes podem rodar juntas.' }, lesson: { en: 'One worktree and file ownership per task; prior-art scout first, verifier last, merge sequentially.', pt: 'Um worktree e ownership por tarefa; prior-art scout primeiro, verifier por último, merges sequenciais.' }, example: 'git worktree add .claude/worktrees/task -b feat/task', path: '.agents/skills/parallel-agents/SKILL.md' }, - repo: { name: 'repo-db', trigger: { en: 'At task start and before debugging.', pt: 'No início da tarefa e antes de diagnosticar.' }, lesson: { en: 'Query the repo map, canonical commands, and known-issues ledger before crawling thousands of files.', pt: 'Consulte mapa, comandos canônicos e known-issues antes de vasculhar milhares de arquivos.' }, example: "jq -r '.verify[]' .agents/db/commands.json", path: '.agents/skills/repo-db/SKILL.md' }, - debt: { name: 'tech-debt', trigger: { en: 'When unrelated debt appears mid-task.', pt: 'Quando dívida não relacionada aparece no meio.' }, lesson: { en: 'Do not smuggle cleanup into a feature. Record it, give it its own branch, and finish the underlying condition.', pt: 'Não esconda limpeza em uma feature. Registre, dê uma branch própria e finalize a condição original.' }, example: 'feature diff ≠ debt cleanup', path: '.agents/skills/tech-debt/SKILL.md' }, - writer: { name: 'skill-writer', trigger: { en: 'When a workflow is explained twice.', pt: 'Quando um fluxo é explicado duas vezes.' }, lesson: { en: 'Procedures become skills; isolated facts become known-issues entries. Keep the body short and the trigger discriminating.', pt: 'Procedimentos viram skills; fatos isolados viram known-issues. Corpo curto e gatilho discriminante.' }, example: '.agents/skills//SKILL.md', path: '.agents/skills/skill-writer/SKILL.md' }, - area: { name: 'frontend / go-api', trigger: { en: 'When changing the matching subsystem.', pt: 'Ao alterar o subsistema correspondente.' }, lesson: { en: 'Encode version-specific traps close to the work: Next/React/AntD boundaries, Connect APIs, sqlc types, migrations, and code generation.', pt: 'Codifique armadilhas de versão perto do trabalho: Next/React/AntD, APIs Connect, tipos sqlc, migrations e codegen.' }, example: 'area → local skill → canonical checks', path: '.agents/skills/' } -}; - -const prompts = { - en: `Inspect this repository's enforcement stack before changing code.\n\n1. Read AGENTS.md.\n2. List .agents/skills and select only skills whose descriptions match the task.\n3. Query .agents/db/commands.json and .agents/db/known-issues.json.\n4. Explain what is guidance versus mechanically enforced by scripts, Husky, commitlint, CI, and PR review.\n5. For each relevant rule, cite the source path and the command that proves it.\n6. Identify gaps where documentation claims enforcement but no executable check exists.\n\nDo not modify files. Return a compact map: rule → source → enforcement point → verification command → remaining gap.`, - pt: `Inspecione a stack de enforcement deste repositório antes de alterar código.\n\n1. Leia AGENTS.md.\n2. Liste .agents/skills e selecione apenas skills cuja descrição corresponda à tarefa.\n3. Consulte .agents/db/commands.json e .agents/db/known-issues.json.\n4. Explique o que é orientação e o que é imposto mecanicamente por scripts, Husky, commitlint, CI e revisão de PR.\n5. Para cada regra relevante, cite o caminho fonte e o comando que a comprova.\n6. Identifique lacunas onde a documentação promete enforcement sem check executável.\n\nNão modifique arquivos. Retorne um mapa compacto: regra → fonte → ponto de enforcement → comando de verificação → lacuna restante.` -}; - -let language = localStorage.getItem('rules-language') === 'pt' ? 'pt' : 'en'; - -function select(selector, value, key) { - document.querySelectorAll(selector).forEach((button) => { - const active = button.dataset[key] === value; - button.classList.toggle('active', active); - button.setAttribute('aria-selected', String(active)); - }); -} - -function renderStage(id) { - const item = stages[id]; - document.querySelector('#stage-detail').innerHTML = `
${item.number}
${item.file}

${item.title[language]}

${item.text[language]}

${language === 'pt' ? 'ABRIR FONTE ↗' : 'OPEN SOURCE ↗'}
${item.code}
`; - select('[data-stage]', id, 'stage'); -} - -function renderSkill(id) { - const item = skills[id]; - document.querySelector('#skill-detail').innerHTML = `
${language === 'pt' ? 'GATILHO' : 'TRIGGER'}${item.trigger[language]}

${item.name}

${item.lesson[language]}

${item.example}
${language === 'pt' ? 'LER SKILL ↗' : 'READ SKILL ↗'}`; - select('[data-skill]', id, 'skill'); -} - -function renderLanguage(next) { - language = next; - document.documentElement.lang = language === 'pt' ? 'pt-BR' : 'en'; - document.querySelectorAll('[data-copy]').forEach((node) => { node.innerHTML = languageCopy[language][node.dataset.copy]; }); - document.querySelector('#explore-prompt').textContent = prompts[language]; - document.querySelectorAll('[data-lang]').forEach((button) => { const active = button.dataset.lang === language; button.classList.toggle('active', active); button.setAttribute('aria-pressed', String(active)); }); - renderStage(document.querySelector('[data-stage].active')?.dataset.stage || 'context'); - renderSkill(document.querySelector('[data-skill].active')?.dataset.skill || 'gate'); - localStorage.setItem('rules-language', language); -} - -async function copyPrompt() { - const value = prompts[language]; - try { - await navigator.clipboard.writeText(value); - document.querySelector('#copy-status').textContent = language === 'pt' ? 'Prompt copiado.' : 'Prompt copied.'; - } catch { - document.querySelector('#copy-status').textContent = language === 'pt' ? 'Selecione o texto manualmente.' : 'Select the text manually.'; - } -} - -document.querySelectorAll('[data-stage]').forEach((button) => button.addEventListener('click', () => renderStage(button.dataset.stage))); -document.querySelectorAll('[data-skill]').forEach((button) => button.addEventListener('click', () => renderSkill(button.dataset.skill))); -document.querySelectorAll('[data-lang]').forEach((button) => button.addEventListener('click', () => renderLanguage(button.dataset.lang))); -document.querySelector('[data-copy-prompt]').addEventListener('click', copyPrompt); -window.addEventListener('scroll', () => { const height = document.documentElement.scrollHeight - innerHeight; document.querySelector('.progress span').style.width = `${height > 0 ? scrollY / height * 100 : 0}%`; }, { passive: true }); -renderLanguage(language); diff --git a/rules/index.html b/rules/index.html deleted file mode 100644 index 4249452..0000000 --- a/rules/index.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - Rules That Survive the Prompt — AI For Dummies - - - - -
- Afield guide - -
/
-
- -
-
-

A real repository case study

-

Rules that
survive the prompt.

-
-

Prompts ask for behavior. Repositories preserve it. The interview project combines written context, reusable skills, executable checks, commit hooks, and independent review so the rule is still present when the conversation is gone.

- -
-
- -
- THE SHORT VERSION - A prompt is advice for one run. A repository rule is reusable context plus an executable boundary. -
- -
- -

From intent to evidence

Five places
a rule can hold.

Not every rule belongs in a hook. Put guidance where an agent can discover it, deterministic policy in a command, cheap checks at commit time, and independent judgment at review.

-
- - - - - -
-
-
- -
- -

Small instruction packages

Teach the trap.
Name the trigger.

These skills are not downloaded magic. They are repository-specific procedures under .agents/skills/, distilled from mistakes, commands, and architectural decisions that kept recurring.

-
-
- - - - - - -
-
-
-
- -
- -
-
CLI RATCHET

Debt may go down.
Never silently up.

pnpm check:ui
-# raw buttons
-# swallowed catches
-# pages without h1
-# hardcoded colours
Read the checker →
-
HUSKY / PRE-COMMIT

Fast checks before history.

pnpm exec lint-staged
-node scripts/check-ui-contract.mjs
Read the hook →
-
COMMIT MESSAGE

Intent has a grammar.

pnpm exec commitlint --edit $1
-
-feat: add interview timer
-fix(api): scope session query
Read commitlint config →
-
INDEPENDENT REVIEW

A second reader checks intent.

.pr-review.json
-├── focus
-├── exclude_paths
-├── languages
-└── instructions
Read review policy →
-
-
- -
-
COPY / ADAPT

Ask your agent to map the enforcement stack.

Use this in the interview repository or adapt the path names to another project.

-

-
- -
-
GO DEEPERRead the implementation, not just this summary.
- -
-
- - - diff --git a/rules/styles.css b/rules/styles.css deleted file mode 100644 index e01dad1..0000000 --- a/rules/styles.css +++ /dev/null @@ -1,6 +0,0 @@ -:root{--paper:#f5f4f1;--ink:#172f42;--muted:#697b89;--accent:#7c78a8;--blue:#527f9f;--gold:#efc76b;--line:#d8dee2;--deep:#102536;--font-sans:manrope,arial,sans-serif;--font-mono:'DM Mono',monospace;}*{box-sizing:border-box}html{scroll-behavior:smooth}body{min-width:320px;margin:0;color:var(--ink);background:var(--paper);font-family:var(--font-sans)}.progress{position:fixed;z-index:20;top:0;left:0;width:100%;height:3px;background:transparent}.progress span{display:block;width:0;height:100%;background:var(--gold)}.topbar{position:sticky;z-index:10;top:0;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;min-height:72px;padding:0 clamp(24px,5vw,90px);border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--paper) 94%,transparent);backdrop-filter:blur(14px)}.back{display:flex;align-items:center;gap:11px;color:var(--ink);font:700 10px var(--font-mono);text-decoration:none;text-transform:uppercase}.back b{display:grid;place-items:center;width:30px;height:30px;color:var(--paper);background:var(--accent);font-size:15px}.topbar nav{display:flex;gap:28px}.topbar nav a,.languages{color:var(--muted);font:600 9px var(--font-mono);letter-spacing:.08em;text-decoration:none;text-transform:uppercase}.languages{justify-self:end;display:flex;align-items:center;gap:7px}.languages button{padding:7px;border:0;color:var(--muted);background:transparent;cursor:pointer}.languages button.active{color:var(--paper);background:var(--blue)}main{max-width:1920px;margin:auto;padding:0 clamp(24px,5vw,90px)}.hero{min-height:700px;padding:130px 0 80px}.eyebrow,.section-label,.thesis>span{color:var(--accent);font:600 9px var(--font-mono);letter-spacing:.1em;text-transform:uppercase}.hero h1,.intro h2{margin:22px 0 50px;font-size:clamp(64px,8.3vw,148px);line-height:.82;letter-spacing:-.075em}.hero h1 em,.intro h2 em{color:var(--blue);font-family:Georgia,serif;font-weight:400}.hero-foot{display:grid;grid-template-columns:1.3fr .7fr;gap:70px;align-items:end}.hero-foot>p{max-width:750px;margin:0;color:var(--muted);font-size:16px;line-height:1.75}.hero-foot aside{display:grid;gap:8px;padding:23px;border-left:6px solid var(--gold);color:var(--paper);background:var(--deep)}.hero-foot aside span,.hero-foot aside small{color:var(--gold);font:500 8px var(--font-mono);letter-spacing:.08em}.hero-foot aside strong{font-size:32px}.thesis{display:grid;grid-template-columns:200px 1fr;gap:35px;align-items:center;margin:0 calc(clamp(24px,5vw,90px) * -1) 130px;padding:35px clamp(24px,5vw,90px);color:var(--paper);background:var(--ink)}.thesis strong{font-size:clamp(20px,2.4vw,38px);line-height:1.2}.section-label{display:flex;justify-content:space-between;padding-bottom:15px;border-bottom:1px solid var(--line)}.section-label span:last-child{color:var(--muted)}.pipeline-section,.skill-section,.examples,.copy-lab{margin-bottom:140px;padding-top:30px}.intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.intro h2{margin:15px 0 0;font-size:clamp(48px,5.6vw,92px)}.intro>p{max-width:650px;margin:0;color:var(--muted);font-size:14px;line-height:1.75}.pipeline{display:grid;grid-template-columns:1fr auto 1fr auto 1fr auto 1fr auto 1fr;align-items:stretch;border:1px solid var(--line);background:var(--paper)}.pipeline button{display:grid;gap:8px;min-height:126px;padding:20px;border:0;color:var(--ink);background:transparent;text-align:left;cursor:pointer}.pipeline button span{color:var(--accent);font:600 9px var(--font-mono)}.pipeline button strong{font:700 13px var(--font-mono)}.pipeline button small{color:var(--muted);font:10px var(--font-mono)}.pipeline button.active{color:var(--paper);background:var(--blue);box-shadow:inset 0 -6px var(--gold)}.pipeline button.active span,.pipeline button.active small{color:var(--gold)}.pipeline>i{align-self:center;color:var(--gold);font-style:normal}.stage-detail{display:grid;grid-template-columns:120px 1.3fr .7fr;gap:35px;min-height:330px;padding:42px;color:var(--paper);background:var(--deep)}.stage-number{color:#ffffff24;font:500 76px var(--font-mono)}.stage-detail>div:nth-child(2)>span,.skill-detail header span{color:var(--gold);font:500 9px var(--font-mono);letter-spacing:.08em}.stage-detail h3,.skill-detail h3{margin:15px 0;font-size:clamp(28px,3vw,48px);letter-spacing:-.05em}.stage-detail p,.skill-detail p{max-width:650px;color:#b8c8d2;font-size:13px;line-height:1.7}.stage-detail a,.skill-detail a,.example-grid a{display:inline-block;margin-top:15px;color:var(--gold);font:600 9px var(--font-mono);letter-spacing:.07em}.stage-detail pre,.skill-detail pre,.example-grid pre,.copy-lab pre{margin:0;overflow:auto;background:#081621}.stage-detail pre{align-self:stretch;padding:28px}.stage-detail code,.skill-detail code,.example-grid code,.copy-lab code{color:#c9d5dc;font:11px/1.7 var(--font-mono)}.skill-console{display:grid;grid-template-columns:minmax(240px,.7fr) minmax(0,1.5fr);min-height:570px;border:1px solid var(--line);background:var(--line);gap:1px}.skill-list{display:grid;grid-template-rows:repeat(6,1fr);gap:1px;background:var(--line)}.skill-list button{display:grid;align-content:center;gap:7px;padding:18px 22px;border:0;color:var(--ink);background:var(--paper);text-align:left;cursor:pointer}.skill-list button strong{font:700 12px var(--font-mono)}.skill-list button small{color:var(--muted);font-size:10px}.skill-list button.active{color:var(--paper);background:var(--accent);box-shadow:inset 6px 0 var(--gold)}.skill-list button.active small{color:#f0eef8}.skill-detail{display:grid;align-content:center;padding:50px clamp(30px,5vw,80px);color:var(--paper);background:#5b7098}.skill-detail header{display:grid;gap:9px;padding-bottom:22px;border-bottom:1px solid #ffffff3a}.skill-detail header strong{font-size:12px}.skill-detail pre{margin:20px 0 0;padding:18px}.example-grid{display:grid;grid-template-columns:1fr 1fr;gap:1px;margin-top:45px;background:var(--line)}.example-grid article{display:grid;grid-template-rows:auto auto 1fr auto;min-height:390px;padding:35px;background:var(--paper)}.example-grid article>span{color:var(--accent);font:600 9px var(--font-mono);letter-spacing:.08em}.example-grid h3{margin:22px 0;font-size:clamp(26px,3vw,42px);line-height:1.05;letter-spacing:-.045em}.example-grid pre{padding:22px}.example-grid a{color:var(--blue)}.copy-lab{display:grid;grid-template-columns:.75fr 1.25fr;gap:1px;color:var(--paper);background:var(--line)}.copy-lab>div{padding:45px;background:var(--accent)}.copy-lab>div span{color:var(--gold);font:600 9px var(--font-mono)}.copy-lab h2{margin:20px 0;font-size:clamp(30px,3.4vw,56px);line-height:1}.copy-lab>div p{max-width:420px;color:#eceaf5;line-height:1.6}.copy-lab article{display:grid;grid-template-rows:auto 1fr auto;min-width:0;background:var(--deep)}.copy-lab button{justify-self:end;display:flex;gap:20px;margin:20px;padding:11px 14px;border:1px solid #ffffff50;color:var(--paper);background:transparent;font:600 9px var(--font-mono);cursor:pointer}.copy-lab pre{padding:26px;white-space:pre-wrap}.copy-lab article>p{min-height:20px;margin:12px 24px;color:var(--gold);font:600 9px var(--font-mono)}.deeper{margin:0 calc(clamp(24px,5vw,90px) * -1);padding:60px clamp(24px,5vw,90px) 100px;background:var(--gold)}.deeper>div{display:flex;justify-content:space-between;gap:30px;margin-bottom:35px}.deeper>div span{color:var(--accent);font:600 9px var(--font-mono)}.deeper>div strong{max-width:700px;font-size:clamp(24px,3vw,44px);line-height:1.1}.deeper nav{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:#132b3b}.deeper nav a{display:grid;gap:9px;min-height:160px;padding:25px;color:var(--paper);background:var(--ink);text-decoration:none}.deeper nav span{color:var(--accent);font:600 10px var(--font-mono)}.deeper nav b{font-size:15px}.deeper nav small{color:#9eb0bb;font:9px var(--font-mono)}.topbar a:focus-visible,.topbar button:focus-visible,.pipeline button:focus-visible,.skill-list button:focus-visible,.copy-lab button:focus-visible,.deeper a:focus-visible{outline:3px solid var(--gold);outline-offset:-3px} - -@media(min-width:2200px){main{max-width:2880px;padding-inline:clamp(150px,7vw,300px)}.hero{min-height:900px}.hero h1{font-size:clamp(150px,7vw,220px)}.pipeline button{min-height:170px}.stage-detail{min-height:440px}.skill-console{min-height:720px}} -@media(max-width:900px){.topbar{grid-template-columns:1fr auto}.topbar nav{display:none}.hero{min-height:auto}.hero-foot,.intro,.copy-lab{grid-template-columns:1fr}.pipeline{grid-template-columns:1fr 1fr 1fr}.pipeline>i{display:none}.pipeline button{min-height:100px}.stage-detail{grid-template-columns:80px 1fr}.stage-detail pre{grid-column:1/-1}.skill-console{grid-template-columns:1fr}.skill-list{grid-template-columns:1fr 1fr;grid-template-rows:none}.example-grid{grid-template-columns:1fr}.deeper nav{grid-template-columns:1fr 1fr}} -@media(max-width:600px){main{padding-inline:18px}.topbar{padding-inline:18px}.hero{padding-top:80px}.hero h1{font-size:clamp(54px,17vw,78px)}.hero-foot{gap:30px}.thesis{grid-template-columns:1fr;margin-inline:-18px;padding-inline:18px}.section-label{gap:20px}.section-label span:last-child{text-align:right}.pipeline{grid-template-columns:1fr}.pipeline button{grid-template-columns:38px 1fr;grid-template-rows:auto auto;min-height:82px}.pipeline button span{grid-row:1/-1;align-self:center}.stage-detail{grid-template-columns:1fr;padding:28px 22px}.stage-number{font-size:48px}.stage-detail pre{grid-column:auto}.skill-list{grid-template-columns:1fr}.skill-detail{padding:34px 24px}.example-grid article{min-height:340px;padding:25px}.copy-lab>div{padding:30px 24px}.deeper{margin-inline:-18px;padding-inline:18px}.deeper>div{display:grid}.deeper nav{grid-template-columns:1fr}.deeper nav a{min-height:120px}} -@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}.progress span{transition:none}} diff --git a/scripts/audit-ui.mjs b/scripts/audit-ui.mjs index d906d67..ef2e734 100644 --- a/scripts/audit-ui.mjs +++ b/scripts/audit-ui.mjs @@ -1,18 +1,20 @@ import { readFileSync, readdirSync, statSync } from 'node:fs'; import { dirname, join } from 'node:path'; +const base = '/ai-for-dummies/'; const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8'); +// The legacy hand-written pages are gone; the built output is the site now. const pages = [ - 'index.html', - 'full-guide/index.html', - 'summary/index.html', - 'models/index.html', - 'agents/index.html', - 'skills/index.html', - 'rules/index.html', - 'skills-review/index.html', - 'hands-on/starter/index.html', - 'hands-on/rules/index.html', + 'dist/index.html', + 'dist/full-guide/index.html', + 'dist/summary/index.html', + 'dist/models/index.html', + 'dist/agents/index.html', + 'dist/skills/index.html', + 'dist/rules/index.html', + 'dist/skills-review/index.html', + 'dist/hands-on/starter/index.html', + 'dist/hands-on/rules/index.html', ]; const walk = (path) => readdirSync(path).flatMap((name) => { @@ -49,8 +51,13 @@ for (const [kind, values] of Object.entries(baselineValues)) { const missing = values.filter((value) => !builtValues[kind].has(value)); if (missing.length) throw new Error(`built CSS lost ${kind}: ${missing.join(', ')}`); } -// Legacy fixtures still ship directly. Resolve every var() from the stylesheets -// that page actually links; Astro's tokens cannot mask a broken standalone page. +// The hands-on labs still ship their own stylesheet rather than going through +// Astro. Resolve every var() from the stylesheets each page actually links, so +// Astro's tokens cannot mask a broken standalone page. +// Astro emits base-absolute hrefs (`/ai-for-dummies/_astro/x.css`); those are +// rooted at `dist/`, not at the page's own directory. +const resolveHref = (page, href) => + href.startsWith(base) ? join('dist', href.slice(base.length)) : join(dirname(page), href); for (const page of pages) { const html = read(page); const linked = [...html.matchAll(/]+rel="stylesheet"[^>]+href="([^"]+)"/gi)].map( @@ -58,7 +65,7 @@ for (const page of pages) { ); const styles = linked .filter((href) => !/^https?:/i.test(href)) - .map((href) => read(join(dirname(page), href))); + .map((href) => read(resolveHref(page, href))); const defined = new Set(styles.flatMap((css) => [...definitions(css)])); const unresolved = new Set( styles.flatMap((css) => diff --git a/scripts/verify.mjs b/scripts/verify.mjs index 2b8e93b..b736766 100644 --- a/scripts/verify.mjs +++ b/scripts/verify.mjs @@ -46,7 +46,7 @@ const source = { read('src/content/providers/claude.json'), read('src/content/providers/gemini.json'), ].join('\n'), - starter: read('hands-on/starter/app.js'), + starter: read('public/hands-on/starter/app.js'), voteService: read('vote-service/main.go'), ndoReview: read('src/content/reviews/ndo-repro.md'), }; @@ -73,32 +73,16 @@ if (rendered(html.starter) !== snapshot('hands-on/starter.txt')) if (rendered(html.labRules) !== snapshot('hands-on/rules.txt')) throw new Error('rules lab rendered-text snapshot changed'); -const braceMatch = (source, open) => { - let depth = 0; - let quote = ''; - let escaped = false; - for (let index = open; index < source.length; index += 1) { - const character = source[index]; - if (quote) { - if (escaped) escaped = false; - else if (character === '\\') escaped = true; - else if (character === quote) quote = ''; - continue; - } - if (character === "'" || character === '"' || character === '`') quote = character; - else if (character === '{') depth += 1; - else if (character === '}' && --depth === 0) return index; - } - throw new Error('could not brace-match legacy Portuguese translations'); -}; -const legacyGuide = read('app.js'); -const translationsStart = legacyGuide.indexOf('const translations ='); -const objectStart = legacyGuide.indexOf('{', translationsStart); -const objectEnd = braceMatch(legacyGuide, objectStart); -const translations = Function(`return (${legacyGuide.slice(objectStart, objectEnd + 1)})`)(); -const portuguese = Object.values(translations.pt).map(rendered); +// The 102 Portuguese strings were extracted verbatim from the legacy +// `app.js` `translations.pt` object at cutover, before that file was deleted. +// This is a legacy capture, not a snapshot of the Astro build: it still asserts +// against an independent source, which is the whole point of the check. +const portuguese = JSON.parse(read('.agents/snapshots/full-guide-pt.json')).map(rendered); if (portuguese.length !== 102) - throw new Error('full-guide Portuguese source no longer has 102 translated strings'); + throw new Error('full-guide Portuguese snapshot no longer has 102 translated strings'); +// Without this, trimming the snapshot would make the check below pass vacuously. +if (portuguese.some((value) => !value.trim())) + throw new Error('full-guide Portuguese snapshot has an empty entry'); if (!portuguese.every((value) => rendered(html.guide).includes(value))) throw new Error('built full-guide lost a Portuguese translation'); diff --git a/skills-review/index.html b/skills-review/index.html deleted file mode 100644 index 021d316..0000000 --- a/skills-review/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - Submitted Skills — Review Desk - - - - - - - -
-
← field guideSUBMITTED SKILLS / REVIEW DESKsubmissions
-
-

A friendly path from draft to dependable

-

Every skill deserves
a clear job.

-

Read the original, understand what already works, and compare a safer, leaner draft. Nothing here overwrites a submission; revisions live in their own review output.

-
-
-
01DiscoverableA precise description tells an agent when to load the skill.
-
02Useful in contextCore workflow stays short; conditional detail loads only when needed.
-
03Safe by designCommands, secrets, and shared systems have explicit boundaries.
-
04Proven in useReal prompts and observable checks turn a draft into a reliable tool.
-
-
-

How to use this desk

Compare.
Then choose.

-
  1. Select a submission, or open an author URL.
  2. Read the gentle review before judging the draft.
  3. Choose Preview Markdown in the file toolbar to render either version.
  4. Copy or download the version you want, then vote for the draft you would ship.
-
-
- -
-
-
-

Why these reviews look this way

-

The recommendations follow the open Agent Skills format: valid frontmatter for discovery, progressive disclosure for context economy, deterministic scripts for fragile repeated mechanics, and behavioral evaluation rather than a checklist of pretty headings.

- -
-
Share an author with ?author=Name, or one review with ?author=Name&skill=skill-id&view=improved. To add a submission later: drop a package under submitted-skills/, add an entry under src/content/reviews/{new-id}.md (and mirror it into skills-review/catalog.js which the desk still reads), then run node scripts/build-skill-review.mjs. Votes call a separate service — see vote-service/ — one per visitor, tracked by network source.
-
- - - diff --git a/skills/app.js b/skills/app.js deleted file mode 100644 index 882cd2c..0000000 --- a/skills/app.js +++ /dev/null @@ -1,23 +0,0 @@ -const packageFiles = { - skill: { label: 'SKILL.md', title: 'The operating contract', body: 'The one file that should always be loaded. Define the exact trigger, the ordered workflow, safety limits, and the evidence the agent returns.', code: '---\nname: review-ui\ndescription: Review a changed UI for focus, reflow, and motion.\n---\n\n1. Inspect the changed interaction.\n2. Run the UI checks.\n3. Return findings with evidence.' }, - references: { label: 'references/', title: 'Facts, only when needed', body: 'Keep conditional detail out of the main instruction. A dialog pattern, framework caveat, or accessibility checklist belongs here when it is not needed for every review.', code: 'references/\n└── accessibility.md\n ├── keyboard interaction patterns\n └── focus and reflow checklist' }, - scripts: { label: 'scripts/', title: 'Mechanics that should not depend on memory', body: 'Turn deterministic checks into runnable tools. The agent still judges the result, but it should not have to recreate a viewport test or filename rule by hand.', code: 'scripts/\n└── check-reflow.mjs\n └── checks 320px, 1280px, and 4K widths' }, - assets: { label: 'assets/', title: 'Starting material, not hidden instructions', body: 'Use assets for templates and examples a person or agent can copy. Keep them clearly named so package readers can choose the right starting point.', code: 'assets/\n├── review-report.md\n└── focus-test-fixture.html' } -}; - -const preview = document.querySelector('#package-preview'); -const buttons = [...document.querySelectorAll('[data-package-file]')]; -function renderPackage(file) { - const item = packageFiles[file]; - preview.classList.remove('is-swapping'); - void preview.offsetWidth; - preview.classList.add('is-swapping'); - preview.innerHTML = `SELECTED / ${item.label}

${item.title}

${item.body}

${item.code}
`; - buttons.forEach((button) => { - const selected = button.dataset.packageFile === file; - button.classList.toggle('active', selected); - button.setAttribute('aria-selected', String(selected)); - }); -} -buttons.forEach((button) => button.addEventListener('click', () => renderPackage(button.dataset.packageFile))); -renderPackage('skill'); diff --git a/skills/index.html b/skills/index.html deleted file mode 100644 index cda52ac..0000000 --- a/skills/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - AI For Dummies — Skills - - - - -
-
← ROUTE MAP03 / SKILLSreview desk ↗
-

Reusable judgment

Teach the
decision.

A skill changes behavior. Keep the trigger precise, put the workflow in SKILL.md, and move conditional facts, scripts, and examples into focused files.

-
-

Package anatomy

One job.
More than
one file.

Choose a file to see why it belongs in the package.

-
-
-

REVIEW-UI / SKILL PACKAGE

- - - - -
-
-
-
-

Create a skill

Observe →
trigger →
validate

01
Observe frictionFind a repeated decision or failure.
02
Define the triggerSay when it should load and when it should stay out.
03
Choose anatomyUse references for facts and scripts for deterministic mechanics.
04
Evaluate behaviorTest realistic prompts, edge cases, safety, and evidence.
- -
- - - diff --git a/src/components/blocks/WorktreeMap.astro b/src/components/blocks/WorktreeMap.astro index c75016e..f790941 100644 --- a/src/components/blocks/WorktreeMap.astro +++ b/src/components/blocks/WorktreeMap.astro @@ -281,7 +281,7 @@ const { branches, initial = 'main', rootLabel = 'ROOT', rootSmall = '● clean' } } - /* token-gap: preserve the legacy full-guide layout threshold; task 20 can retire it with the legacy stylesheet */ + /* token-gap: preserve the legacy full-guide layout threshold; responsive.css is gone, so this rule is now the only definition of it */ @media (max-width: 600px) { .tree-stage { height: auto; diff --git a/src/layouts/ChapterLayout.astro b/src/layouts/ChapterLayout.astro index 03c72b9..b2347c3 100644 --- a/src/layouts/ChapterLayout.astro +++ b/src/layouts/ChapterLayout.astro @@ -13,7 +13,7 @@ import BaseLayout from './BaseLayout.astro'; import TopBar from '../components/blocks/TopBar.astro'; import SiteFooter from '../components/blocks/SiteFooter.astro'; -import chaptersStylesheet from '../../chapters.css?url'; +import chaptersStylesheet from '../../legacy/styles/chapters.css?url'; interface Props { title: string; diff --git a/src/pages/full-guide.astro b/src/pages/full-guide.astro index fc50e3b..db7b5af 100644 --- a/src/pages/full-guide.astro +++ b/src/pages/full-guide.astro @@ -14,8 +14,8 @@ import CopyPrompt from '../components/islands/CopyPrompt.astro'; import GuideSelector from '../components/islands/GuideSelector.astro'; import LanguageToggle from '../components/islands/LanguageToggle.astro'; import ReadingProgress from '../components/islands/ReadingProgress.astro'; -import '../../styles.css'; -import '../../full-guide/audit.css'; +import '../../legacy/styles/guide.css'; +import '../../legacy/styles/audit.css'; type Entry = { data: T }; const toRecord = (entries: Entry[]) => @@ -1464,7 +1464,7 @@ const base = import.meta.env.BASE_URL; margin-left: auto; } } - /* token-gap: preserve the legacy full-guide layout threshold; task 20 can retire it with the legacy stylesheet */ + /* token-gap: preserve the legacy full-guide layout threshold; responsive.css is gone, so this rule is now the only definition of it */ @media (max-width: 600px) { .topbar-tools .edition { display: none; @@ -3083,7 +3083,7 @@ const base = import.meta.env.BASE_URL; grid-column: auto; } } - /* token-gap: preserve the legacy full-guide layout threshold; task 20 can retire it with the legacy stylesheet */ + /* token-gap: preserve the legacy full-guide layout threshold; responsive.css is gone, so this rule is now the only definition of it */ @media (max-width: 600px) { .tree-detail { grid-template-columns: 1fr; diff --git a/src/pages/skills-review.astro b/src/pages/skills-review.astro index 45795b3..84fc3dc 100644 --- a/src/pages/skills-review.astro +++ b/src/pages/skills-review.astro @@ -1,8 +1,8 @@ --- import { getCollection } from 'astro:content'; import BaseLayout from '../layouts/BaseLayout.astro'; -import reviewStylesheet from '../../skills-review/styles.css?url'; -import changeLensStylesheet from '../../skills-review/change-lens.css?url'; +import reviewStylesheet from '../../legacy/styles/skills-review.css?url'; +import changeLensStylesheet from '../../legacy/styles/change-lens.css?url'; const reviews = await getCollection('reviews'); const reviewOrder = [ @@ -152,6 +152,6 @@ const serializedCatalog = JSON.stringify(catalog).replace(/ diff --git a/src/pages/skills.astro b/src/pages/skills.astro index 9e9518b..ac538b8 100644 --- a/src/pages/skills.astro +++ b/src/pages/skills.astro @@ -8,7 +8,7 @@ import { getEntry } from 'astro:content'; import ChapterLayout from '../layouts/ChapterLayout.astro'; import ChapterHero from '../components/blocks/ChapterHero.astro'; import SkillPackageExplorer from '../components/islands/SkillPackageExplorer.astro'; -import skillsStylesheet from '../../skills/styles.css?url'; +import skillsStylesheet from '../../legacy/styles/skills.css?url'; // Required-field guard. The chapters schema marks section eyebrow / // panelLabel / panelCode / steps / copy as optional because the schema diff --git a/summary/index.html b/summary/index.html deleted file mode 100644 index ed706a8..0000000 --- a/summary/index.html +++ /dev/null @@ -1 +0,0 @@ -AI For Dummies — Route map
← AI FOR DUMMIES00 / ROUTE MAPreview desk ↗

Start here

Ship the
system.

This guide turns AI work into a shape: frame the problem, choose the model and agent, isolate changes, teach repeatable decisions, and verify the result.

05

Practice

Compare prompts and skill-enabled runs.

Open lab →
06

Review desk

Browse original files and improved drafts.

Open desk →