# UI Design Phase (Step 3)

## Step 3.0 — Mockup Source Decision (only when `mockups.status: none`)

When the state file shows `mockups.status: none` AND UI impact ≠ N/A, the user
has not provided any mockup at intake. Before falling into the **Full** pipeline,
offer them an explicit choice — generate internally or hand off to **Claude
Design** (Anthropic's design product, design-system-sync'd) — so they don't have
to abandon the PRD flow halfway.

Skip this step entirely when `mockups.status` ∈ {`provided`, `partial`,
`pending_external`} (already decided) or when UI impact = N/A (no design work).

### The question (single STOP)

Ask the user, verbatim:

> Non hai fornito mockup all'intake. Come preferisci produrli per questa feature?
> **(A) Internamente** — genero io 3 opzioni HTML (workflow `ui-design`), iteriamo qui.
> **(B) Handoff a Claude Design** — ti preparo un prompt context-rich da incollare in Claude Design; quando hai i mockup tornami con i path locali.

**STOP.** Wait for an explicit `A` / `B` answer. Silence or ambiguity ⇒ ask
again (do NOT default silently).

### Branch A — Internal generation

Set `mockups.source: internal` in the state file, then fall through to the
**Precondition decision tree** below — the `none` row routes to Full mode
unchanged. No other side-effect of Step 3.0.

### Branch B — Claude Design handoff

1. **Collect context from the state file + config + overlays:**
   - `feature_title`, `feature_objective_one_sentence` from `## Feature Description`.
   - `user_flow_summary`, `primary_personas` from `## Discovery Log` (dimensions 1 + 2).
   - `screens_in_scope[]` from `## UI Design` (derive from User Stories + ISA touchpoints if not yet populated — same logic as Hybrid mode § "Build the per-screen routing table").
   - `brand_voice` from `.baldart/overlays/prd.md` if present (skip section otherwise).
   - When `features.has_design_system: true`: read `${paths.design_system}/INDEX.md` (primitive list) + `${paths.design_system}/tokens-reference.md` (tokens excerpt). When `false`: omit § 5 of the prompt entirely.
   - `stack.framework`, `stack.charting`, `stack.animation` from `baldart.config.yml`.
   - `output_format_preference` derived from `stack.framework` (Next/Remix → React+Tailwind; vanilla → HTML/CSS; otherwise ask the user).
   - Numeric token tables (type scale, spacing, radius, contrast, motion durations) from `framework/agents/design-system-protocol.md` — cite verbatim.

2. **Render the prompt** by populating
   [../assets/claude-design-handoff-prompt.template.md](../assets/claude-design-handoff-prompt.template.md)
   with the collected context. Strip empty conditional sections.

3. **Present the rendered prompt** to the user inside a fenced markdown
   code-block, prefixed with this instruction (verbatim):

   > Incolla il prompt qui sotto in Claude Design. Quando hai i mockup pronti,
   > tornami con i **path locali** dei file (PNG, HTML, TSX, o link Figma) e
   > riprendo da qui — copio i file nella cartella del PRD, li analizzo come
   > al solito (Step 1.6 Mockup Intake), e Step 3 entra in Hybrid mode.

4. **Update state file** before stopping:
   - `mockups.status: pending_external`
   - `mockups.source: claude-design`
   - `mockups.handoff_prompt_path: ${paths.prd_dir}/<slug>/handoff/claude-design-prompt.md` (save the rendered prompt to disk so it survives session restarts).
   - `step_3_mode: pending_external_handoff` (transitional — overwritten when user returns).

5. **STOP.** Wait for the user to return with local paths.

### Re-entry from handoff (user returns with paths)

When the user comes back with one or more local paths after the handoff:

1. Invoke **Step 1.6 — Mockup Intake** verbatim
   ([SKILL.md](../SKILL.md) § Hard Rule 16,
   [discovery-phase.md](discovery-phase.md) § "Mockup analysis schema"):
   - Copy local files into `${paths.prd_dir}/<slug>/mockups/` with collision-safe
     rename.
   - Run the full Mockup analysis schema (screens, user_flow, components,
     states_visible, copy_excerpts, gaps, design_system_alignment).
   - Populate `## UI Design` in the state file.
2. Flip `mockups.status` from `pending_external` to `provided` or `partial`
   based on coverage of `screens_in_scope[]`. Keep `mockups.source: claude-design`
   as audit trail.
3. Fall through to the **Precondition decision tree** below — the `provided` /
   `partial` row routes to **Hybrid mode** unchanged.

---

## Precondition decision tree

Read `mockups.status` and `screens_in_scope[]` from the state file `## UI Design`
section (populated by Step 1.6 and/or Step 3.0 re-entry) plus the UI impact
dimension from the discovery checklist. Pick exactly one branch:

| Condition | Branch | Action |
|-----------|--------|--------|
| UI impact = N/A | **Skip totale** | Mark task 2 `completed` ("Skipped — no UI"). `step_3_mode: skipped`. Proceed to PRD Writing Phase. |
| `mockups.status: none` AND UI impact ≠ N/A | **3.0 Decision** | Run Step 3.0 above first. Branch A ⇒ fall through to **Full**; Branch B ⇒ STOP, then re-enter. |
| `mockups.status: pending_external` | **STOP** | User is in handoff with Claude Design. Do not advance until paths arrive (see § "Re-entry from handoff"). |
| `mockups.status: none` AND `mockups.source: internal` | **Full** | Run the standard pipeline 3a → 3b → 3c → 3d → 3e as below. `step_3_mode: full`. |
| `mockups.status` ∈ {`provided`, `partial`} AND UI impact ≠ N/A | **Hybrid** | Per-screen routing (see § Hybrid mode below). `step_3_mode: hybrid` (or `mockup-only` if every screen is covered AND no `mockup_analysis.design_system_alignment.violations` — distinct from `skipped`, which means no UI at all). |

Mark task 2 as `in_progress` for the Full and Hybrid branches.

## Delegation to `ui-design` Skill

This phase delegates ALL design work to the `ui-design` skill. Do NOT duplicate
the design workflow here — invoke the skill and follow its output.

### How to invoke

Use the `Skill` tool: `Skill(ui-design)`

The skill handles the full pipeline:

| PRD Step | ui-design Skill Step | What happens |
|----------|---------------------|--------------|
| 3a — UI Context Analysis | Steps A + B | Component discovery, context screenshots, codebase analysis |
| 3b — 3 Design Options | Steps C + D + E | Generate 3 HTML mockups, evaluate with separate `ui-expert` agent, open in Safari |
| 3c — Design Iteration | Step F | User feedback loop with re-evaluation |
| 3d — Save & Inventory | Step G | Save design.html, extract UI Element Inventory |
| 3e — Registry Coherence | **Step H** | Post-Intervention Coherence Check — BLOCKING when `features.has_design_system: true`; surfaces `DS_INDEX_DRIFT` / `DS_COMPONENT_STALE` / `DS_TOKENS_DRIFT` findings; must pass before task 2 is marked `completed` |

### What to pass to the skill

When invoking `ui-design`, ensure it has access to:

- **`WORKING_DIRECTORY`** — the absolute path of the PRD's docs worktree,
  read from the state file `## Worktree` section. Pass it explicitly in the
  args (e.g. `WORKING_DIRECTORY=<absolute-path-of-$WORKTREE_PATH>`) so the
  skill's Step G saves `design.html` inside the worktree, not into the
  consumer's main repo. Without this, the design lands on main, the `/prd`
  Step 7 stage list can't find it, and the merged PRD ships without its
  design asset. See `ui-design/SKILL.md` § "Working Directory" (added v3.22.0).
- Feature description (from state file `## Feature Description`)
- All discovery answers relevant to UI (from state file `## Discovery Log`)
- The `<slug>` for file naming
- Existing component matches (from registry lookup below)

### Component Registry Lookup (before design)

Before invoking `ui-design`:

1. Read `${paths.references_dir}/component-registry.md`.
2. When `features.has_design_system: true`, ALSO read `${paths.design_system}/INDEX.md` (component index + authority matrix) — required by HARD RULE 14 in `prd/SKILL.md` and by the BLOCKING cascade in `ui-design/SKILL.md`. Skipping this read is a protocol violation.
3. From feature description, identify UI elements likely needed (tables, forms, modals, etc.).
4. Match against both registries. Pass results to `ui-design`:

```
### Existing Components (from component-registry.md)
REUSE — already exist:
- [Component] — [path] — [notes]

MISSING — no match found:
- [element description]
```

This prevents designing new components when 450+ reusable ones already exist.

### Gates (Full branch)

- **After Step 3b:** all 3 polished options open in Safari AND screenshots shown inline.
  **STOP.** Wait for user to choose.
- **After Step 3c:** explicit user approval ("confermo", "va bene", "ok").
- **After Step 3d:** `${paths.prd_dir}/<slug>/design.html` exists (inside `$WORKTREE_PATH`) AND `## UI Element Inventory`
  is populated in the state file with at least 3 elements, AND every interactive
  or iconographic row carries a `function_ref` (an US/AC/endpoint or `decorative`) —
  no unresolved `orphan` (Functional Traceability Gate, see
  `framework/agents/design-system-protocol.md`).
- **After Step 3e (BLOCKING when `features.has_design_system: true`):** ui-design Step H
  (Post-Intervention Coherence Check) must complete with no unresolved `DS_INDEX_DRIFT` /
  `DS_COMPONENT_STALE` / `DS_TOKENS_DRIFT` findings (or every finding explicitly waived
  by the user and logged as such). Do NOT mark task 2 `completed` until this gate clears.

Mark task 2 as `completed`. Update state file status to `specs-confirmed`.

---

## Hybrid mode (Step 1.6 produced mockups)

Triggered when `mockups.status` ∈ {`provided`, `partial`}. The principle: the user
has already done the design work for some screens — don't redo it. Validate, align
to the design system, approve, and inventory. Generate ONLY the screens not covered.

### Build the per-screen routing table

1. Construct `screens_in_scope[]` by merging:
   - User Stories from the state file (one row per US that implies a screen).
   - ISA `NAVIGATION` and `DASHBOARD` touchpoints that imply landing screens.
   - The `mockup_analysis.screens[]` list itself (in case the mockups include
     screens the user stories didn't make explicit).
2. For each entry, set `covered_by_mockups: true`. Match **exact-join-first**:
   - **Exact join (Claude-Design bundles).** When the entry has a `slot_id` /
     `screen_id` (populated by Step 1.6.4b structural resolution, see
     [discovery-phase.md](discovery-phase.md) § "Step b2") AND a
     `mockup_analysis.screens[]` entry shares the same `slot_id`/`screen_id`, join
     them **deterministically** — no ambiguity, no STOP. This is the common case
     for handoff bundles and skips the fuzzy guesswork entirely.
   - **Fuzzy fallback.** Only when `slot_id` is absent (chat images, single files,
     `generic` bundles) fall back to case-insensitive substring match on
     `mockup_analysis.screens[].name` — and keep the STOP-and-ask on ambiguity:
     "questa schermata `<X>` corrisponde al mockup `<Y>`?".
3. Persist `screens_in_scope[]` (with `slot_id`/`screen_id`/`canvas_ref` when
   present) in the state file `## UI Design` section.

### Per-screen execution

| `covered_by_mockups` | What runs | Notes |
|----------------------|-----------|-------|
| `true` | Component Registry Lookup (adapted) + 3c (approval gate) + 3d (save & inventory) | SKIP 3a (options) and 3b (generation). Use the provided mockup as the canonical reference. |
| `false` | Subskill `ui-design` 3a + 3b SCOPED TO THIS SCREEN, then 3c + 3d | Pass the screen name + relevant discovery answers + context-primer output. Mockups for OTHER screens already approved are passed as "existing design language to match" so the new screen looks consistent. |

### Step 3 sub-flow in Hybrid mode

1. **Component Registry Lookup (all screens, before any generation).**
   For each screen — covered OR new — match `mockup_analysis.screens[].components[]`
   (covered) or the inferred component list (new) against
   `${paths.references_dir}/component-registry.md` and, when
   `features.has_design_system: true`, against `${paths.design_system}/INDEX.md`.
   - For covered screens: any component used in a mockup but NOT in the registry
     becomes either (a) a REUSE recommendation if a close match exists, or (b) a
     candidate new primitive — flag in `mockup_analysis.design_system_alignment`.
   - For new screens: passed to `ui-design` as the REUSE list (prevents the
     subskill from inventing new components).
2. **Generation pass (new screens only).**
   For each new screen, invoke `Skill(ui-design)` with payload:
   ```
   feature_slug: <slug>
   scope: single-screen
   screen_name: <X>
   discovery_excerpt: <answers relevant to this screen>
   reuse_components: <list from registry lookup>
   design_language_anchor: mockups/<existing-mockup-1>.png, mockups/<existing-mockup-2>.png
   ```
   The subskill produces 3 options for that one screen (its standard 3b output).
   Open in Safari, **STOP**, wait for user to choose.
3. **Approval gate (all screens, 3c).**
   Present in ONE message:
   - Thumbnail + path for each `covered_by_mockups: true` screen.
   - The user-chosen option for each `generated_in_step_3b: true` screen.
   - All `design_system_alignment.violations[]` consolidated into a "Issues da
     risolvere prima di Step 4" list with the proposed token/component swap.
   Ask explicit confirmation ("confermo", "va bene", "ok"). **STOP.**
4. **Save & Inventory (all screens, 3d).**
   - For covered screens: copy `mockups/<file>.png` references into the unified
     UI Element Inventory; no `design.html` regeneration needed unless the subskill
     also ran. If at least one new screen was generated, the merged `design.html`
     includes both the new mockups and embeds/links to the existing ones.
   - For all-covered runs (`generated_in_step_3b: false` everywhere): set
     `design.html_path: n/a (mockup-only)` and `step_3_mode: mockup-only` in the state file;
     the PRD will link directly to `${paths.prd_dir}/<slug>/mockups/`.
   - Update `## UI Element Inventory` in the state file with at least one row per
     covered screen + per generated screen.

### Hybrid gates

- **After per-screen 3b (only if any new screen exists):** all 3 polished options
  per new screen open in Safari AND screenshots shown inline. **STOP.** Wait for
  user choice.
- **After 3c (approval):** explicit user approval AND every entry in
  `design_system_alignment.violations[]` is either resolved (token/component
  swapped in the inventory) or explicitly waived by the user (logged as such).
- **After 3d:** UI Element Inventory populated with at least one row per screen in
  `screens_in_scope[]`, every interactive/iconographic row carrying a `function_ref`
  (no unresolved `orphan` — Functional Traceability Gate).
  `design.html_path` set OR `mockup-only` flag recorded.
- **After 3e — Registry Coherence (BLOCKING when `features.has_design_system: true`):**
  ui-design Step H (Post-Intervention Coherence Check) must complete with no
  unresolved `DS_INDEX_DRIFT` / `DS_COMPONENT_STALE` / `DS_TOKENS_DRIFT` findings
  (or every finding explicitly waived by the user and logged as such). Applies to
  all Hybrid sub-modes (`hybrid`, `mockup-only`). Do NOT mark task 2 `completed`
  until this gate clears.

Mark task 2 as `completed`. Update state file status to `specs-confirmed`.

### Canonical example

Feature with 5 screens (`Lista ordini`, `Dettaglio ordine`, `Filtri`, `Onboarding`,
`Stato pagamento`). Mockups provided for 3 of them.

```
screens_in_scope:
  - { name: "Lista ordini",      covered_by_mockups: true,  generated_in_step_3b: false }
  - { name: "Dettaglio ordine",  covered_by_mockups: true,  generated_in_step_3b: false }
  - { name: "Filtri",            covered_by_mockups: true,  generated_in_step_3b: false }
  - { name: "Onboarding",        covered_by_mockups: false, generated_in_step_3b: true  }
  - { name: "Stato pagamento",   covered_by_mockups: false, generated_in_step_3b: true  }
```

Result: `ui-design` subskill runs TWICE (one invocation per new screen, each
producing 3 options). The 3 covered screens skip 3a/3b entirely. The final approval
gate presents all 5 screens together. `step_3_mode: hybrid`. The PRD links to
`mockups/` for the 3 covered + `design.html` for the 2 new.
