---
name: build-prototype
description: "Use to scaffold a working POC for verification — triggered by phrases like 'build the prototype', 'scaffold the POC', 'turn the wireframe into something I can run', 'make it interactive', 'spin up the prototype', 'let's see this running'. Stack is selected per work item from `artifacts.prototype.stack:` (vite-react default, or nextjs / existing-repo / python-fastapi / cli / go-service / none) — the scaffold matches the production target. Produces a runnable POC at pocs/{name}-prototype/ (or skinned into an existing host app for nextjs / existing-repo). The prototype is the verification surface for concept + product behaviour before any production code is written. Skip if the wireframe is required by the stack and is not locked, if a prototype already exists at the target path (use iterate-prototype for changes), or if the work is production code under TDD (use build-tdd)."
---

# Build Prototype

## Overview

Once a wireframe is locked, the prototype turns it into something the user can run. Real React components, real state transitions, real seed data — but in-memory storage, mocked external calls, no auth, no production hardening. The prototype answers "does this actually work as an interaction model?" and surfaces gotchas that the wireframe couldn't.

**Core principle:** the prototype is the spec. Mocks and in-memory state are tolerated here and ONLY here. The shape this skill produces becomes the shape the production build (Phase 6) inherits.

**Announce at start:** "I'm using the build-prototype skill to scaffold a working POC from the locked wireframe."

## When to Use

- Phase 3 of a prototype-driven flow (after wireframe is locked, before Phase 4 iteration and Phase 5 codification)
- For `/feature` on existing apps: scaffold a `pocs/{feature-name}-prototype/` mini-prototype branch of the existing app's tech stack — same skill, smaller scope
- Ad-hoc when a wireframe is mature enough to validate via running code

**Do NOT skip when:**
- The wireframe "looks complete" — wireframe completeness is necessary but not sufficient; running code surfaces interaction-model issues the wireframe can't
- The team is in a hurry to ship — skipping prototype is what produces "implementation exists but is not wired" in production

## Stack variants

The prototype stack is selectable per work item via `artifacts.prototype.stack:` in the manifest. The scaffold matches the production target — a Go service prototype is a Go service, a CLI prototype is a working CLI, a FastAPI prototype is a FastAPI service. Forcing a single stack on every product would produce verification surfaces that don't match production reality.

| `stack:` value | Scaffold target | Use when |
|---|---|---|
| `vite-react` (default for React/no-stack projects) | Fresh Vite + React 18 + TS strict + Tailwind v4 + Zustand v5 + lucide-react + Biome at `pocs/{name}-prototype/` | Greenfield React app, or extending a Vite project; UI-first product. |
| `nextjs` | Skin new screens into the existing Next.js app under a `prototype/` route group or branch — reuse host components + styling, do NOT recreate a Vite scaffold | Existing Next.js codebase. |
| `existing-repo` | Skin into the existing app at the user's chosen path/branch; match the host's framework (Vue, Astro, Svelte, etc.) | Existing app with a non-Next.js stack or one forge doesn't have a recipe for. |
| `python-fastapi` | `pocs/{name}-prototype/` with FastAPI + uvicorn, sample-request scripts (`sample_request.sh`), and the auto-generated `/docs` OpenAPI playground as the verification UI; seed fixtures under `fixtures/` | Python backend service. The verification surface is request-inspection + scripted samples, not a React shell. |
| `cli` | `pocs/{name}-prototype/` with the CLI binary (whatever language fits) + `demo.sh` exercising the command-line flow + sample outputs under `samples/` | CLI tool. The verification surface is "the binary runs and produces correct output." |
| `go-service` | `pocs/{name}-prototype/` with the Go service (HTTP or gRPC) + `grpcurl`/`curl` demo scripts + sample-request fixtures | Go service. The verification surface is wire-protocol-level (grpcurl traces, curl-able endpoints). |
| `none` | Skip the prototype phase entirely; `phase_plan.prototype: skipped` must match | Work item where prototype-driven SDLC doesn't fit (library, IaC, research). |

### How the default is chosen

At `/feature` or `/greenfield` preflight, the planner proposes a `stack:` value from `project.stack:` in `.claude/CLAUDE.md` (set by `/setup`) plus a quick read of the work item description:

| Project signals | Proposed `stack:` |
|---|---|
| `next` in stack | `nextjs` |
| `react` or `vite` in stack (no Next) | `vite-react` |
| `fastapi` / `flask` / `django` in stack | `python-fastapi` |
| `go` in stack (with gRPC/HTTP server deps) | `go-service` |
| CLI-shaped — no UI framework, binary entrypoint | `cli` |
| Any other existing app | `existing-repo` |
| Library / IaC / research / no app shape | `none` |

The user confirms or overrides; the chosen value lands at `artifacts.prototype.stack:` in the manifest. Build-prototype reads from there at Step 1.

### The verification-surface principle

The prototype is the verification surface for the feature before production code. Each stack's verification surface matches its production reality:

- `vite-react` / `nextjs` / `existing-repo` — clickable UI mirroring wireframe states.
- `python-fastapi` / `go-service` — request-inspector or wire-protocol demo (no React shell — that's theatre, not verification).
- `cli` — the binary itself plus a demo script that exercises it.
- `none` — there is no prototype phase; the work item proceeds straight to codify (`harden`) from requirements or concept/wireframe (if any).

Don't bend the stack to fit a verification surface the user doesn't need. A Vite + React shell over a Go service is theatre — it fictionalizes a UI that won't exist in production and trains the prototype to lie about how the system is actually verified.

## Inputs

| Source | Required | Purpose |
|---|---|---|
| Locked wireframe HTML | yes | Visual + interaction-model spec |
| Wireframe README | yes | View list, hash routes, design intent |
| Concept slides | yes | High-level intent + out-of-scope context |
| Existing prototype (if extending) | optional | Codebase to extend |
| Manifest at `.forge/work/{type}/{name}/manifest.yaml` | yes | Phase state target |

## Outputs

The output shape depends on `artifacts.prototype.stack:` (see Stack variants above). The detailed tree below is for `vite-react`; `nextjs` outputs are screens skinned into the host app, `existing-repo` outputs are screens in the host's framework, `python-fastapi` produces a FastAPI service + scripts, `cli` produces a binary + demo, `go-service` produces a Go service + grpcurl scripts. Step 1's per-stack branches enumerate each.

For `vite-react`:

```
pocs/{name}-prototype/
├── package.json
├── vite.config.ts
├── tsconfig.json
├── tailwind.config.ts
├── biome.json
├── index.html
├── src/
│   ├── main.tsx          # entry
│   ├── App.tsx           # router shell
│   ├── routes/           # one file per top-level route
│   ├── components/       # reusable UI primitives
│   ├── features/         # feature-area folders, each owning its screens
│   ├── store/            # Zustand stores
│   ├── data/             # seed data (TS const arrays, NOT db)
│   ├── types/            # shared TS types
│   └── styles/
│       └── tokens.css    # design tokens extracted from wireframe
├── README.md             # what this prototype is, how to run it, what's mocked
└── .forge/
    ├── feedback.md       # running list of user iteration feedback
    └── convergence-log.md # rejected approaches, tried-and-discarded
```

Run with:
```bash
cd pocs/{name}-prototype && npm install && npm run dev
```

## Process

### Step 0: locate inputs

| Input | How to find |
|---|---|
| Repo root | `git rev-parse --show-toplevel` or `${CLAUDE_PROJECT_DIR}` |
| Manifest | The single in-progress manifest under `.forge/work/*/*/manifest.yaml`. Multiple in flight = ask user. |
| Wireframe path | Manifest's `artifacts.wireframe.html_path`, or default `pocs/{name}-wireframe/index.html` |
| Concept deck | Manifest's `artifacts.concept.deck_path`, or default `decks/{name}/slides.md` |
| Output target | `pocs/{name}-prototype/` (default) or override via manifest's `artifacts.prototype.path` (set by this skill at Step 6 scaffold-complete; `locked_at` is written later by `iterate-prototype`, not here) |

Wireframe-lock requirement depends on the stack:
- UI stacks (`vite-react`, `nextjs`, `existing-repo`) **require** `artifacts.wireframe.locked_at` — the wireframe is the screen list this skill scaffolds against. If absent, surface the gap and stop.
- Non-UI stacks (`python-fastapi`, `cli`, `go-service`) do NOT require a wireframe — the verification surface is wire-protocol or CLI behaviour, not screens. Read the source-of-truth from `phase_plan.wireframe: skipped` (expected) plus the requirements doc, concept deck, or `artifacts.discover-requirements.path:` for endpoint / command surface.
- `none` should not invoke this skill — see Step 1's `none` branch.

### Step 1: scaffold the project

Read `artifacts.prototype.stack:` from the manifest. If absent, default to `vite-react` (and surface a warning — the preflight planner should have set it). If `stack: none`, surface the contradiction (build-prototype shouldn't be invoked when prototype is skipped) and stop. Otherwise branch:

#### Stack `vite-react`

Initialize the Vite + React + TS skeleton:

```bash
cd <repo-root>
npm create vite@latest pocs/{name}-prototype -- --template react-ts
cd pocs/{name}-prototype
npm install
npm install -D @biomejs/biome tailwindcss@next @tailwindcss/vite
npm install zustand@^5 lucide-react
```

Then add forge-standard configs:
- Replace `tailwind.config.ts` with v4 config that reads the wireframe's design tokens
- Add `biome.json` with forge defaults (2-space indent, double quotes, semicolons)
- Update `vite.config.ts` to include the `@tailwindcss/vite` plugin
- Replace `src/App.tsx` skeleton with the router shell
- Create `src/styles/tokens.css` extracted from the wireframe's CSS variables

Create the project README listing what's mocked and how to run.

#### Stack `nextjs`

Identify the host Next.js app (working directory or path the user supplies). DO NOT recreate a Vite scaffold. Create new screens as a route group under the existing app:

- `app/(prototype)/<feature>/` or `pages/prototype/<feature>/` depending on app router style
- Reuse the host's components, layouts, and styling (Tailwind / CSS modules / styled-components — whatever the host uses)
- Add seed-data imports under the prototype route group, mocking external services with local TS const arrays
- Update the host README or add `prototype/README.md` listing what's mocked and how to run the host dev server to see the new screens

#### Stack `existing-repo`

Coordinate with the user on the host app's path, branch, and framework (Vue / Astro / Svelte / Solid / etc.). Scaffold the new screens IN the host app using the host's conventions — DO NOT introduce React or Tailwind if the host doesn't already use them. Output is a set of file changes in the host app, not a fresh `pocs/` directory. Record the host path in `artifacts.prototype.path:`.

#### Stack `python-fastapi`

Initialize a FastAPI service at `pocs/{name}-prototype/`:

```bash
cd <repo-root>
mkdir -p pocs/{name}-prototype && cd pocs/{name}-prototype
python -m venv .venv && source .venv/bin/activate
pip install fastapi 'uvicorn[standard]' pydantic
```

Scaffold:
- `main.py` with the routes the wireframe (or requirements doc) describes
- `requirements.txt` pinning fastapi + uvicorn + pydantic
- `fixtures/` directory with seed data as JSON or Python const dicts
- `sample_request.sh` exercising each endpoint with curl
- `README.md` documenting `uvicorn main:app --reload` + the `/docs` URL as the verification UI

The verification surface IS the auto-generated `/docs` OpenAPI playground plus the sample-request scripts — no React shell.

#### Stack `cli`

Initialize the CLI at `pocs/{name}-prototype/` using whatever language fits the production target (Python with click/typer, Go with cobra, Node with commander, or pure Bash). Scaffold:

- The binary entrypoint (e.g. `main.py`, `cmd/cli/main.go`, `bin/cli.js`, or `cli.sh`)
- `demo.sh` that exercises the CLI's primary flows with sample inputs
- `samples/` directory with recorded sample outputs (stdout + stderr + exit codes) for the demo's invocations
- `README.md` documenting how to run the CLI, what the demo exercises, and where to add new commands

The verification surface is "the binary runs and produces the recorded sample output for each demo invocation."

#### Stack `go-service`

Initialize the Go service at `pocs/{name}-prototype/`:

```bash
cd <repo-root>
mkdir -p pocs/{name}-prototype && cd pocs/{name}-prototype
go mod init {module-name}
```

Scaffold:
- `cmd/server/main.go` — service entrypoint (HTTP or gRPC depending on production target)
- `proto/*.proto` if gRPC; generate with `protoc` + the appropriate plugins
- `internal/` packages following the host project's layout
- `scripts/grpcurl-demo.sh` or `scripts/curl-demo.sh` exercising each endpoint with sample requests
- `fixtures/` directory with sample request bodies (JSON or protobuf textproto)
- `README.md` documenting `go run ./cmd/server` + how to run the demo scripts

The verification surface is wire-protocol-level — grpcurl traces or curl outputs against the running service.

#### Stack `none`

This should not happen — if `stack: none`, the prototype phase should be `phase_plan.prototype: skipped`. Surface the contradiction (manifest claims a stack of `none` but build-prototype was invoked) and stop. The right action is to mark the phase as `skipped` and proceed to Phase 5 (codify) directly.

### Step 2: identify partitions

**Stack note.** The partitioning model below assumes a UI stack (`vite-react`, `nextjs`, `existing-repo`) with a wireframe-driven view list. For non-UI stacks, adapt the principle to the stack's idiom: `python-fastapi` partitions by endpoint group or resource; `cli` partitions by subcommand or feature flag set; `go-service` partitions by service surface (gRPC service definitions or HTTP route groups). The mechanics — independent feature areas, parallel builds, partition-plan documentation — are the same.

Read the wireframe's view list (from wireframe README + the `VIEWS` registry in the wireframe HTML). Identify natural partitions — independent feature areas that can be built in parallel.

Partition heuristics:
- **By user role** — agent screens vs supervisor screens (different feature folders, no overlap)
- **By tab cluster** — distinct top-level tabs in the wireframe (separate route trees)
- **By system area** — UI screens vs tutorial overlay vs settings panel

If the wireframe is small (≤5 views, single user role) → single partition (no parallel). If the wireframe is medium (6-15 views, role-distinct or area-distinct) → 2-3 partitions. Don't over-partition — each partition's prototype-builder needs enough work to justify its own context.

Document the partition plan in `pocs/{name}-prototype/.forge/partition-plan.md` so subsequent iterations can resume.

### Step 3: dispatch prototype-builder per partition

For each partition, dispatch a `prototype-builder` subagent in parallel. Each instance receives:
- The wireframe HTML
- The wireframe README
- Its assigned partition (specific views to build)
- File-ownership boundaries (which `src/features/{area}/` is yours; what's shared)
- The shared scaffold (types, store skeleton, routes registry) — read-only for the subagent

The subagent returns:
- Files to write under its assigned `src/features/{area}/`
- Updates to add to shared types (in `src/types/`)
- Updates to add to the shared store (Zustand slice)
- Seed data files

The main session merges results: filesystem-level merge for non-overlapping partition output; for shared updates (types, store), apply each subagent's diff in turn.

### Step 4: verify

The verification check matches the stack's verification surface (per the verification-surface principle in the Stack variants section).

#### `vite-react` / `nextjs` / `existing-repo` (UI stacks)

```bash
cd pocs/{name}-prototype   # or the host app's dev command for nextjs / existing-repo
npm run dev                # smoke test: dev server starts, no compile errors
npx biome check .          # lint passes (vite-react only — host app has its own linter)
```

Use Playwright to verify each wireframe view has a corresponding running screen:

```bash
npx playwright codegen http://localhost:5173    # or host app's URL
```

Click through each route. Each screen should:
- Render without console errors
- Have the same visual structure as the wireframe state
- Have working interactions (clicks navigate, forms accept input, state updates)
- Use seed data (real-shaped, fake-content)

#### `python-fastapi`

```bash
cd pocs/{name}-prototype
source .venv/bin/activate
uvicorn main:app --reload
# in another shell:
bash sample_request.sh    # exercises each endpoint
```

Verify each endpoint listed in the requirements / wireframe has a working route + sample response that matches the contract. Open `http://localhost:8000/docs` to confirm the OpenAPI playground renders.

#### `cli`

```bash
cd pocs/{name}-prototype
bash demo.sh > /tmp/demo.out 2>&1    # capture full stdout + stderr
diff samples/expected.out /tmp/demo.out   # confirm outputs match recorded samples
```

Each demo invocation should match its recorded sample output. Drift means either the CLI changed or the samples need re-recording — flag in feedback.md.

#### `go-service`

```bash
cd pocs/{name}-prototype
go run ./cmd/server &
SERVER_PID=$!
bash scripts/grpcurl-demo.sh   # or scripts/curl-demo.sh
kill $SERVER_PID
```

Verify each service method (gRPC) or route (HTTP) responds with the expected shape against the sample requests.

---

Capture findings in `pocs/{name}-prototype/.forge/feedback.md` if there's any drift between prototype and wireframe / requirements.

### Step 5: side-effect capture during iteration

Phase 3 (prototype build) and Phase 4 (iteration) are when real gotchas + conventions emerge. During the build and iteration cycles:

- **Gotchas**: every time prototype-builder hits a state-management surprise, library quirk, framework mounting issue, or type-system pothole, write to `aiwiki/gotchas/{date}-{slug}.md` per the gotcha schema. The wiki-lint hook validates on save.
- **Conventions**: when patterns settle (file naming, import order, state-shape conventions, design-token usage), write to `aiwiki/conventions/{slug}.md`. The `prototype-reviewer` flags new conventions emerging across iteration cycles.

These outputs feed Phase 5 codification. Do not skip them — Phase 5 reads from `aiwiki/gotchas/` and `aiwiki/conventions/` directly.

### Step 6: record initial scaffold

After the initial scaffold passes its first manual click-through (Step 4 above — dev server starts, screens render, in-memory state loads), record the path in the manifest:

- `artifacts.prototype.path: pocs/{name}-prototype/`
- `artifacts.prototype.scaffold_status: complete`

**Do NOT write `artifacts.prototype.locked_at` here.** The lock is owned by `iterate-prototype` (Phase 4), which writes `locked_at` on its convergence signal ("satisfied" / "LOCKED" / "done") after polish iteration. Single-writer ownership: a two-writer situation produced a race-condition risk; `build-prototype` writes only path + scaffold status, `iterate-prototype` writes the lock.

Control transfers to `iterate-prototype` for the polish loop. Phase 5 (`harden`) blocks until `iterate-prototype` writes `artifacts.prototype.locked_at`.

## Backend integration during prototype (UI stacks)

The patterns below apply to `vite-react` / `nextjs` / `existing-repo`. For pure backend stacks (`python-fastapi`, `go-service`), the prototype IS the backend — no integration question. For `cli`, the demo script exercises whatever the CLI calls (in-memory data or a mocked service). For `none`, this section doesn't apply.

For UI stacks with a backend dependency, the prototype frontend connects to a local backend (Vite proxy to a separate dev server) OR uses fully in-memory mocks. Two patterns:

| Pattern | Use when |
|---|---|
| Local backend + Vite proxy | Backend can be run locally with seed data; integration concerns are real and worth verifying |
| Fully in-memory | Backend is complex enough that running it would slow the prototype loop. Stub responses in `src/data/mocks.ts`. |

Document the choice in `pocs/{name}-prototype/README.md` so Phase 5 codification knows what's mocked.

## What build-prototype does NOT do

- Does NOT write production code (mocks and in-memory state are intentional here)
- Does NOT implement real auth (seed user; real auth is a Phase 6 production-build delta)
- Does NOT set up CI / deployment (those are Phase 6/7)
- Does NOT run code review or static analysis gates (those are Phase 6)
- Does NOT add E2E test infrastructure beyond Playwright codegen for verification (full E2E suite is Phase 6)

## Common mistakes

| Mistake | Fix |
|---|---|
| Hardening the prototype (real auth, real DB, error handling for unlikely failures) | Stop. The prototype is the spec; hardening is Phase 6's job. Time spent here is wasted twice. |
| Skipping seed data and using empty arrays | Empty UIs hide layout bugs. Use realistic-shaped seed data with placeholder content. |
| One giant feature folder | Partition by feature area. Even a small prototype benefits from `src/features/cases/`, `src/features/users/` etc. |
| Skipping `pocs/{name}-prototype/.forge/feedback.md` | The feedback file is how iteration converges; without it, the prototype just keeps changing without a plan |
| Missing Biome config (vite-react only) | The vite-react path's lint/format convention; downstream tooling expects it. Other stacks use their own (ruff for python-fastapi, gofmt+golangci-lint for go-service, host's linter for nextjs/existing-repo, shellcheck/etc. for cli). |
| Inventing screens not in the wireframe | The wireframe is the spec; if a screen is missing, surface the gap to the user (the wireframe needs an iteration), don't extrapolate |
| Pre-promoting prototype gotchas to typed pages without `support-gotcha` | Gotchas use the `support-gotcha` skill flow; lint validates the schema. Write them through the proper channel. |

## Red Flags

**Never:**
- Implement real auth, real DB, real external API calls (Phase 6's job)
- Skip wireframe verification — every wireframe view must have a corresponding running screen
- Lock the prototype with known-broken interactions (the lock signal means "this is what production should match")
- Over-partition (more than 3 partitions for typical work; if you need 4+, the wireframe is too sprawling — push back)

**Always:**
- For UI stacks (`vite-react`, `nextjs`, `existing-repo`): match the wireframe's visual structure — design tokens, icon set, layout primitives — using the stack's styling convention (Tailwind for `vite-react`; the host's styling system for `nextjs` / `existing-repo`)
- Use realistic-shaped seed data — placeholder content but real data shapes
- Capture gotchas + conventions during iteration (they're Phase 5 inputs)
- Verify with `npm run dev` + click-through before any iteration cycle ends

## I/O Contract

| Field | Value |
|---|---|
| **Requires** | Locked wireframe HTML + README, concept deck, manifest |
| **Produces** | A working POC at `pocs/{name}-prototype/` (shape per `artifacts.prototype.stack:` — full Vite + React app for `vite-react`, FastAPI service for `python-fastapi`, CLI binary + demo for `cli`, Go service for `go-service`; or skinned into the host app for `nextjs` / `existing-repo`), plus `.forge/feedback.md` under the prototype dir, plus `aiwiki/gotchas/` + `aiwiki/conventions/` entries captured during iteration |
| **Updates manifest** | `artifacts.prototype.{path, scaffold_status: complete}` after the initial scaffold's click-through. `locked_at` is written by `iterate-prototype` on convergence — this skill does NOT write it (single-writer ownership; see Step 6). |
| **Triggers** | wiki-lint on every `aiwiki/**` write during iteration; phase-close dream after lock |

## Integration

| Caller | When |
|---|---|
| Wireframe-lock event in `/feature` and `/greenfield` | Phase 2 → Phase 3 transition |
| Manual invocation | When a wireframe is mature enough to verify via running code |

| Dispatches | For |
|---|---|
| `prototype-builder` subagent (multi-instance) | Per-partition implementation in own context windows |

| Pairs with | For |
|---|---|
| `iterate-prototype` skill | Polish loop after initial scaffold; user feedback drives revisions |
| `prototype-reviewer` agent | Drift check between prototype and wireframe before Phase 5 |
| `support-gotcha` skill | Capture gotchas surfaced during iteration |
| `harden` skill | Phase 5 successor — reads the locked prototype as input |
