---
name: Validate - ui-polish + verify wiring
description: Enforce theme tokens via the ui-polish CLI, verify the scaffold-doc outputs + applied wiring, then typecheck
next_step: null
---

## STEP 03: Validate — ui-polish + verify integration

`scaffold-doc` (step-02) already wrote the i18n files, upserted
`docs-manifest.json`, and handed you the wiring plan you applied. This step
**enforces colours deterministically** and **verifies** the result.

### 0. Structure gate (blocking)

The doc must lead with a factual **objective** and carry **no** value-proposition
pitch (SKILL.md rule #12). Re-run `extract-doc` on the module and assert the scan
of the **now-generated** artifacts is both clean and complete:

```bash
npx --prefer-offline tsx skills/documentation/cli/extract-doc/index.ts \
  --spec '{"type":"<type>","target":"<module>","application":"<app>","projectPath":"<project-root>"}'
```

Two mirror conditions, both hard gates:

- **`report.existingDoc.forbiddenSections` MUST be empty.** If non-empty, the doc
  is **not** done: for each `{ file, line, token }`, remove the offending section
  (problème/solution, Bénéfices, Avant/Après — see step-02 "Correcting an existing
  doc"), then re-run until it returns 0.
- **`report.existingDoc.missingRequired` MUST be empty.** It can list:
  - `"objective"` — the mandatory Section 1 is absent: add the **Objectif** section
    first (i18n `sections.objective` + `objective`, or `overview.objective` for
    DocRenderer; rendered via `t('objective')`) — the header subtitle does NOT count.
  - `"summary"` (user) — the header accent tagline is absent: add the `summary` key
    and render it between title and subtitle (`text-xl … text-[var(--color-accent-600)]`),
    with the inline stats line.

  Fix each and re-run until it returns 0.

**NON-blocking companions** (advice only — the gate NEVER requires them, and it
never requires REMOVING an existing Référence technique on a re-run without
`--tech`):

- `report.existingDoc.adviseRolesTable` — the doc still lacks the Section 2
  « Accès & rôles » table while a roles source exists: add it now if you
  skipped it, or surface the advice in your final report.
- `report.existingDoc.overflowRisks[]` — `<code>`/`font-mono` elements without
  a break utility: fix (`break-all`/`break-words`) or surface.

This reuses the exact same deterministic detection as step-01 — no separate rules.

### 1. Colour / theme compliance → run ui-polish

Theme-token compliance is NOT checked by hand here — the ui-polish CLI does it.
Run it on the generated page (apply mode, regeneration disabled so the page is
never rebuilt — only colour utilities are rewritten):

```bash
npx --prefer-offline tsx skills/development/frontend/ui-polish/cli/ui-polish/index.ts \
  --project-path "<web-root>" --mode apply --regenerate-threshold 0 \
  --page "src/pages/docs/business/<group>/<app>/<module>/index.tsx" \
  --rules R2,R3,R7,R12,R13,R14,R15,R16,R17
```

Then re-run in `--mode audit` and confirm **0 errors**. Finish the residual
neutral cases the CLI flags (see the `/ui-components` skill). Doc-specific intent
the CLI can't infer — verify by eye: dashboard chart series use `--dataviz-*`
(not accent), highlight blocks use the discreet accent pattern.

### 2. Verify the scaffold-doc outputs (bimodal — `data.mode` from step-02)

**Both modes:**
- [ ] 4 i18n files at `src/i18n/locales/{fr,en,de,it}/docs-{kebab}.json` — `user` FLAT (no root key); `developer/database/testing` nested under the namespace key (source only)
- [ ] `docs-manifest.json` has the entry (correct `id`, `path`, `i18nNamespace`, `languages`)

**`source` monorepo — wiring applied** (each plan item's `idempotencyMarker` now present):
- [ ] `i18n/config.ts` — namespace in all 4 spots (destructured var, `import()`, return object, `ns: [ … ]`)
- [ ] `components/routing/DocRoutes.tsx` — lazy import + `<Route>` child under the `/docs/business` group
- [ ] `contexts/DocPanelContext.tsx` — `docMapping` keys (+ `appDocMapping` if default)
- [ ] `pages/system/docs/user/UserIndexPage.tsx` — module entry (user type) + its `user.modules.<x>.name/.description` i18n keys

**`client` project:**
- [ ] `src/extensions/<kebab>Registry.ts` exists and registers `PageRegistry.register('docs.<app>.<module>', …)`
- [ ] The `aggregate` wiring instruction was EXECUTED: `componentRegistry.generated.ts` imports `'./<kebab>Registry'` AND `moduleResources.generated.ts` registers the `'<kebab>'` namespace (all 4 locales)
- [ ] The page calls `useTranslation('<kebab>')` (the kebab namespace — NOT camelCase)
- [ ] **NO file under `node_modules/` was edited** (the DocPanel mapping is package-owned — the `panel-limitation` info instruction is reported to the user, not worked around)

### 3. Quality checks (user type — the Mock UI is yours)

- [ ] Mock UI faithfully reproduces the real page (not a generic KPI/table)
- [ ] Each chart matches the real recharts TYPE (from `extract-doc` `charts[]`)
- [ ] Expressive mock data: ratio max/min(>0) ≥ 5x, peak ≥ 90% + valley ≤ 15%, height ≥ `h-24`/`h-20`
- [ ] Every Mock UI block has an `Annotation`, inside the same padded container
- [ ] No DocPanel overflow: `overflow-hidden` on chart cards, `min-w-0` on flex children, no fixed px widths
- [ ] Root container `<div className="space-y-8 max-w-5xl mx-auto pb-12">` (no horizontal padding)
- [ ] Section 1 is **Objectif** (description of what the module does) — no problème/solution, Bénéfices or Avant-Après (enforced by the §0 gate)
- [ ] Section 2 is **« Accès & rôles »**: the Rôle | Peut faire | Portée table from `report.accessRoles.rows` only (when `source !== 'none'`; plain access card otherwise), `access.unverified` caveat when `source === 'ba'`, unmapped permissions surfaced, join warnings reported to the user
- [ ] Référence technique appendix present **only if `--tech`** (and an existing one was NOT removed by a re-run without the flag); header stats (`businessRules` + `apiEndpoints`) present in both cases

### 4. Typecheck

At the web root: `npm run typecheck` (or `tsc --noEmit`) — phantom imports or a
malformed wiring edit fail here. Fix before finishing.

### Artifact reference (verified against the app)

| Artifact | `source` monorepo | `client` project |
|----------|-------------------|------------------|
| User doc page | `src/pages/docs/business/{platform\|personal}/{app}/{module}/index.tsx` (route omits the `platform/personal` segment) | `src/pages/docs/business/{app}/{module}/index.tsx` (folder mirrors the route) |
| i18n (central) | `src/i18n/locales/{lang}/docs-{kebab}.json` (FLAT for user; nested under the namespace for DocRenderer) | same path, always FLAT; **namespace = the kebab file name** |
| i18n registration | `src/i18n/config.ts` | `src/extensions/moduleResources.generated.ts` (via aggregate-component-registry) |
| Routing | `src/components/routing/DocRoutes.tsx` (NOT `DynamicRouter.tsx` — it only calls `{DocRoutes()}`) | `src/extensions/docs-{kebab-path}Registry.ts` → package `buildExtensionDocRoutes()` (`docs.*` PageRegistry keys) |
| Manifest | `src/pages/docs/docs-manifest.json` | same (created by scaffold-doc if missing) |
| Contextual panel | `src/contexts/DocPanelContext.tsx` | package-owned — NOT editable client-side (`panel-limitation` info) |
| User index | `src/pages/system/docs/user/UserIndexPage.tsx` | package-owned — no client entry |

### Completion

Documentation is generated, themed (ui-polish), wired, internationalised (4 langs)
and listed in the manifest. Route renders inside `DocsLayout`.

- `source`: the DocPanel edge button opens it for the module's routes.
- `client`: the page is reachable at `/docs/business/{app}/{module}` (direct URL
  + "open in new tab" from the panel). The contextual edge button targets the
  doc only once the installed `@atlashub/smartstack` derives its mapping from
  the `docs.*` PageRegistry keys — until then it falls back to the user-docs
  root: report the `panel-limitation` instruction to the user verbatim.
