# Task 15a — The one guide selector island

**Agent**: `component-builder` · **Model**: **Codex** **Depends on**: 05, 10 ·
**Parallel with**: 15b · **Blocks**: 15d **Worktree**:
`.agents/scripts/worktree.sh start 15a guide-selector`

## Why this task exists

Task 15 was attempted twice on Codex and produced no usable commit either time.
Its own second report said "this is an incomplete scaffold, not the real
migration requested". `MODEL-ROUTING.md` says a task that needs more than two
models' worth of hand-holding is too big — so it is split. This is the first
piece: **the interactive machinery, with no page migration in it.**

## Scope

One new island under `src/components/islands/`. You import nothing into a page
and you migrate no page — tasks 07 through 11 shipped components ahead of their
pages the same way, and the gate is happy with an unimported component.

## What you are replacing

`app.js` has nine functions that are the same function nine times:

| Function              | Trigger attribute     | Detail panel target    |
| --------------------- | --------------------- | ---------------------- |
| `render`              | `data-phase`          | `#phase-panel`         |
| `renderWorker`        | `data-worker`         | `#worker-detail`       |
| `renderTree`          | `data-tree`           | `#tree-detail`         |
| `renderRoute`         | `data-route`          | `#route-detail`        |
| `renderModelProvider` | `data-model-provider` | `#provider-detail`     |
| `renderEffort`        | `data-effort`         | `#effort-detail`       |
| `renderSkillFile`     | `data-skill-file`     | `#skill-detail`        |
| `renderSkillWorkflow` | `data-skill-step`     | `#builder-detail`      |
| `renderCommonSkill`   | `data-common-skill`   | `#common-skill-detail` |

Each: click a button in a group, mark it active, swap the panel's `innerHTML`
from a content object. **Build one island, not nine.** If your diff has nine
components you have missed the point of the task.

The panels' inner markup differs per group (`#route-detail` draws a `--score`
meter, `#tree-detail` draws owner/checkout/note/command). Take the shape from
the slot or from a per-group layout, not from nine islands.

## The one coupling that is not uniform

`data-model-provider` clicks also re-run `renderEffort` with the currently
active effort. Preserve that. Everything else is independent.

## Asserted by verify.mjs — all nine names must survive

`render('plan')`, `renderTree`, `renderWorker`, `renderRoute`,
`renderModelProvider`, `renderEffort`, `renderSkillFile`, `renderSkillWorkflow`,
`renderCommonSkill`. Also `role="tablist"`.

These are implementation-detail assertions. They look deletable and are not —
each pins a feature. **Never delete one.** If a name genuinely cannot survive
the new shape, stop and report it; task 19 re-points assertions, you do not.

## Also deliver

- Keyboard: Arrow keys, Home and End move between buttons in a group; focus
  visible throughout. `role="tablist"` groups follow the ARIA tabs pattern.
- Server-render the initially active panel. The panel must not be empty before
  hydration.
- `client:visible`.

## Do not

- Migrate `/full-guide/` or create `src/pages/full-guide.astro`. That is 15d.
- Touch the language toggle or the copy buttons. Those are 15c and 15b.
- Touch `src/content/config.ts`, `verify.mjs`, `tokens.css`, `responsive.css`.
- Reformat `app.js`. It is in `.prettierignore`; keep it that way.
- Substitute a near-miss design token for a legacy value. Mark it:
  `/* token-gap: <reason>; owner design-system-keeper */`. See
  `.agents/rules/gates.md`.

## Done when

- [ ] One island, driven by the collections task 05 filled
- [ ] All nine behaviours reachable through it, including the provider/effort
      coupling
- [ ] Keyboard and focus complete
- [ ] `pnpm run gate` green — the **full** gate, not just `verify` + `audit-ui`
- [ ] 42 assertions intact
- [ ] Report names the island's props so 15d can wire it without guessing
