- remove legacy .ai/ knowledge shots and reports, semantic skills invariant assessment, and obsolete .kilo/workflows/ - update agent model selection (omniroute/terra) for qa-tester, security-auditor, svelte-coder - add swarm-master agent and speckit openapi/prototype/resume/validate plus test.* commands; update speckit plan/ux/implement docs - refresh skill SKILL.md files (semantics core/testing/svelte/belief, molecular-cot-logging, semantic-frontend) - add semantic curation report
18 KiB
description, handoffs
| description | handoffs | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Generate a feature-local interactive HTML prototype from UX contracts, producing specs/<feature>/prototype/index.html plus a prototype manifest and state-coverage report. No production source mutation. |
|
User Input
$ARGUMENTS
You MUST consider the user input before proceeding (if not empty).
Applicability
This command is applicable ONLY when the feature has a UI surface. For backend-only features, skip gracefully with: "No UI surface detected — prototype not applicable. Proceed to /speckit.openapi or /speckit.plan."
Decision gate: If FEATURE_DIR/contracts/ux/ exists (from /speckit.ux), generate the full prototype. If only ux_reference.md exists, generate a lightweight prototype from the reference. If neither exists, skip.
Principle
You are generating a read-only, interactive HTML artifact that validates UX contract states against actual browser behavior. The prototype is a design verification tool, not production code. It proves that every declared @UX_STATE can be reached, that @UX_FEEDBACK mechanisms work, and that @UX_RECOVERY paths are traversable — all without touching frontend/src/.
Design fidelity is mandatory, not optional: the prototype MUST visually match the application's real design system. It is built by copying the exact utility classes and design tokens from the production Svelte components, not by inventing a parallel "prototype style". A prototype that looks different from the app fails its purpose — reviewers cannot judge states they will never see in production. If you find yourself writing a custom hex color, custom radius, or custom shadow that is not in frontend/tailwind.config.js, you are doing it wrong.
Outline
Phase 0: Pre-Flight
- Setup: Run
.specify/scripts/bash/check-prerequisites.sh --json --paths-onlyfrom repo root. ParseFEATURE_DIR. - Verify applicability: Check for
FEATURE_DIR/contracts/ux/orFEATURE_DIR/ux_reference.md. If neither exists and no UI surface is indicated, report skip and exit. - Load context:
FEATURE_DIR/spec.md— user stories and acceptance criteriaFEATURE_DIR/ux_reference.md— interaction referenceFEATURE_DIR/contracts/ux/screen-models.md— model inventory (if exists)FEATURE_DIR/contracts/ux/api-ux.md— API shapes for realistic mock data (if exists)FEATURE_DIR/contracts/ux/<screen>-ux.md— per-screen UX contracts (if exists).opencode/skills/semantics-svelte/SKILL.md— §VI canonical FSM template, §VII design tokensfrontend/tailwind.config.js— design token SSOT: semantic color palette (primary/secondary/destructive/success/warning/info/ghost/surface/border/text), typography, spacing, radiusfrontend/src/app.css— global styles and motion preferencesfrontend/src/lib/ui/— existing design-system atom inventory (Button, Card, Input, Select, Badge, PageHeader, Skeleton, EmptyState, Pagination, etc.)frontend/src/lib/components/— existing composite widget inventoryfrontend/src/lib/ui/index.ts— component export index- Every
.sveltecomponent the prototype will use — read the full source to copy its exact class strings
Phase 0.5: Design System Alignment (MANDATORY — before any HTML)
Extract the design system truth from production sources. This phase produces a working set of tokens and class recipes that the prototype MUST use verbatim.
Step 1 — Extract design tokens from frontend/tailwind.config.js:
- Semantic palette:
primary.*,secondary.*,destructive.*,success.*,warning.*,info.*,ghost.*,surface.*,border.*,text.*,brand.*,terminal.*(if applicable) - Record hex values exactly: e.g.
primary.DEFAULT = #2563eb,primary.hover = #1d4ed8,surface.page = #f8fafc,text.muted = #64748b - Record widths (sidebar 240px), font families (JetBrains Mono for terminal)
Step 2 — Extract component class recipes from frontend/src/lib/ui/*.svelte:
- Read the full source of each component the prototype uses (Button, Card, Badge, PageHeader, Input, Select, Skeleton, EmptyState, Pagination, ConfirmDialog, Toast if used)
- Copy the exact
classstrings from the Svelte template, e.g.:Buttonbase:inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 rounded-mdButtonprimary:bg-primary text-white hover:bg-primary-hover focus-visible:ring-primary-ringButtonsizes:sm: h-8 px-3 text-xs,md: h-10 px-4 py-2 text-sm,lg: h-12 px-6 text-baseCard:rounded-lg border border-border bg-surface-card text-text shadow-sm, paddingp-6(md)Badgevariants:bg-success-light text-success,bg-warning-light text-warning,bg-destructive-light text-destructive,bg-info-light text-info,bg-primary-light text-primary,bg-surface-muted text-text-muted; shaperounded-full text-xs font-mediumPageHeader:flex items-center justify-between mb-8, titletext-3xl font-bold tracking-tight text-textEmptyState: read source, copy its structure and classesSkeleton:animate-pulse+ muted surface classes
- If the app uses dark mode / terminal palette (log viewer, task drawer): replicate
terminal.bg/terminal.surface/terminal.borderwhere the feature touches those surfaces
Step 3 — Build the prototype stylesheet as a Tailwind-utility shim:
- The prototype is a single self-contained HTML file (no build step). Inline the Tailwind utility classes the app actually uses as a minimal CSS shim: for every class string copied in Step 2, write the CSS rule that implements it (e.g.
.bg-primary { background-color: #2563eb; },.hover\:bg-primary-hover:hover { background-color: #1d4ed8; }). - Color values MUST come only from
tailwind.config.js. No invented hex codes. If a color is needed that is not a token, use the nearest semantic token. - Keep the shim scoped and complete: every class used in the HTML body MUST have a definition in the
<style>block.
Phase 1: Extract Representational States
From the loaded UX contracts and reference docs, build the representative state inventory:
For each screen identified in the feature:
- Mandatory states (from UX contracts or inferred):
idle— before any user actionloading— during async operationloaded— data visible, readyempty— no data (first use or filtered)error— failure state with recovery
- Story-specific states (from per-screen UX contracts):
- Every distinct
@UX_STATEdeclared in contracts - Every
@UX_FEEDBACKmechanism (toast, inline error, modal) - Every
@UX_RECOVERYpath (retry, cancel, navigate away)
- Every distinct
- Edge states (from Phase 2 of
/speckit.ux):- Stale data with refresh indicator
- Partial data (some loaded, some failed)
- Background update notification
- Rate-limited with countdown
- Network offline with reconnection
State coverage requirement: Every @UX_STATE declared in UX contracts MUST be represented. Every declared @UX_RECOVERY path MUST be reachable from its error state. Output a state coverage table in the manifest showing contract → prototype mapping.
Phase 2: Build Static Prototype
Create specs/<feature>/prototype/index.html:
Mandatory structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[Feature] — Interactive Prototype</title>
<style>
/* Embedded styles — no external deps */
/* Use Tailwind-like utility classes matching design tokens */
/* Responsive: mobile-first with breakpoints at 640px, 768px, 1024px */
</style>
</head>
<body>
<!-- State Switcher (top bar, always visible) -->
<nav class="prototype-state-switcher">...</nav>
<!-- Screen content — one <section> per screen -->
<main>
<section id="screen-1" class="prototype-screen">...</section>
</main>
<script>
// Inline JavaScript for state switching
// No frameworks, no build step, no external deps
// All states toggleable via the state switcher
</script>
</body>
</html>
Rules:
- Single file:
index.htmlis self-contained. All CSS and JS are inline. No external dependencies by default. - USE THE REAL CLASS RECIPES — verbatim: Every interactive element, container, and label in the prototype MUST carry the exact same Tailwind class strings as the production component it represents (from Phase 0.5 Step 2). Do NOT simplify, rename, or "clean up" production classes. Examples:
- Buttons:
class="inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 rounded-md bg-primary text-white hover:bg-primary-hover focus-visible:ring-primary-ring h-10 px-4 py-2 text-sm" - Cards:
class="rounded-lg border border-border bg-surface-card text-text shadow-sm p-6" - Badges:
class="inline-flex items-center gap-1.5"wrapper +class="rounded-full px-2.5 py-1 text-xs font-medium bg-success-light text-success" - PageHeader:
class="flex items-center justify-between mb-8"+class="text-3xl font-bold tracking-tight text-text"
- Buttons:
- Tokens from
tailwind.config.jsonly: The CSS shim's color/radius/shadow/spacing values MUST be the exact hex/px fromfrontend/tailwind.config.js. Zero invented values. If you cannot find a token for a needed style, use the nearest semantic token or note it in the manifest as a design gap. - Match component behavior: Disabled buttons get
disabled:opacity-50+disabled:pointer-events-none; loading buttons show the spinner SVG withanimate-spin; skeletons useanimate-pulse; badges use the semantic variant pair (bg-*-light text-*). - No production source mutation: The prototype lives in
specs/<feature>/prototype/. It NEVER writes tofrontend/src/. - Accessibility: All interactive elements MUST have: appropriate ARIA roles,
aria-liveregions for dynamic content, keyboard navigation (Tab/Enter/Space), focus management (matchfocus-visible:ring-2classes), minimum 44×44px touch targets on mobile, andalttext for images/icons. - Responsive: Match the app's actual breakpoints (mobile-first; Tailwind sm 640px / md 768px / lg 1024px). Test on both viewports via the state switcher's viewport toggle.
- State switcher: A fixed toolbar at the top of the prototype that allows:
- Switching between screens (if multiple)
- Toggling between states for each screen
- Toggling viewport size (desktop 1280px / mobile 375px)
- Shows CURRENT state name, can trigger transitions (loading → loaded, loaded → error, etc.)
- The switcher itself is a prototype chrome, not app UI — it may use plain styling, but every element INSIDE the screen sections must use production classes
- Realistic mock data: Use data shapes from
api-ux.mdto populate loaded states with plausible content. Empty states show realistic empty-state components. Error states show realistic error messages.
Phase 3: Generate Prototype Manifest
Create specs/<feature>/prototype/manifest.md:
#region Std.Opencode.PrototypeManifest [C:3] [TYPE ADR] [SEMANTICS prototype,manifest,[DOMAIN]]
@defgroup Prototype Interactive HTML prototype manifest for [FEATURE].
## Prototype Metadata
- **Feature**: [feature name]
- **Source contracts**: contracts/ux/
- **Screens represented**: N
- **Total states**: N
- **Accessibility validations**: keyboard nav, ARIA roles, touch targets, focus management
- **Responsive breakpoints**: 375px (mobile), 1280px (desktop)
## State Coverage
| Screen | @UX_STATE Contract | Prototype State | Reachable? | Recovery Path |
|--------|-------------------|-----------------|------------|---------------|
| Dashboard | idle | idle (default) | ✅ | — |
| Dashboard | loading | loading (3s auto) | ✅ | — |
| Dashboard | loaded | loaded (with mock data) | ✅ | — |
| Dashboard | empty | empty (no data mock) | ✅ | — |
| Dashboard | error | error (network fail) | ✅ | retry button → loading |
| Dashboard | stale | stale (cached + indicator) | ✅ | refresh button |
## Screen ↔ Story Traceability
| Prototype Screen | User Story | UX Contract | Acceptance Criteria Verified |
|-----------------|------------|-------------|------------------------------|
| /dashboard | US1: View Dashboards | DashboardUx | AC1: list loads, AC2: empty state |
| /migration | US2: Migrate Items | MigrationUx | AC1: step wizard, AC2: error recovery |
## Validation Results
- [ ] All @UX_STATE contracts reachable via state switcher
- [ ] All @UX_RECOVERY paths traversable
- [ ] Keyboard navigation: Tab order verified
- [ ] Touch targets: ≥44×44px on mobile viewport
- [ ] ARIA: live regions for loading/error states
- [ ] No broken links or dead-end states
- [ ] Responsive layout: mobile viewport does not overflow
## Design System Reuse
| Element | Source | Prototype Mapping |
|---------|--------|-------------------|
| Button | $lib/ui/Button.svelte | Same class string: `bg-primary text-white hover:bg-primary-hover ... h-10 px-4 py-2 text-sm` |
| Card | $lib/ui/Card.svelte | Same class string: `rounded-lg border border-border bg-surface-card text-text shadow-sm p-6` |
| Badge | $lib/ui/Badge.svelte | Same class string: `rounded-full px-2.5 py-1 text-xs font-medium bg-{variant}-light text-{variant}` |
| Skeleton | $lib/ui/Skeleton.svelte | `animate-pulse` + muted surface |
| EmptyState | $lib/ui/EmptyState.svelte | Copy structure + classes from source |
| PageHeader | $lib/ui/PageHeader.svelte | Same class string: `flex items-center justify-between mb-8` + `text-3xl font-bold tracking-tight text-text` |
| Input | $lib/ui/Input.svelte | Copy classes from source |
| Select | $lib/ui/Select.svelte | Copy classes from source |
## Design Token Audit (MANDATORY)
Every color/radius/shadow/spacing value used in the prototype MUST trace to `frontend/tailwind.config.js`. Complete this table during build:
| Token (tailwind.config.js) | Hex / Value | Used in prototype (elements) |
|----------------------------|-------------|------------------------------|
| `primary.DEFAULT` | `#2563eb` | primary buttons, active states |
| `primary.hover` | `#1d4ed8` | primary button hover |
| `primary.light` | `#eff6ff` | `bg-primary-light` badge variant |
| `destructive.DEFAULT` | `#dc2626` | destructive buttons, error accents |
| `destructive.light` | `#fef2f2` | `bg-destructive-light` badge variant |
| `success.DEFAULT` / `success.light` | `#22c55e` / `#f0fdf4` | success badges |
| `warning.DEFAULT` / `warning.light` | `#f59e0b` / `#fffbeb` | warning badges |
| `info.DEFAULT` / `info.light` | `#0ea5e9` / `#f0f9ff` | info badges |
| `surface.page` | `#f8fafc` | page background |
| `surface.card` | `#ffffff` | card background |
| `border.DEFAULT` | `#e2e8f0` | borders |
| `text.DEFAULT` / `text.muted` | `#0f172a` / `#64748b` | body / secondary text |
| `brand.gradient-*` | `#0ea5e9 → #06b6d4 → #4f46e5` | brand elements (if applicable) |
| `terminal.*` | dark palette | only if feature touches log/task surfaces |
**Audit gate**: scan the final `index.html` for any hex color (`#[0-9a-fA-F]{3,6}`) or hardcoded px radius that does NOT appear in the token table above. Every such value is a FAIL — replace with the nearest semantic token or document in the manifest as an intentional design gap with the production source that defines it.
#endregion Std.Opencode.PrototypeManifest
Phase 4: Browser Validation
Open specs/<feature>/prototype/index.html in the browser and validate:
- State coverage: Cycle through every state via the state switcher. Confirm each declared
@UX_STATEis visually represented. - Recovery paths: From each error state, verify the recovery action leads to the correct next state (retry → loading, dismiss → idle, etc.).
- Keyboard navigation: Tab through all interactive elements. Confirm focus rings are visible (match
focus-visible:ring-2classes). Confirm Enter/Space activate buttons and links. - Responsive: Toggle viewport size. Confirm layout adapts without overflow or broken alignment.
- Accessibility snapshot: Use browser DevTools accessibility tree to confirm ARIA roles and labels are correct.
- Design fidelity (MANDATORY): Visually compare the prototype against the real app's equivalent components (open
frontend/dev server or reference screenshots). Confirm:- Colors match the semantic palette (buttons, badges, alerts use identical hues)
- Typography scale matches (PageHeader
text-3xl font-bold, buttonstext-sm, badgestext-xs) - Spacing/padding matches (Card
p-6, Buttonpx-4 py-2, gapsgap-1.5/gap-4) - Radius matches (
rounded-mdbuttons,rounded-lgcards,rounded-fullbadges) - Shadows match (
shadow-smcards) - Any mismatch is recorded in the manifest as a design gap with a fix note
Record results in manifest.md under "Validation Results" and "Design Token Audit".
Phase 5: Report
Report:
- Prototype path:
specs/<feature>/prototype/index.html - Manifest path:
specs/<feature>/prototype/manifest.md - Screens represented: N
- Total states: N
- State coverage: N/N contracts reachable (100% required)
- Recovery paths: N/N traversable
- Accessibility: keyboard nav ✅/❌, ARIA ✅/❌, touch targets ✅/❌
- Design fidelity: ✅ all colors/radius/shadows from
tailwind.config.js; N production components replicated with verbatim class strings; N design gaps documented - Token audit: N/N hex values traced to
tailwind.config.js(100% required) - Recommended next command:
/speckit.openapi(if API surface) or/speckit.plan