A complete end-to-end example. Start with a one-line feature description, finish with a merged PR. This is what the loop looks like in real life.
Project: tsd-exercise-01 (from Chapter 01 — Next.js 15 + Docker Compose)
Feature: A cookie consent modal that blocks analytics until the user accepts.
Branch: auto-created by /speckit.specify
Total time: ~50 minutes (real time, including thinking)
cd ~/repos/tsd-exercise-01
docker compose up -d
claude
In Claude:
/prep
Output: Read project context. Ready.
(One step. Every session.)
/speckit.constitution (one-time)You only run this once per project. If you’ve already run it, skip to step 2.
/speckit.constitution
Claude reads .specify/memory/constitution.md (if it exists) or creates one. It walks you through naming the project’s non-negotiable principles. For tsd-exercise-01 you might write:
# Project Constitution
## Principles
1. **Docker-first** — All commands run inside the container. Never `npm install` on the host.
2. **TypeScript strict mode** — No `any`. No `@ts-ignore` without an issue link.
3. **TDD** — Tests written before implementation. New code without tests is rejected in /code-review.
4. **No secrets in committed files** — `${VAR:-placeholder}` pattern for env vars.
5. **Atomic commits** — Each commit ships one logical change.
Claude writes this to .specify/memory/constitution.md and reports the version (v1.0.0).
Why this matters: every later command (/speckit.plan, /speckit.analyze) reads this file and enforces the principles. You set the rules once; the rest of the loop respects them.
/speckit.specify/speckit.specify add a cookie consent modal that blocks analytics scripts until the user accepts
Claude:
cookie-consent005)005-cookie-consentspecs/005-cookie-consent/spec.md from the SpecKit template[NEEDS CLARIFICATION] markers for ambiguous decisionsspecs/005-cookie-consent/checklists/requirements.mdOutput you should see:
Branch created: 005-cookie-consent
Spec written: specs/005-cookie-consent/spec.md
Checklist: specs/005-cookie-consent/checklists/requirements.md
Spec validation: 14/16 items pass
Open clarifications:
Q1: Should consent be remembered across browsers (server-side) or per-browser (localStorage)?
Q2: How long is consent valid before re-asking?
Q3: When the user rejects, do we block ALL analytics or only non-essential?
Run /speckit.clarify to resolve, or /speckit.plan to proceed with defaults.
Open the spec file and read it. It’s now under version control on the new branch. Don’t skim — actually read it. If something is wrong, ask Claude to fix it before moving on.
/speckit.clarify/speckit.clarify
Claude presents the questions one at a time, in priority order. Each question is multiple-choice or short-answer:
Q1: Should consent be remembered across browsers (server-side) or per-browser (localStorage)?
| Option | Answer | Implications |
|--------|-----------------------------------------------------|----------------------------------------------------|
| A | Per-browser (localStorage) | Simpler, no backend, user reconsents each browser |
| B | Server-side, keyed by session cookie | Requires auth, consent persists across browsers |
| C | Server-side, keyed by IP | No auth needed but unreliable for mobile/dynamic IPs|
| Custom | Provide your own answer | |
Your choice:
You answer A. Claude updates the spec — adds a Functional Requirement saying “consent stored in localStorage”, removes the question from Open Questions, and saves the file.
Then question 2, then question 3. After all 3, Claude reports:
Clarifications complete.
Spec: specs/005-cookie-consent/spec.md
Sessions added: ## Clarifications → ### Session 2026-04-06
Questions asked: 3 / 5
Open questions remaining: 0
Next: /speckit.plan
/speckit.plan/speckit.plan
Claude reads the now-clarified spec, the constitution, and CLAUDE.md. Then generates:
specs/005-cookie-consent/plan.md — stack choices, file layout, architecturespecs/005-cookie-consent/research.md — technical decisionsspecs/005-cookie-consent/data-model.md — ConsentPreference entityspecs/005-cookie-consent/contracts/ — interface signaturesspecs/005-cookie-consent/quickstart.md — integration scenarioOpen plan.md. You’ll see something like:
## Stack & Constraints
- Framework: Next.js 15 (from CLAUDE.md)
- Storage: localStorage (from spec clarification 1)
- Component pattern: 5-file atomic (from CLAUDE.md)
- Tests: Vitest (from CLAUDE.md)
## File Layout
src/components/ConsentModal/
├── index.tsx
├── ConsentModal.tsx
├── ConsentModal.test.tsx
├── ConsentModal.stories.tsx
└── ConsentModal.accessibility.test.tsx
src/lib/consent.ts
src/lib/consent.test.ts
src/app/layout.tsx (modified)
## Data Model
ConsentPreference: { acceptedAt: ISO8601, version: 1 }
## Contracts
getConsent(): ConsentPreference | null
setConsent(): void
<ConsentModal />: self-mounting component, props: none
The plan is the last stop before tasks. Read it. If the architecture is wrong, fix it now — fixing in plan is cheap, fixing in code is expensive.
/speckit.tasks/speckit.tasks
Claude reads the plan and produces specs/005-cookie-consent/tasks.md:
## Phase 1: Setup
- [ ] T001 — Create empty `src/components/ConsentModal/` directory
## Phase 2: Tests (TDD)
- [ ] T010 [P] — Write `src/lib/consent.test.ts` (round-trip get/set)
- [ ] T011 [P] — Write `src/components/ConsentModal/ConsentModal.test.tsx`
- [ ] T012 [P] — Write `src/components/ConsentModal/ConsentModal.accessibility.test.tsx`
## Phase 3: Core
- [ ] T020 — Implement `src/lib/consent.ts` (depends on T010)
- [ ] T021 — Implement `src/components/ConsentModal/ConsentModal.tsx` (depends on T011, T012)
- [ ] T022 — Implement `src/components/ConsentModal/index.tsx` (barrel export)
## Phase 4: Integration
- [ ] T030 — Mount `<ConsentModal />` in `src/app/layout.tsx`
- [ ] T031 — Wire analytics gating (conditional render of `<Script />`)
## Phase 5: Polish
- [ ] T040 [P] — Add `ConsentModal.stories.tsx` for Storybook
- [ ] T041 [P] — Add changelog entry
11 tasks, 5 phases, parallelism marked. Each task is mechanical to execute.
/speckit.analyze (optional, recommended)/speckit.analyze
Read-only check across spec, plan, tasks, and constitution. Output:
### Specification Analysis Report
| ID | Category | Severity | Location | Summary | Recommendation |
|-----|-------------|----------|-----------------------|--------------------------------------------------|-----------------------------------------------|
| C1 | Coverage | MEDIUM | tasks.md | T031 (analytics gating) has no test task | Add T013: integration test for gated analytics|
| A1 | Ambiguity | LOW | spec.md FR-003 | "Modal is accessible" — unmeasurable | Specify WCAG 2.1 AA + keyboard-only flow |
Coverage: 10/11 requirements have tasks (91%)
Critical issues: 0
You ask Claude to fix C1 (add the missing test task) and A1 (sharpen the FR). Claude updates tasks.md and spec.md. Re-run /speckit.analyze:
Coverage: 11/11 requirements have tasks (100%)
Critical issues: 0
LOW issues: 0
Next: /speckit.implement
Green. Proceed.
/speckit.implement/speckit.implement
Claude executes the tasks, phase by phase:
═══════════════════════════════════════════
Phase 1: Setup
═══════════════════════════════════════════
✓ T001 — Created src/components/ConsentModal/ (0.2s)
═══════════════════════════════════════════
Phase 2: Tests (TDD)
═══════════════════════════════════════════
✓ T010 — Wrote src/lib/consent.test.ts (3 tests, all failing as expected) (1.4s)
✓ T011 — Wrote ConsentModal.test.tsx (5 tests, failing as expected) (2.1s)
✓ T012 — Wrote ConsentModal.accessibility.test.tsx (3 tests, failing as expected) (1.8s)
✓ T013 — Wrote analytics-gating integration test (failing as expected) (1.7s)
Phase boundary: tests are red ✓ (correct for TDD)
═══════════════════════════════════════════
Phase 3: Core
═══════════════════════════════════════════
✓ T020 — Implemented src/lib/consent.ts (T010 now passing: 3/3) (3.2s)
✓ T021 — Implemented ConsentModal.tsx (T011: 5/5, T012: 3/3) (8.4s)
✓ T022 — Implemented index.tsx (0.4s)
═══════════════════════════════════════════
Phase 4: Integration
═══════════════════════════════════════════
✓ T030 — Mounted <ConsentModal /> in layout.tsx (1.1s)
✓ T031 — Wired analytics gating (T013 now passing) (2.3s)
═══════════════════════════════════════════
Phase 5: Polish
═══════════════════════════════════════════
✓ T040 — Added ConsentModal.stories.tsx (1.5s)
✓ T041 — Added changelog entry (0.3s)
═══════════════════════════════════════════
BUILD COMPLETE
═══════════════════════════════════════════
Tasks: 12/12 ✓
Test suite: PASS (47 tests, 1.2s)
Lint: PASS
Type-check: PASS
Next: /code-review
12 tasks, all green. You watched the whole thing. You can interrupt with Esc at any point if Claude goes sideways.
/code-review/code-review
Five-phase audit. Output:
| Category | Found | Fixed | Remaining |
|----------------|-------|-------|-----------|
| Security | 1 | 1 | 0 |
| Performance | 0 | 0 | 0 |
| Code Quality | 2 | 2 | 0 |
| Test Coverage | 0 | 0 | 0 |
Security finding: the consent value didn’t have a version field check on read — fixed. Code quality: an unused import in index.tsx and a missing aria-label on the close button — fixed.
All zeros remaining. Ship it.
/test/test
Test Results
Passing: 47
Failing: 0
Skipped: 0
Duration: 1.3s
Green. Proceed.
/commit/commit
Claude runs lint and type-check (Docker), they pass, then commits:
feat(consent): add cookie consent modal blocking analytics
Closes spec 005-cookie-consent. Modal blocks analytics scripts until
user accepts. Consent stored in localStorage with version field.
WCAG 2.1 AA compliant with keyboard navigation and focus trap.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Hash: a1b2c3d. Branch: 005-cookie-consent.
/ship/ship
Pre-flight, switch to main, merge with --no-ff, delete the feature branch, list stray branches.
═══════════════════════════════════════════
SHIP COMPLETE
═══════════════════════════════════════════
Merged: 005-cookie-consent → main (--no-ff)
Hash: a1b2c3d → e4f5g6h (merge commit)
Branch: 005-cookie-consent (deleted)
Stray: none
Local only — `git push` when ready.
═══════════════════════════════════════════
You decide when to push.
You went from a one-line description to a merged feature with:
In ~50 minutes. Without losing track of what you were building once.
Read back the artifacts in specs/005-cookie-consent/:
spec.md — what you wanted, plus the clarificationsplan.md — how it was going to be builttasks.md — every task checked offdata-model.md, contracts/, research.md, quickstart.md — supporting docsWhen you come back to this feature in 6 months to add per-vendor toggles, you read these files first. They’re the source of truth for “what does this code do and why.”
| Situation | What to do |
|---|---|
/speckit.clarify asks a question you don’t know the answer to |
Pick the most reasonable option and add a TODO. Don’t block on perfection. |
/speckit.plan proposes an architecture you disagree with |
Tell Claude: “Replan with X instead of Y”. Don’t accept a plan you wouldn’t write yourself. |
/speckit.analyze finds a CRITICAL issue |
Stop. Fix it before /speckit.implement. Re-run /speckit.analyze. |
/speckit.implement gets a test failure mid-phase |
Claude halts. Ask Claude to fix the failure. Re-run /speckit.implement — it picks up from the failed task. |
/code-review finds something you don’t want fixed |
Tell Claude to skip that finding and explain why. |
/test fails after /speckit.implement |
Bug in the implementation. Claude tracks it down. The tests already exist, so the failure is precise. |
../03-slash-commands/catalog/speckit.md — full reference for each command../principles/spec-before-code.md — why this discipline exists