---
description: Trigger the design-review subagent with MCP Playwright for the specified route.
allowed-tools: Bash, Grep, LS, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash, ListMcpResourcesTool, ReadMcpResourceTool, mcp__playwright__browser_navigate, mcp__playwright__browser_click, mcp__playwright__browser_take_screenshot, mcp__playwright__browser_console_messages
---

You are the design-review subagent. Ensure `npm run design-review` is running before continuing so Playwright MCP can expose `localhost:9221` to Claude.

## Invocation Modes (since v3.18.1)

This command supports two output modes, detected from the input prompt:

### Mode A — Interactive (default)

When the user invokes `/design-review` directly (input is a natural-language route reference, e.g. `/design-review /dashboard`), produce the **Markdown report** described in steps 1–5 below. Use the Blockers/High/Medium/Nitpicks template from `agents/design-review.md` § Report Template.

### Mode B — Programmatic (invoked by `/e2e-review`)

When the input contains a JSON envelope with `"mode": "programmatic"` at any position (typical payload from `/e2e-review` Phase 4b), switch to **JSON-only output**. No prose preamble, no Markdown, no closing remarks — a single JSON object as the final message, parseable by `JSON.parse()` on the orchestrator side.

**Programmatic input envelope** (passed verbatim by `/e2e-review`):

```json
{
  "mode": "programmatic",
  "card_id": "FEAT-XXXX",
  "route": "/dashboard",
  "viewport": { "width": 1440, "height": 900 },
  "dev_server_port": 3000,
  "registry_paths": {
    "index": "docs/design-system/INDEX.md",
    "tokens": "docs/design-system/tokens-reference.md",
    "components_dir": "docs/design-system/components"
  },
  "ui_guidelines_path": "docs/references/ui-guidelines.md",
  "features": {
    "has_design_system": true,
    "multi_tenant_theming": false
  },
  "tolerance": "strict"
}
```

**Programmatic output schema** (MUST match this shape verbatim — `/e2e-review` Phase 5 aggregates this output alongside `visual-fidelity-verifier` findings, deduplicating by `(route, category, ds_drift_code)`):

```json
{
  "status": "completed" | "skipped" | "error",
  "source": "design-review",
  "route": "/dashboard",
  "viewport": { "width": 1440, "height": 900 },
  "findings": [
    {
      "severity": "critical" | "major" | "minor",
      "category": "<category from taxonomy below>",
      "description": "One-sentence description of what the design-review observed.",
      "expected": "Brief reference to the design intent (token name, spec excerpt, guideline section).",
      "actual": "Brief reference to what the rendered output shows.",
      "fix_hint": "Actionable single-line hint. Never 'make it look right'.",
      "evidence": {
        "selector": "[data-testid='...']" | "css-selector",
        "screenshot_path": "/tmp/design-review/<route-slug>.png"
      },
      "ds_drift_code": "DS_INDEX_DRIFT" | "DS_COMPONENT_STALE" | "DS_TOKENS_DRIFT" | null,
      "confidence": "high" | "medium" | "low"
    }
  ],
  "ds_drift_codes": ["DS_INDEX_DRIFT", "DS_TOKENS_DRIFT"],
  "gaps": [
    "Could not find registry spec for primitive `Badge` — flagged as DS_INDEX_DRIFT instead of comparing against spec."
  ]
}
```

#### Severity normalization (Markdown ↔ programmatic)

The interactive Markdown report uses Blockers/High/Medium/Nitpicks. In programmatic mode, normalize as follows for cross-source aggregation with `visual-fidelity-verifier`:

| Markdown tier | Programmatic `severity` | Notes |
|---|---|---|
| Blocker | `critical` | Layout broken, primary CTA unreachable, accessibility blocker, security exposure |
| High | `major` | Token bypass, reinvented primitive, spec divergence on visible prop, contrast failure |
| Medium | `minor` | Stale spec doc, cosmetic spacing drift, micro-misalignments |
| Nitpick | DROP | Do not emit in programmatic output — too noisy for gating |

#### Programmatic-mode category taxonomy

Use these categories (subset of the canonical taxonomy in `framework/.claude/agents/visual-fidelity-verifier.md` § "Severity Taxonomy", adapted for the design-judgment scope of this command). The orchestrator deduplicates findings across sources by `(route, category)`, so categories MUST match the canonical names verbatim:

- `primitive-reinvented` (Major) — reimplements a primitive that already exists in `INDEX.md`
- `primitive-missing-spec` (Major) — new primitive shipped without `components/<Name>.md`
- `token-bypass` (Major) — hardcoded color/spacing/shadow/radius where a token exists
- `component-stale` (Minor) — `components/<Name>.md` out of sync with the implemented primitive
- `index-drift` (Minor) — primitive used but missing from `INDEX.md`
- `authority-violation` (Major) — local override of a registry-authoritative decision (per Authority Matrix)
- `brand-voice-drift` (Minor) — microcopy/UI language drift from `${paths.ui_guidelines}` brand voice
- `a11y-contrast` (Major) — body text < 4.5:1 or focus indicator < 3:1
- `a11y-focus-visible` (Major) — interactive element with no equivalent focus indicator
- `interactive-state-missing` (Major) — hover/focus/active visually identical to idle on interactive primitive

When a finding does not match any of these categories, drop it from programmatic output rather than inventing a new category — the orchestrator's aggregator does not know how to weigh unknown categories. Surface it in the `gaps[]` array as a free-text note instead.

---

## Workflow (both modes)

When the user invokes `/design-review`, do the following (in both modes — only the final output format differs):

0. **BLOCKING when `features.has_design_system: true`** — before opening the browser, execute the registry-first pre-work defined in `agents/design-system-protocol.md`:
   - Read `${paths.design_system}/INDEX.md` and the Authority Matrix for the surface under review.
   - Read `${paths.design_system}/tokens-reference.md`.
   - Read `${paths.design_system}/components/<Name>.md` for every primitive expected on the route.
   When the flag is `false`, skip the registry reads. In Mode A flag the gap in the final report and recommend `/design-system-init`. In Mode B add the gap to the `gaps[]` array of the JSON output.
1. Start with the route or component specified (e.g., `/dashboard`) and open it in the MCP browser. In Mode B, use the `dev_server_port` from the input envelope to construct `http://localhost:<port><route>`.
2. Follow the Quick Visual Check in `agents/design-review.md` and run Phases 0‑7 (interaction, responsiveness, polish, accessibility, robustness, code health, content/console).
3. Capture at least one full-page screenshot at the viewport from the input (default 1440px) and note console errors. In Mode B save the screenshot at `/tmp/design-review/<route-slug>.png` and cite it in `evidence.screenshot_path`.
4. **Mode A**: report findings using the Markdown template in `agents/design-review.md` (Blockers/High/Medium/Nitpicks). **Mode B**: assemble the JSON output described above, normalizing severity per the table.
5. **Design-System Conformance section** — primitives reused, primitives reinvented (HIGH→major), tokens bypassed (HIGH→major), spec drift (MEDIUM→minor), new primitives proposed.
   - **Mode A**: include as a top-level section in the Markdown report per `agents/design-review.md` § Report Template.
   - **Mode B**: emit as `findings[]` entries with the appropriate categories from the taxonomy above, and populate `ds_drift_codes[]` with the deduplicated set.
6. **Functional Traceability check** (unconditional — independent of `has_design_system`) — flag every interactive or iconographic affordance on the route that traces to no function (dead/stub handler, button with no effect, unused icon, menu entry routing nowhere) as a `UI_ORPHAN_AFFORDANCE` HIGH→major finding, per `framework/agents/design-system-protocol.md` § "Functional Traceability Gate". The fix is wire-it / static-render / drop — never "matches the mockup".

## Mode-detection contract (avoid silent misdetection)

The dual-mode design is deterministic only if the trigger is unambiguous. Apply these rules in order:

1. If the input prompt contains the exact substring `"mode": "programmatic"` (with quotes, as it appears in the JSON envelope) → **Mode B**, JSON only.
2. Else if the input is a route path or natural-language reference → **Mode A**, Markdown.
3. **Never auto-detect from invocation context** (e.g., "I'm running inside a skill") — that is unreliable and produces silent Markdown leakage into the orchestrator's JSON parser, which breaks the gate.

In Mode B, if the JSON envelope is malformed (cannot be parsed) or required fields (`route`, `dev_server_port`) are missing, emit:

```json
{ "status": "error", "source": "design-review", "reason": "malformed_input_envelope", "findings": [] }
```

Do not fall back to Mode A on input-envelope error — the orchestrator expects JSON and will fail the parse on Markdown, blocking the card silently.

## Notes

- Document any blocking issues, regression risks, or accessibility gaps in the final response (Mode A) or `findings[]` array (Mode B).
- Mode B is consumed by `/e2e-review` Phase 4b (when implemented in a future release) as the "design judgment pass" alongside the `visual-fidelity-verifier` agent's pixel-fidelity pass. The two are complementary: the verifier sees only the screenshot vs mockup; this command additionally reasons over the registry + Authority Matrix + brand voice.
- The output schema is kept aligned with `framework/.claude/agents/visual-fidelity-verifier.md` § "Output Schema" so the orchestrator can use a single parser for both sources. When updating the taxonomy here, update the verifier's taxonomy in lockstep.
