# Task 16 — Review desk

**Agent**: `page-migrator` · **Model**: **Codex** — highest defect risk
**Depends on**: 06, 11, 13 · **Parallel with**: 15 **Worktree**:
`.agents/scripts/worktree.sh start 16 page-review-desk`

## Goal

`/skills-review/` → Astro. The most interactive page: search, filtering, lazy
file fetching, markdown rendering, a client-side diff view, six URL params, and
a live API call to `vote-service/`.

## The six query params are a public contract

`?author=`, `?skill=`, `?view=`, `?file=`, `?compare=`, `?render=` — documented
in the page footer and shared externally. All six must round-trip, and browser
back/forward must restore state (`syncUrl` / `selectFromUrl` today).

**Test every one manually.** A snapshot diff cannot catch a broken deep link.

## Islands

The catalog + detail pane is genuinely interactive: `client:load` is justified
here. The vote widget is `client:visible`. Everything else server-renders.

## vote-service integration

`window.SKILLS_REVIEW_VOTE_API = 'https://ai-for-dummies-vote.marcospaulo.dev.br'`
is set inline in `index.html` today. Either keep the global or move it to
`PUBLIC_VOTE_API` — **if you move it, update `vote-service/README.md` in the
same change**, since it documents the coupling.

`ALLOWED_ORIGIN` on the deployed service is
`https://netcracker.pages.marcospaulo.dev.br`. If the site's origin changes, the
vote API breaks with a CORS error. It does not change in this plan — but verify
after cutover.

## Asserted by verify.mjs

Interaction tokens: `from './catalog.js'`, `from './files.js'`, `renderList`,
`renderDetail`, `selectSkill`, `packageSummary`, `markdownHeadings`,
`markdownToc`, `loadSelectedFile`, `schedulePackageSearch`, `fetchSource`,
`packageSearchText`, `diffMarkup`, `diffRows`, `searchParams.set('compare')`,
`markdownMarkup`, `syncUrl`, `selectFromUrl`, `URLSearchParams`,
`navigator.clipboard`, `document.execCommand`.

Plus the catalog count: `id:'` occurrences across both catalogs **must equal
24**.

Same rule as task 15 — re-point with task 19, never delete.

## Watch for

The markdown renderer is hand-rolled (`markdownMarkup`, `markdownHeadings`,
`markdownToc`). Task 06 moves rendering to build time — but the **diff view
needs raw source text**, not rendered HTML. Keep both available.

## Done when

- [ ] All six query params round-trip; back/forward restores state
- [ ] Search, filter, file tabs, preview, change lens, download, copy all work
- [ ] Vote widget reaches the live API; CORS preflight succeeds
- [ ] Snapshot diff empty; screenshots match; checklist complete
- [ ] `pnpm run gate` green
