| name | spec-writer |
| description | Write, update, and maintain living specification documents and propose change documents. Use when user says 'write a spec', 'create a spec', 'spec out', 'update spec', 'spec this', 'design a feature', 'write a feature spec', or needs to create/modify specs in docs/specs/ or propose changes in docs/changes/. Handles the complete spec lifecycle: creation, updates, gap analysis, and change proposals. |
Spec Writer
This skill manages the complete specification lifecycle: creating living spec documents in docs/specs/<name>/, updating them to reflect current implementation, identifying gaps between spec and code, and proposing change documents in docs/changes/ to close those gaps.
Scope — Documentation Only
CRITICAL: This skill writes ONLY specs and change documents. It MUST NOT write, modify, or generate any implementation code.
- Do NOT edit source files (
.ts, .tsx, .js, .py, etc.)
- Do NOT create or run database migrations
- Do NOT modify tests, configs, or any non-documentation file
- Do NOT install packages or run build commands
- The ONLY files this skill creates or modifies are in
docs/ (specs, changes, indexes)
If the user asks to "write a spec AND implement it", write the spec/changes first, then stop and tell the user to use /dev for implementation.
Core Principles
- Specs are living knowledge — They describe the CURRENT state of the system, not a future plan. They MUST be kept in sync with implementation.
- One spec per major feature — Never create a single monolithic spec for an entire application. Each major feature area (e.g., authentication, file storage, upload system, password protection) gets its own spec. A general "architecture" or "setup" spec is appropriate for project-level concerns (tech stack, directory structure, deployment topology), but feature behavior belongs in feature-specific specs. This keeps specs focused, readable in one sitting, and independently maintainable.
- Changes are plans — Planning, task tracking, and implementation details go in
docs/changes/NNNN-name.md, never in specs.
- RFC 2119 everywhere — All specs and changes MUST use RFC 2119 keywords (MUST, MUST NOT, SHALL, SHALL NOT, SHOULD, SHOULD NOT, MAY, OPTIONAL) for requirement precision.
- Behavioral scenarios — Every requirement SHOULD have GIVEN/WHEN/THEN scenarios that map directly to test cases. Scenarios live in the document that OWNS the requirement — never copied into a second document that references it.
- Exhaustive exploration — Research the codebase deeply before writing anything. Every claim in a spec must be verified against actual code.
- Clarify before writing — Use AskUserQuestion for scope decisions and design choices.
- Single-owner contracts — Every behavioral rule has exactly ONE owning document. Other documents LINK to it; they MUST NOT restate it. See "Single-Owner Contracts" below.
- Observable behavior, not mechanism — Specs describe what a user or consumer can observe, not the implementation that produces it. See "Specification Altitude" below.
Single-Owner Contracts
CRITICAL: Never write the same behavioral rule in two documents.
The most common failure mode of a spec corpus is duplication. A contract written in the spec, restated in the change document's requirements, restated again in a scenario, and again in a task line is four copies and four chances to drift. Every later edit that touches one copy leaves the other three stale — and reviewers then report each stale copy as a separate defect, indefinitely. The duplication IS the defect, not the drift it eventually produces.
The ownership rule:
| Content | Owner |
|---|
| Behavior, requirements, defaults, state matrices, scenarios | The spec |
| Sequencing, dependencies, migrations, PR breakdown, per-change design decisions, out-of-scope boundaries, testing requirements | The change document |
When writing a change document, reference the spec instead of restating it:
<!-- ❌ WRONG — restates the spec, will drift -->
### Functional requirements
- The widget MUST toggle on tap, MUST show a spinner while the call is in
flight, MUST render `unavailable` state as inert, and MUST NOT fire when
the entity is in a transitional state...
<!-- ✅ RIGHT — references the spec, states only what this change owns -->
### Functional requirements
The [widget spec](../specs/widgets/index.md) owns the option keys, defaults,
state matrix, and its scenarios — this change's acceptance criteria, not
restated here. What implementing them requires of this change:
- Options are stored under `item.config` and edited via the existing config form.
- **Legacy migration:** the loader rewrites `enableFoo` to `showFoo`; the legacy
key is never written back.
Rules:
- A change document's requirements section MUST open by naming the spec section that owns the behavior, then list ONLY what the change itself owns.
- If a change document needs a GIVEN/WHEN/THEN scenario, first ask whether the behavior belongs in the spec. Change documents get scenarios ONLY for things they introduce that no spec owns (e.g. a migration, a bugfix in a service mapping, a build gate).
- If a behavior has no owning spec section yet, ADD it to the spec — do not let the change document become its de facto home.
- When you catch yourself writing "per the spec" followed by a restatement of that spec, delete the restatement and keep the link.
Specification Altitude
Specs describe observable behavior. They MUST NOT prescribe the implementation that produces it.
Naming a library's components, methods, or built-in behaviors inside a normative requirement converts a third-party API into a checkable claim — reviewers will then verify it against that library forever, and it goes stale on every upgrade. It is review surface the spec never needed.
<!-- ❌ WRONG — names a library API in a normative rule -->
- Cancelling (button, ESC, outside tap) MUST fire nothing — Radix
`AlertDialog.Content` blocks interact-outside dismissal.
<!-- ✅ RIGHT — states the observable rule -->
- Cancelling MUST fire nothing and leave no pending state. Dismissal MUST
require an explicit choice.
Rules:
- Do NOT write component names, method names, library-specific props/events, hook names, class names, or file layout into spec requirements. Those are implementation-time decisions.
- Exception: genuine cross-cutting commitments that other documents depend on — a design-token contract, a stable selector contract, cascade ordering, a named dispatch path, a public API surface — are architecture, not mechanism, and belong in the spec.
- Implementation guidance that IS useful goes in the change document's Design Decisions, where it is explicitly a decision for that change rather than a standing contract.
MUST density is a smell. Each MUST is an assertion someone must verify against every other one. If a requirement does not describe something observable or a contract another document depends on, write it as prose or a design decision instead.
RFC 2119 Reference
Per RFC 2119, these keywords indicate requirement levels:
- MUST / SHALL / REQUIRED — Absolute requirement
- MUST NOT / SHALL NOT — Absolute prohibition
- SHOULD / RECOMMENDED — Strong recommendation; exceptions need justification
- SHOULD NOT / NOT RECOMMENDED — Strong discouragement; exceptions need justification
- MAY / OPTIONAL — Truly optional; interoperability must work with or without
Workflow
Phase 0: Setup
Every time this skill is invoked, run the setup skill first to ensure docs structure and instruction files are in place:
Skill tool: skill="fx-dev:setup"
This is fast and idempotent — it checks what exists and only creates/modifies what's missing. It handles:
docs/ folder structure (specs/, changes/, tasks.md, index.yml, index.md)
AGENTS.md task-tracking instructions (+ the CLAUDE.md → @AGENTS.md pointer)
REVIEW.md PR review instructions (+ .coderabbit.yaml pointing CodeRabbit at it)
Wait for setup to complete before proceeding.
Phase 1: Deep Research
This phase MUST be thorough. Insufficient research leads to inaccurate specs.
1.1 Local Codebase Exploration
Launch Explore sub-agents (subagent_type: Explore) to deeply understand the relevant parts of the codebase:
- Map all files, modules, and data models the feature touches
- Identify existing patterns, abstractions, and conventions
- Find related features or systems already implemented
- Note constraints (auth patterns, API conventions, DB schema patterns, component libraries)
- Read test files to understand expected behaviors
- Check git history for recent changes in relevant areas
Launch multiple Explore sub-agents in parallel if the feature spans distinct areas (e.g., frontend + backend + database + tests).
1.2 Technology and Pattern Research
Launch a sub-agent that loads the tech-scout skill (Skill tool: skill='fx-research:tech-scout') to:
- Discover how similar features are commonly implemented
- Identify relevant libraries, APIs, or standards
- Find best practices and anti-patterns
Skip only when the feature is purely internal with no new technology.
1.3 Web Discovery
Use WebSearch to find:
- How other products implement similar features
- Relevant RFCs, standards, or specifications
- Community discussions about tradeoffs
1.4 Existing Specs and Changes
Read all existing specs and changes to understand the current documentation landscape:
ls docs/specs/ docs/changes/ 2>/dev/null
cat docs/index.yml 2>/dev/null
Check if a spec already exists for this area. If so, this is an update, not a creation.
1.5 Synthesize Research
Before proceeding, compile a mental model of:
- What exists in the codebase today (actual behavior, not aspirational)
- What patterns and conventions must be followed
- What external patterns and best practices apply
- What the key design decisions and tradeoffs are
- What existing specs cover and what gaps remain
Phase 2: Mode Selection
Determine what work is needed:
A) New Spec — No spec exists for this system/feature area. Go to Phase 3.
B) Update Existing Spec — A spec exists but is outdated or incomplete. Go to Phase 4.
C) Spec + Changes — User wants to define desired behavior (spec) AND plan implementation work (changes) to get there. Go to Phase 3 or 4, then Phase 5.
When the mode is ambiguous, use AskUserQuestion:
AskUserQuestion:
question: "A spec already exists at docs/specs/<name>/. What would you like to do?"
options:
- label: "Update the spec to match current implementation"
description: "Audit the code and update the spec to reflect reality"
- label: "Update the spec AND propose changes"
description: "Update the spec to the desired state, then create change documents for unimplemented parts"
- label: "Only propose changes"
description: "Keep the spec as-is and create change documents for new work"
Phase 3: Create New Spec
3.1 Create Spec Directory
mkdir -p docs/specs/<spec-name>
Where <spec-name> is a brief, descriptive kebab-case name (e.g., user-authentication, payment-processing, notification-system).
3.2 Write the Spec
Read the template at references/spec-index-template.md and write docs/specs/<spec-name>/index.md.
Critical rules:
- Describe the system as it currently exists in the codebase. If the feature doesn't exist yet, describe the desired behavior and note it as unimplemented.
- Use RFC 2119 language for all requirements.
- Include GIVEN/WHEN/THEN scenarios for every requirement.
- Include actual code snippets from the codebase where they clarify the design.
- Verify every claim against the actual code — do not guess or assume.
- NO task lists in specs. Tasks belong in change documents.
- Initialize the Changelog with a creation entry.
3.3 Scope Analysis
CRITICAL: Do NOT create a single spec for an entire application. Break the work into multiple specs by major feature area.
A single spec MUST:
- Cover one cohesive feature or domain (e.g., "authentication", "file-upload", "password-protection")
- Be readable in one sitting
- Have clear boundaries that don't overlap with other specs
Spec organization for a new project:
- Architecture/setup spec (optional) — Covers project-level concerns: tech stack, directory structure, deployment topology, dev workflow. Does NOT contain feature requirements.
- Feature specs (one per major feature) — Each covers a distinct capability of the system. Change documents reference the feature spec they implement.
Example: For a file hosting app, create separate specs:
architecture — Tech stack, project structure, deployment
file-serving — HTTP serving, Content-Type, directory indexes
authentication — IAP verification, user identity
file-upload — Upload API, zip extraction, ownership
password-protection — Password middleware, cookie auth, prompt page
management-ui — React SPA, file browser, upload UX
NOT one giant spec covering the entire application.
When the user asks to "write a spec for X" where X is a whole application, decompose it into feature specs. Use AskUserQuestion to confirm the breakdown if unsure.
Bootstrapping change documents (project scaffolding, initial setup) are appropriate and SHOULD reference the architecture spec.
3.4 Supplementary Documents
For complex specs, create additional files in the spec folder:
api-reference.md — Detailed API documentation
data-models.md — Schema definitions and type hierarchies
architecture.md — Detailed architecture diagrams and explanations
The index.md MUST link to all supplementary documents.
Phase 4: Update Existing Spec
4.1 Deep Implementation Audit
This is the most critical step. Explore the codebase exhaustively to find every divergence between the spec and the actual implementation:
Launch multiple Explore sub-agents in parallel targeting:
- Every file referenced in the current spec
- Every module, API endpoint, data model mentioned
- Test files that verify the behaviors described
- Recent git commits that may have changed behavior
4.2 Gap Analysis
Categorize findings:
- Spec is accurate — Implementation matches spec. No action needed.
- Spec is outdated — Implementation changed but spec wasn't updated. Update the spec.
- Spec is aspirational — Spec describes behavior that doesn't exist yet. Either:
- Remove from spec (if no longer desired)
- Keep in spec and create a change document to implement it
- Undocumented behavior — Implementation has behavior not in the spec. Add to spec.
4.3 Update the Spec
Edit docs/specs/<spec-name>/index.md to reflect the current truth:
- Add missing requirements discovered during audit
- Update requirements that have changed
- Remove requirements for deprecated behavior
- Update code snippets to match current implementation
- Add new GIVEN/WHEN/THEN scenarios for undocumented behavior
4.4 Update Changelog
Add an entry to the spec's Changelog for every update:
| YYYY-MM-DD | <Description of what changed> | [NNNN-change-name](../../changes/NNNN-change-name.md) |
If updating to match existing implementation (no change document), use — for the document link.
Phase 5: Propose Change Documents
For every gap between the spec (desired state) and the implementation (current state), create change documents in docs/changes/.
5.1 Determine Change Numbering
ls docs/changes/ 2>/dev/null | sort -n | tail -1
Start at 0001 if none exist. Increment from the highest existing number. Zero-pad to 4 digits.
5.2 Scope Each Change Document
Each change document SHOULD be:
- Focused — One coherent set of related modifications
- Small — Implementable in 1-3 PRs (aim for smaller, more focused PRs)
- Independent — Minimally dependent on other changes (note dependencies when they exist)
Split large efforts into multiple change documents. It is BETTER to have many small, focused changes than few large ones.
5.3 Write Change Documents
Read the template at references/change-template.md and write each change to docs/changes/NNNN-<name>.md.
Critical rules:
- Every change MUST reference the spec it relates to
- MUST NOT restate behavior the spec already owns — open the requirements section by naming the owning spec section, then list only what the change itself owns (sequencing, migrations, registration, dispatch-path moves, bugfixes). See "Single-Owner Contracts" above. This is the single most important rule for keeping a corpus reviewable.
- Use RFC 2119 language for the requirements the change genuinely owns
- Include GIVEN/WHEN/THEN scenarios ONLY for behavior this change introduces that no spec owns — never copy a spec's scenarios
- Include a detailed
## Tasks section with checkbox items
- Every capability the requirements mandate MUST appear in a task; a change that requires infrastructure later changes depend on cannot be markable complete without a task for it
- Each top-level task SHOULD map to one PR
- Include design decisions with rationale
- Be specific about files to modify, APIs to change, tests to write
- NEVER duplicate change document tasks into
docs/tasks.md — tasks belong in the change document that defines them. docs/tasks.md is ONLY for work not tied to any change document.
- MANDATORY
### Testing Requirements subsection — Every change document's ## Requirements section MUST open with a ### Testing Requirements subsection that restates the project's standing, merge-blocking testing rules in tight bullet form. See 5.3.1 below.
5.3.1 Project-Specific Testing Requirements (mandatory)
Every change document MUST include a ### Testing Requirements subsection as the first item under ## Requirements. This is not optional and applies to every change, no matter how small. Skipping this subsection is a defect in the change document.
Source the rules from the target project, not from this skill. Before writing the subsection:
- Look for the project's testing conventions. Common locations, in order of preference:
- The architecture spec's Testing / Development Conventions section (e.g.,
docs/specs/architecture/index.md#testing)
- A dedicated
docs/specs/<testing-or-quality-spec>/
CONTRIBUTING.md or docs/contributing.md
AGENTS.md or REVIEW.md at the repo root
- Extract the actual project rules. These will differ per project. Examples of the kinds of rules you might find:
- Coverage thresholds (total, per-diff, or none)
- Required frameworks (e.g.,
pytest, go test, vitest)
- Isolation rules (e.g., "integration tests MUST use real Postgres via containers", or conversely "unit tests MUST mock all I/O")
- Race/concurrency flags (e.g.,
-race in Go, asyncio strict-mode in Python)
- Allowed suppression pragmas and their justification rules
- E2E / contract / snapshot policies
- Phrase the subsection to reference the source. The first sentence MUST link to the exact section in the project documentation where the rules live. This makes the change doc self-healing when rules evolve.
- Use tight bullets. Each rule is one line. RFC 2119 language where appropriate.
- State the consequence. Close with a one-line reminder that weakening any rule to land the PR is a defect in the PR, not in the rule.
Never invent rules or copy rules from a different project. If a project has no documented testing conventions:
- State this explicitly in the change doc's Testing Requirements as a known gap.
- Propose a minimal defensible baseline (e.g., "new behavior MUST have tests; CI MUST execute the suite; failing tests MUST block merge").
- Add an Open Question recommending the project codify a conventions document.
Minimal shape (language-agnostic):
### Testing Requirements
This change MUST satisfy the project's standing testing rules (see [<Section Name>](<link>)). CI enforces these as merge gates:
- <rule 1 from the project>
- <rule 2 from the project>
- <rule 3 from the project>
Skipping or weakening any of these rules to land the PR MUST be treated as a bug in the PR, not in the rule.
Examples — do NOT reuse verbatim across projects. Each is specific to its project's conventions:
- Go service with strict coverage: "100% diff-scoped coverage", "integration tests MUST use testcontainers-go — mocking Postgres is forbidden", "
go test ./... -race MUST pass", "//nolint MUST carry a rule name and justification"
- TypeScript library: "Every exported function MUST have a unit test", "
vitest run --coverage MUST pass with coverage at or above the project's configured threshold", "no .only or .skip in committed tests"
- Python data pipeline: "
pytest MUST pass on the full suite", "new SQL transformations MUST ship with a dbt test", "type check (mypy --strict) MUST pass"
The rules you write MUST come from the project you're operating in, discovered via step 1 above. If you didn't read the project's conventions, you aren't ready to write the Testing Requirements subsection.
5.4 Exhaustive Coverage
This is where thoroughness matters most. For each change document:
- Explore deeply — Launch Explore sub-agents to understand every file that will be touched
- Detail every task — Include specific file paths, function names, test scenarios
- Consider edge cases — What happens on error? Under load? With invalid input?
- Include test tasks — Every behavioral change MUST have corresponding test tasks
- Note dependencies — If this change depends on another, note it explicitly
Phase 6: Update Indexes
After creating or modifying specs and changes, update both index files. Index files MUST strictly follow their templates — no ad-hoc columns, renamed fields, or alternative status values.
6.1 Update docs/index.yml
Read the template at references/docs-index-yml-template.md. The template defines the exact field names, types, and allowed values. Add/update entries for every new or modified spec and change.
Strict rules:
- Use field names EXACTLY as defined:
name, path, description, status for specs; id, name, path, description, spec, status, depends_on for changes
- ALL fields are REQUIRED — never omit
description, name, or depends_on
- Status values MUST be lowercase:
active/deprecated for specs, draft/in-progress/complete for changes
- NEVER use alternative status values like
proposed, current, Proposed, Current
6.2 Update docs/index.md
Read the template at references/docs-index-md-template.md. The template defines the exact table columns and order. Add/update the tables to include every new or modified spec and change.
Strict rules:
- Specs table columns MUST be:
Spec | Description | Status — no renaming to "ID", "Name", etc.
- Changes table columns MUST be:
# | Change | Spec | Status | Depends On — no renaming, no extra columns
- Status values MUST be lowercase:
active/deprecated for specs, draft/in-progress/complete for changes
- The
Spec column in both tables MUST be a markdown link, not plain text
- The
Description column MUST be filled — never leave it empty or omit it
Phase 7: Verification
After writing each spec and change document, verify against the codebase:
- Code references — Confirm that any files, functions, types, or modules mentioned actually exist
- API patterns — Verify that proposed or documented APIs follow existing conventions
- Schema accuracy — Confirm database tables, columns, and types match reality
- Component patterns — Verify UI components exist and follow project conventions
- Import accuracy — Confirm libraries and modules referenced are available
- Scenario testability — Every GIVEN/WHEN/THEN scenario should be directly implementable as a test
For each discrepancy:
- Fix the spec/change directly if the correction is clear
- Add an Open Question if the right approach is ambiguous
Output
After completing all phases, report:
- Specs created or updated (with paths)
- Change documents created (with paths and one-line summaries)
- Key findings from the implementation audit (if updating)
- Open questions that need user input
- Suggested next steps:
- "Changes ready for implementation via
/dev or /team"
- "Spec has open questions that need resolution first"
- "Spec updated to match current implementation — no changes needed"