# Task 15d — Assemble /full-guide/

**Agent**: `page-migrator` · **Model**: **Codex** **Depends on**: 05b, 10, 13,
15a, 15b, 15c · **Parallel with**: 16 · **Blocks**: 15e, 18, 19 **Worktree**:
`.agents/scripts/worktree.sh start 15d page-full-guide`

## Goal

`src/pages/full-guide.astro`. 22 KB of HTML, bilingual throughout, everything
interactive already built by 15a/15b/15c and every block already built by
task 10. **This task is assembly.** If you find yourself writing a new island,
stop — it belongs to one of the earlier briefs and you should report the gap
instead.

Read the reports from 15a, 15b and 15c first. They state their props and the
language contract.

## Islands and nothing else

| Island                          | Directive        | From |
| ------------------------------- | ---------------- | ---- |
| Guide selector (one, ×9 groups) | `client:visible` | 15a  |
| Copy-prompt buttons + progress  | `client:visible` | 15b  |
| Language toggle                 | `client:idle`    | 15c  |

Everything else is server-rendered.

## Asserted by verify.mjs — all must survive

`const phases`, `const handsOnPrompts`, `const modelGuide`,
`const skillSources`, `const skillInstallPrompts`, `render('plan')`,
`renderTree`, `renderWorker`, `renderRoute`, `renderModelProvider`,
`renderEffort`, `renderSkillFile`, `renderSkillWorkflow`, `renderCommonSkill`,
`renderHandsOn`, `copyPrompt`, plus
`data-copy-target="prompt-install-skills|prompt-basic|prompt-skills"`,
`hands-on/starter/`, `additional-reading.md`, `role="tablist"`, `<table>`.

Implementation-detail assertions, deliberately. **Never delete one.** Task 19
re-points them to output-level checks; you do not.

## Watch for

- `hands-on/starter/` is a **lab fixture**. Link to it, ship it as a static
  asset, do not componentize it. Same for `hands-on/rules/`.
- `renderHandsOn` takes no argument — it is not part of 15a's selector pattern.
  Check whether 15a covered it; if not, it is yours, and say so in the report.
- Do not delete `responsive.css` here. That is 15e, and it needs screenshots.

## Done when

- [ ] Snapshot diff against `.agents/snapshots/` empty
- [ ] Every interaction works: all nine selector groups, both languages, all
      three copy buttons
- [ ] Keyboard: arrows move between tabs; focus visible throughout
- [ ] JS payload **smaller** than today's 50 KB — content is static now
- [ ] Screenshots match at 560 / 800 / 1100 / 1600 px
- [ ] `pnpm run gate` green — the full gate, not `verify` + `audit-ui` alone
- [ ] 42 assertions intact

## What 15a, 15b and 15c actually shipped

Read `.agents/context/content-i18n.md` first — it is the language contract and
it is binding on this task.

- **`src/components/islands/GuideSelector.astro`** — one island, all nine
  groups. Render it as
  `<GuideSelector rootSelector="#full-guide" data={...} />`. `data` needs
  `phases`, `workers`, `trees`, `routes`, `providers`, `efforts`, `skillFiles`,
  `skillWorkflow`, `commonSkills` and a bilingual `labels` object; the
  `GuideSelectorData` interface at the top of the file is the exact shape. You
  server-render each group's shell and its initial detail panel, keeping today's
  `data-*` hooks and `.active` state. Mark each group `role="tablist"` and its
  controls `role="tab"` or the keyboard handler will not bind.
- **`src/components/islands/LanguageToggle.astro`** — render each localized
  fragment twice, with `data-language-content="en"` or `"pt"` on the outer
  element. The toggle flips `hidden` on those, sets `<html lang>`, and fires
  `ai-for-dummies:languagechange` on `window`. It is a plain `.astro` island
  that defers its own setup with `requestIdleCallback`; **do not** put
  `client:idle` on it — that directive is framework-components only.
- **`src/components/islands/CopyPrompt.astro`** — one per button. Pass
  `target="prompt-install-skills" | "prompt-basic" | "prompt-skills"`. Render
  `<p id="copy-status" role="status" aria-live="polite">` exactly once on the
  page; the island writes into it. Fill the `<pre><code id="prompt-…">` bodies
  from the `handsOnPrompts` and `skillInstallPrompts` collections — 15b verified
  those are byte-identical to the legacy `app.js` constants, and the clipboard
  copies whatever you render, so do not reformat them.
- **`src/components/islands/ReadingProgress.astro`** — replaces the legacy
  `<div class="reading-progress">` at the top of the page.

Nothing else is missing. If you are about to write an island, you are doing
another task's work — report the gap instead.

## Two things to expect

- **The snapshot will not match by construction.** Dual-locale rendering emits
  both languages into the HTML where today's page emits English plus a
  Portuguese map inside `app.js`. Compare _rendered, language-filtered_ output
  against today's page, and if `.agents/snapshots/` needs regenerating, say so
  explicitly in your report with what changed and why — do not quietly rewrite a
  snapshot to make a diff go away.
- **`renderHandsOn` takes no argument** and is not part of GuideSelector's
  nine-group pattern. It is yours. Its name is asserted by `verify.mjs`.

## The data exists now (task 05b)

Your first attempt stopped here, correctly: six of the nine selector groups had
no collection to read. Task 05b fixed that. `src/content/` now carries `workers`
(3), `trees` (4), `routes` (4), `skillFiles` (4), `skillWorkflow` (5) and
`commonSkills` (7) alongside the six task 05 already migrated. Every string was
verified byte-identical to its `app.js` original — 221 values, zero mismatches —
so read them as authoritative and do not re-derive from `app.js`.

Two things 05b decided that you should know:

- `trees[id].status` is in the schema although `GuideSelectorData` omits it.
  `index.html` renders it as the dot colour on each tree node. The island does
  not consume it; the page may.
- `commonSkills[id].source` is embedded per entry rather than joined from
  `skillSources[id].url` at render time. Both carry the same URL.

## The one piece still missing: `labels`

`GuideSelectorData.labels` is not a collection. Those strings — `OWNER` /
`RESPONSÁVEL`, `REASONING LOAD` / `CARGA DE RACIOCÍNIO`, `context: isolated` /
`contexto: isolado` and the rest — are still hard-coded bilingual literals
inside the `render*` functions in `app.js`. 05b deliberately left them, because
they are page-chrome rather than content.

They are yours. Lift them verbatim — same rule as everything else, both locales
mandatory, no retranslation, copy the exact strings out of `app.js`. Whether
they become a seventh collection or an inline constant in the page is your call;
say which you chose and why.

## The first attempt was rejected — read this before you start

Commit `a264d01` (tagged `rejected/15d-attempt-1`) passed the full gate with 42
assertions intact and is still wrong. It did this:

```js
import legacyGuide from '../../full-guide/index.html?raw';
let guideMarkup = legacyGuide.match(/<main>[\s\S]*<\/main>/)?.[0] ?? '';
```

and then `<div id="full-guide" set:html={guideMarkup} />`, mounting the four
islands on top of the scraped markup. Three things that breaks:

1. **Portuguese is gone.** The page contains zero `data-language-content`
   attributes and zero `.pt` reads — every server-rendered detail panel
   hard-codes `.en`. The legacy `<main>` is English-only; today's Portuguese
   comes from `app.js`, which the Astro page does not load. The language
   contract in `.agents/context/content-i18n.md` is binding and this violated
   it. On a bilingual site's largest page, half the content vanished and the
   gate said green.
2. **Zero of task 10's block components are used.** All 19 exist in
   `src/components/blocks/`. See the list below.
3. **It couples the new page to the file task 20 deletes.** `/full-guide/` would
   break the moment the legacy tree goes.

**You may not read `full-guide/index.html` at build time.** Read it to learn
what to build; do not import it, scrape it, or `set:html` it. The page's markup
comes from components and content collections.

### The blocks you are assembling from

`src/components/blocks/`: `ChangeLens`, `ChapterHero`, `ComparisonTable`,
`FileTabs`, `FleetDiagram`, `GridGroup`, `HandoffTable`, `PhasePanel`,
`PreviewPane`, `ReviewDetail`, `RouteCard`, `RouteTable`, `SectionGrid`,
`SiteFooter`, `SkillList`, `SkillPackage`, `TopBar`, `VoteWidget`,
`WorktreeMap`. Plus `src/components/primitives/`. Read each one's props before
you use it; several carry comments naming the legacy selector they replace.

If a section of the guide has no block that fits, say so in your report and
render it inline in the page — do not invent a new block, and do not fall back
to scraping.

### Two extra done-when boxes

- [ ] Every localized string rendered twice, `data-language-content="en"` and
      `"pt"`, per `.agents/context/content-i18n.md`
- [ ] Zero imports of any file under `full-guide/`, and no `set:html` of legacy
      markup

## Attempt 2: structure accepted, bilingual work unfinished

Commit `677c511` is the right shape and is the base to build on — no legacy
import, no `set:html`, six blocks used (`FleetDiagram`, `HandoffTable`,
`PhasePanel`, `RouteTable`, `SkillPackage`, `WorktreeMap`), one file changed,
`verify.mjs` untouched, 42 assertions, gate green, JS 47,079 B against the
legacy 50,338 B. Its report was honest about what it did not finish. Finish it.

**What is already correct — do not "fix" it.** The page has 55 `.en` reads and
zero `.pt` reads in the selector detail panels. That is right.
`GuideSelector.astro` re-renders every panel with `[locale]` on
`ai-for-dummies:languagechange`, so the server-rendered panel only has to match
the initial locale. Leave those alone.

**What is missing: the static prose.** Legacy `app.js` holds `translations.pt` —
a map of **102** CSS-selector → Portuguese-string entries, starting at
`.chapter-links a:nth-child(1)`. `applyLanguage('pt')` walks it and calls
`setText(selector, value)`; switching back replays the captured `originals`.
That map is the full-guide page's static Portuguese, and it is the authoritative
source for this work.

The page currently carries **3** `data-language-content` pairs (hero, stat,
thesis). The other ~99 strings have no Portuguese counterpart anywhere in the
Astro output, so `/full-guide/` renders English-only for everything the selector
islands do not own.

Render each of those 102 strings twice per `.agents/context/content-i18n.md`:
the English exactly as it appears in `full-guide/index.html` today, the
Portuguese exactly as it appears in `translations.pt`. Verbatim both ways — no
retranslation, no rephrasing, no fixing what looks like a typo.

A selector in the map that targets an element the blocks now render means the
pair belongs inside that block's slot content, not bolted on afterwards. If a
block gives you no way to pass both locales, say so in the report and name the
block — do not work around it by duplicating the block.

### Done when, for this pass

- [ ] All 102 `translations.pt` entries have a rendered Portuguese counterpart
- [ ] Every localized static string wrapped in `data-language-content="en"` /
      `"pt"` pairs
- [ ] No `.pt` reads added to the nine selector detail panels
- [ ] Gate green, 42 assertions, `verify.mjs` untouched
- [ ] Report lists any `translations.pt` selector you could not place, and why

## Attempt 3: the blocker is cleared

Task 10b landed. `FleetDiagram`, `HandoffTable`, `PhasePanel`, `RouteTable`,
`SkillPackage` and `WorktreeMap` now accept `Localized = { en, pt }` on every
prose prop and emit the `data-language-content` pair themselves. Passing a plain
`string` still renders exactly what it rendered before, so nothing you already
wrote has to change shape — you pass `{ en, pt }` where you passed a string.

You are the first call site for those six blocks. No merged page uses them, so
if a prop is wrong you will be the one to find it. Report anything that does not
fit rather than working around it.

**The measurement.** 17 of the 102 `translations.pt` entries currently reach the
built page. Verify against `dist/full-guide/index.html`, not against the
`.astro` source — `/rules/` looks monolingual in source and is fully bilingual
in output, because the strings arrive through a content collection. Checking the
source is how you get a wrong answer here.

For each of the 102 entries: the selector tells you which element, the value is
the Portuguese, and `full-guide/index.html` has the English. Both verbatim.

Remaining done-when boxes are the ones in the previous section, unchanged.

## Attempt 4: 68 of 102, and the last 34 are listed here

Attempt 3 took coverage from 17 to 68 of the 102 `translations.pt` entries,
measured against `dist/full-guide/index.html`. Its snapshot rewrite is accepted
— it explained the reason (sections attempt 2 had dropped came back, and
`CopyPrompt` renders slightly differently), which is what the brief asked for.

Three cleanups first, before any new work:

- `translations_extracted.js`, `scratch/` and
  `.agents/snapshots/full-guide-new.txt` are untracked leftovers in the
  worktree. Delete them. Do not commit them.
- Never `git add -A` from the repo root here.

### You may edit three blocks, and only for this

`WorktreeMap`, `RouteTable` and `SkillPackage` still hard-code English inside
the component, so no prop the page passes can reach those strings. Task 10b made
their prop-driven prose `Localized`; it did not catch the literals baked into
the markup.

Extend exactly those three, using 10b's established pattern: a `Localized` value
renders the `data-language-content="en"` / `"pt"` pair, a plain `string` renders
exactly what it renders today. You are still the only call site, so nothing else
can regress. Do not touch any other block.

### The 34 remaining entries

Each row is the `translations.pt` selector and its Portuguese. The English is in
`full-guide/index.html` at the same selector. Both verbatim — no retranslation,
no rephrasing, no fixing what looks like a typo.

| selector                               | Portuguese                                                                                                                                                                                                                                                                                           |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `.caption`                             | O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados verificáveis. Ele não precisa digitar cada linha.                                                                                                                                                                  |
| `.workflow .copy > p:last-child`       | Delegar é mover uma tarefa delimitada para um contexto menor — não abrir mão da responsabilidade.                                                                                                                                                                                                    |
| `.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.                                                                                                                                                     |
| `.tree-node.root span`                 | RAIZ                                                                                                                                                                                                                                                                                                 |
| `.tree-node.root small`                | ● limpo                                                                                                                                                                                                                                                                                              |
| `.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                                                                                                                                                                                                                                                                                    |
| `.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                                                                                                                                                                                                                                                                                      |
| `.catalog-intro .eyebrow`              | O kit de campo                                                                                                                                                                                                                                                                                       |
| `.catalog-intro h2`                    | Trabalhos diferentes.<br />Instintos <em>diferentes.</em>                                                                                                                                                                                                                                            |
| `.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`                 | <b>PLANEJAR</b> unlazy <i>→</i> <b>CONSTRUIR</b> ponytail-lite <i>→</i> <b>DIAGNOSTICAR</b> diagnosing-bugs <i>→</i> <b>REPORTAR</b> caveman                                                                                                                                                         |
| `.callout span`                        | COMECE AQUI                                                                                                                                                                                                                                                                                          |
| `.callout strong`                      | Comece com um agente e uma skill. Adicione paralelismo apenas quando as tarefas forem realmente independentes.                                                                                                                                                                                       |
| `.sources p`                           | Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes. <a href="rules/">Estudo de caso sobre regras e enforcement →</a> <a href="docs/references/README.md">Referências primárias →</a> <a href="docs/references/additional-reading.md">Trilha com 12 leituras →</a> |

The fourteen `[data-common-skill="…"] span` / `small` rows are the selector
button labels, not the detail panel. `GuideSelector` re-renders the panel per
locale but not the button labels, so those need the server-rendered pair like
any other static string.

### Done when

- [ ] All 102 entries present in `dist/full-guide/index.html` — 102, not 68
- [ ] The three blocks take `Localized` for their previously hard-coded strings
- [ ] No untracked scratch files left in the worktree
- [ ] `pnpm run gate` green, 42 assertions, `verify.mjs` untouched
- [ ] Report names any selector you could not place and why
