| name | product-spec |
| description | Generate docs/product-spec.md from discover-notes.md (or raw notes) against the locked schema. Auto-routes to greenfield (10 sections) or brownfield (11 sections) template based on context_type in discover-notes.md or cwd auto-detection. Use when the user has discovery notes ready and wants a schema-conformant spec written to disk. Trigger phrases: "write the spec", "generate spec", "create the spec from notes", "turn notes into a spec", "spec from discover-notes". Use AFTER /discover, not in place of it.
|
| argument-hint | [path-to-notes-file] |
| allowed-tools | ["Read","Write","Bash","AskUserQuestion","TaskCreate","TaskUpdate"] |
Product Spec: Generate docs/product-spec.md from discover-notes
This skill takes a discovery notes file and emits a docs/product-spec.md that conforms to the locked schema, routing every gap to ## Open Questions rather than inventing content.
The skill auto-routes to the correct template based on context_type in the input:
- greenfield → 10-section spec template (product built from scratch)
- brownfield → 11-section spec template (delta-change to an existing system)
The skill is a document generator, not a discovery facilitator. It NEVER invents domain decisions, business-logic rules, success criteria, or user stories. Anything missing in the input goes verbatim into ## Open Questions so a human can resolve it.
The locked schema this skill conforms to lives at ../discover/references/product-spec-schema.md (relative to this SKILL.md). Read it before generating any artifact and re-check the produced file against it before writing to disk.
When to use, when to skip
Use when: the user has run /discover (and docs/discover-notes.md exists with a checkpoint block), OR the user has a raw notes file they want turned into a spec draft, OR the user explicitly asks to (re-)generate docs/product-spec.md.
Skip when: the user is still ideating and has no notes — point at /discover first. Skip also when the user wants to edit an existing spec by hand — this skill writes whole files; surgical edits are out of scope.
Relationship to other skills
/discover — produces discover-notes.md, the canonical input. Always preferred upstream of this skill.
/setup — scaffolds the /docs skeleton. If docs/ is missing, /discover delegates to it; this skill expects the directory to already exist.
Initial Response
When this skill is invoked:
- If a path argument was provided (e.g.
/product-spec @notes/raw.md or /product-spec docs/discover-notes.md), capture it as the input path. Proceed to Step 1.
- If no argument was provided, default the input path to
docs/discover-notes.md and proceed to Step 1. Do not prompt yet — Step 1 handles the missing-input case.
Process
Step 1: Locate input
Resolve the input path:
- If an argument was passed, use it verbatim (strip a leading
@ if present).
- Otherwise, default to
docs/discover-notes.md.
Test the resolved path:
test -f "<resolved-path>"
If the file exists, read it FULLY (no limit/offset) and proceed to Step 1.5.
If the file does not exist, ask:
AskUserQuestion:
- question: "No input file found at
<resolved-path>. How would you like to proceed?"
header: "Input?"
options:
- label: "Run /discover first (Recommended)"
description: "Stop here. Run /discover to produce discover-notes.md, then re-invoke /product-spec."
- label: "Paste raw notes"
description: "I'll wait for you to paste any notes you have. The thin-input check will warn about missing signals."
- label: "Cancel"
description: "Exit without changes."
multiSelect: false
On "Run /discover first": print "Stopping. Run /discover to produce discover-notes.md, then re-invoke /product-spec." and STOP.
On "Paste raw notes": prompt "Paste your notes below. End with an empty line." and capture the user's text as the in-memory input. Proceed to Step 1.5 with that content.
On "Cancel": STOP without changes.
Step 1.5: Determine context type
Determine whether to generate a greenfield or brownfield spec:
-
If the input has context_type: in frontmatter — use that value directly. No confirmation needed.
-
If no context_type: in frontmatter (raw notes, pasted input) — auto-detect from cwd:
Use the same multi-signal detection as /discover (Step 0.7): check for git history (Tier 1), lockfiles (Tier 2), manifest files (Tier 3), and bonus signals (source dirs, framework configs). Any Tier 1 or Tier 2 hit → propose brownfield. Tier 3 only → propose brownfield with ambiguity flag. No signals → propose greenfield.
Confirm with the user:
AskUserQuestion:
- question: "No context_type found in the input. Based on cwd markers, this looks like [greenfield|brownfield]. Correct?"
header: "Context"
options:
- label: "[Detected mode] — correct (Recommended)"
description: "Generate a [greenfield|brownfield] spec."
- label: "[Other mode] — override"
description: "Generate a [other] spec instead."
multiSelect: false
Store the resolved context_type for use in Steps 2 and 3. Proceed to Step 2.
Step 2: Assess input
Score the input on a 0–4 shaped-vs-thin heuristic. Each signal contributes 1 point:
Greenfield signals:
- Frontmatter
checkpoint: block present — strongest signal that this came from /discover. Look for the literal checkpoint: key inside a YAML frontmatter fence at the top of the file.
- At least one FR-NNN-format requirement — grep for
^- FR-\d{3}: (bulleted line, three-digit zero-padded index, colon-space).
- At least one Given/When/Then block — grep for
\*\*Given\*\* AND \*\*When\*\* AND \*\*Then\*\* anywhere in the body.
- Explicit business-logic capture — a
## Business Logic section exists AND its first non-blank line is a single declarative sentence (heuristic: ≤ 200 chars, ends in ., not equal to # TODO: domain rule — see Open Questions and not blank/placeholder).
Brownfield signals (replace signal 1 when context_type: brownfield):
- Frontmatter
checkpoint: block present AND context_type: brownfield — strongest signal that this came from /discover in brownfield mode. Also check for ## Current System section in the body.
2–4. Same as greenfield.
Compute the total. Document the heuristic explicitly in the conversation so a future maintainer can tune it:
Input assessment (heuristic, 4 signals, 1 point each):
[✓|✗] Frontmatter checkpoint block — <found|missing>
[✓|✗] FR-NNN format requirements — <found N FRs|missing>
[✓|✗] Given/When/Then user stories — <found|missing>
[✓|✗] Explicit one-sentence business rule — <found|missing>
Score: <N>/4
Score ≥ 2: input is shaped enough; proceed to Step 3 silently.
Score < 2: trigger the thin-input warning. Name each missing signal explicitly (do NOT print a generic "your notes are thin" — name what's missing and why it matters):
This input scored <N>/4 on the shape heuristic. Missing signals:
- <signal name>: <one-line consequence for the generated spec>
- ...
A spec generated from thin input will have many `# TODO` placeholders and a long
`## Open Questions` section. That's a valid intermediate state, but if you have
time to run /discover first, the resulting spec will be substantially stronger.
Then ask:
AskUserQuestion:
- question: "How would you like to proceed?"
header: "Thin input"
options:
- label: "Run /discover first (Recommended)"
description: "Stop here. Use /discover to fill in the missing signals, then re-invoke /product-spec."
- label: "Proceed anyway"
description: "Generate the spec from what's there. Missing pieces land in ## Open Questions verbatim."
- label: "Cancel"
description: "Exit without changes."
multiSelect: false
On "Run /discover first": print the redirect message and STOP. On "Proceed anyway": continue to Step 3 with score < 2 recorded so later steps know to expect TODOs. On "Cancel": STOP.
Step 3: Generate Spec
Read the schema reference FULLY one more time (../discover/references/product-spec-schema.md) to confirm the field list and section names have not drifted.
Build the spec content in memory first (not on disk yet):
3a. Frontmatter
Populate every required frontmatter field per the schema:
project — extract from input frontmatter project: if present; otherwise from a Title heading (# <Project>); otherwise # TODO: project — see Open Questions.
version — 1 for the first spec this skill writes. The collision step (Step 4) bumps this if the user picks a versioned save.
status — draft. Never promote to reviewed/locked; that's a downstream decision.
created — today's date in YYYY-MM-DD (use Bash: date +%Y-%m-%d).
context_type — greenfield or brownfield (from Step 1.5).
product_type — pull from input if available; otherwise # TODO: product_type — see Open Questions (and add an Open Question entry).
target_scale, timeline_budget — same rule. If the input has the field, copy it verbatim; if not, emit # TODO: <field> — see Open Questions and add a matching Open Question. For brownfield, timeline_budget uses delivery_weeks instead of mvp_weeks.
Do NOT populate team_profile, tech_preferences, or deployment_constraint into spec frontmatter, even when the input notes carry them. Those fields are gathered by downstream tech-stack-selection (greenfield) or stack-assessment (brownfield) steps, not by the spec. If the input has them, summarize them into the Step 5 hand-off message under "forward to tech-stack/stack-assess" so the user knows the content is being routed, not silently dropped — but DO NOT emit them in spec frontmatter.
Field key names are load-bearing per the schema. Field values are not.
3b. Required sections (in schema order)
The section list depends on context_type:
Greenfield (10 sections):
Emit exactly these 10 ##-level headings, in this exact order (the schema's section-name contract is what downstream parsers split on):
## Vision & Problem Statement
## User & Persona
## Success Criteria (with ### Primary / ### Secondary / ### Guardrails)
## User Stories
## Functional Requirements
## Non-Functional Requirements
## Business Logic
## Access Control
## Non-Goals
## Open Questions
Brownfield (11 sections):
Emit exactly these 11 ##-level headings, in this exact order:
## Current System Overview — what exists now: key architecture, tech stack, user base. This section has no greenfield equivalent; it establishes the baseline that all subsequent sections describe changes against.
## Problem Statement & Motivation — what's wrong/missing, why now. Delta-framed: focuses on the gap between current state and desired state.
## User & Persona — who is affected (existing users + new if any). For brownfield, emphasize existing users whose experience changes.
## Success Criteria (with ### Primary / ### Secondary / ### Guardrails) — how we know the change worked. Guardrails should explicitly include existing behavior that must not regress.
## User Stories — what changes for the user. Delta-framed: Given/When/Then describes the new behavior, with explicit notes on what was different before.
## Scope of Change — what's being modified/added/removed. Explicit delta: categorize each item as new, modified, or removed. This replaces the implicit "everything is new" assumption of the greenfield ## Functional Requirements.
## Constraints & Compatibility — backward compatibility, data migration, existing integrations, preserved behavior. The brownfield-specific section that makes preservation explicit.
## Business Logic Changes — domain rule additions/modifications (not full domain model). If the change is infrastructure-only (no domain logic change), state that explicitly.
## Access Control Changes — permission changes if any. If no changes, state: "No access control changes."
## Non-Goals — what we're NOT changing. Critical for brownfield: explicitly names existing system aspects that are out of scope.
## Open Questions
Do NOT emit ## Data Model, ## Data Model Changes, ## Implementation Decisions, ## Testing Strategy, or ## Deployment & CI/CD sections in either mode — those concerns are not part of the spec schema. Entities and their lifecycles emerge from FRs and User Stories and are pinned during stack selection / implementation planning, not in the spec. If the input notes carry data-model or implementation content, summarize it into the Step 5 hand-off message under "forward to technical-roadmap" so the user knows it's being routed, not silently dropped — but DO NOT emit those sections in the spec.
Section content rules (both modes)
For each section:
- If the input has matching content — transcribe it faithfully into the section. Preserve user wording. Convert formatting only when the schema demands a specific shape (e.g., FR-NNN format, Given/When/Then for user stories, three-subsection Success Criteria). Do not rephrase, summarize, or "improve" the user's words.
- If the input has partial content — transcribe what's there, then close with
# TODO: <what's missing> — see Open Questions inside the section, and add a matching numbered entry under ## Open Questions.
- If the input has no matching content — emit just the heading plus
# TODO: <section name> — see Open Questions, and add a matching numbered entry under ## Open Questions.
If /discover recorded Socratic blockquotes under FRs, preserve them verbatim — they're load-bearing for downstream review.
If discover-notes.md carried a ## Quality cross-check block (from Step 7 of /discover), mirror each gap into ## Open Questions as a numbered entry naming the missing element and its consequence.
Brownfield-specific content rules:
- FRs with
Change: preserved become explicit preservation items in ## Scope of Change, not ## Non-Goals.
## Current System Overview maps from discover-notes' ## Current System section.
## Constraints & Compatibility maps from discover-notes' ## Constraints & Preserved Behavior section.
- Delta-framing convention: sections describe what changes, not the full system. "The auth model adds Google OAuth alongside existing email login" — not "The system supports email login and Google OAuth."
Hard rule — never invent: if the input does not contain a one-sentence business rule, the ## Business Logic / ## Business Logic Changes section MUST read # TODO: domain rule — see Open Questions and Open Questions MUST carry "What is the one-sentence business rule? — TBD by user. Block: yes (spec is hollow until resolved)." Do not write a placeholder rule. Do not "extrapolate" a rule from entity nouns appearing in FRs or User Stories. The whole point of this skill is to surface gaps, not paper over them.
Same rule applies to: success criteria, user stories, FR priorities, NFR targets, access control, non-goals. If it's not in the input, it goes to Open Questions.
3c. Pre-write self-review
Before any disk write, run a self-review pass against the schema's required-sections list AND a content-level lint for technical leak:
Structural checks:
- Parse the in-memory spec content. Extract every
## heading.
- Compare to the canonical section list for the active
context_type (10 for greenfield, 11 for brownfield). Verify ALL sections are present, in order, exact spelling. The spec must NOT contain ## Data Model or ## Data Model Changes — those sections were retired.
- Verify the frontmatter declares all required keys per the schema (
project, version, status, created, context_type, product_type, target_scale, timeline_budget).
- Verify
## Success Criteria contains ### Primary, ### Secondary, ### Guardrails subsections (or, if missing, that they're flagged as TODO with corresponding Open Questions entries).
Content-level lint for technical leak:
-
Scan all ##-level section bodies (excluding brownfield ## Current System Overview, where naming the existing stack is allowed) for tokens that indicate implementation detail has leaked into the spec. Treat each hit as a leak unless it is part of a verbatim user quotation explicitly being routed to Open Questions:
- Vendor / hosted-service names:
OpenRouter, Stripe, Auth0, Supabase, Firebase, Vercel, Cloudflare, AWS, GCP, Azure, OpenAI, Anthropic, etc. (any proper-noun product/service).
- Schema / ORM notation:
(FK), nullable, _hash, _at column suffixes presented as field lists, password_hash, cascade, soft-delete, hard-delete, migration, backfill.
- Runtime location:
client-side, server-side, on the edge, in the cache, in the worker.
- Enforcement mechanism:
per IP, per user-agent, token bucket, rate-limit per <axis>.
- UI affordance (when used to state an NFR, not a user story):
spinner, progress bar, streaming response, modal, toast.
- Transport / protocol:
WebSocket, gRPC, GraphQL, REST endpoint, webhook, SSE.
- Implementation verbs in domain rules: "the LLM does X", "the SRS library decides Y", "the database stores Z" (naming the component performing the rule, rather than stating the rule).
For each hit, emit a structured warning. Do NOT silently rewrite — abort the write so the user can see what leaked.
If any structural OR lint check fails, abort the write and report:
Spec generation self-review FAILED:
Structural:
- Missing section: <name>
- Out-of-order section: <name> (expected position N, found position M)
- Missing frontmatter key: <key>
- Retired section present: <name>
Technical leak (content lint):
- <section name>: "<offending phrase>" — <category, e.g. vendor name / schema notation / runtime location>
- ...
The spec was NOT written. For structural failures: the schema and the generator
have drifted — re-read ../discover/references/product-spec-schema.md and reconcile.
For leak failures: the input notes carry implementation detail that the spec does
not own. Either (a) rewrite the offending phrasings as outside-observable
properties / scope decisions and re-run, or (b) move the leaked content into
discover-notes' `## Forward: ...` blocks so a downstream skill consumes it.
Then STOP. Do not proceed to Step 4.
If all checks pass, proceed to Step 4 with the validated content in hand.
Step 4: Collision check
test -f docs/product-spec.md
If the file does not exist, write to docs/product-spec.md and proceed to Step 5.
If the file exists, ask:
AskUserQuestion:
- question: "docs/product-spec.md already exists. How would you like to proceed?"
header: "Collision"
options:
- label: "Save as product-spec-vN.md (Recommended)"
description: "Preserve history. The new spec lands at the next available product-spec-vN.md slot. The unversioned product-spec.md is unchanged."
- label: "Overwrite product-spec.md"
description: "Replace the existing product-spec.md. The prior version is lost (unless you've committed it)."
- label: "Abort"
description: "Exit without writes. No collision resolution."
multiSelect: false
On "Save as product-spec-vN.md": pick N by scanning docs/ for files matching product-spec-v*.md. Treat the unversioned product-spec.md as v1. The next slot is N = (max existing N or 1) + 1. Write the validated content to docs/product-spec-v<N>.md and bump the in-content version: frontmatter field to <N>. Proceed to Step 5.
On "Overwrite product-spec.md": write the validated content to docs/product-spec.md. Keep version: 1 (overwriting is a replacement, not a new version). Proceed to Step 5.
On "Abort": STOP without writes.
Step 5: Hand off
After the write lands, summarize what was produced:
═══════════════════════════════════════════════════════════
SPEC GENERATED
═══════════════════════════════════════════════════════════
Project: [project from frontmatter]
Context type: [greenfield | brownfield]
Path: [docs/product-spec.md | docs/product-spec-vN.md]
Schema sections: [10 / 10 | 11 / 11] present
Frontmatter: <K populated, M as TODO> (8 keys total)
Open Questions: <count> entries
Sections fully populated from input:
- <list of section names with non-trivial content>
Sections marked TODO (see Open Questions):
- <list of section names with TODO placeholders>
═══════════════════════════════════════════════════════════
If the input notes carried forward-looking concerns (tech-stack preferences, implementation notes, deploy hints), list them briefly so the user knows they're being routed, not dropped:
Forward to next step (not in spec):
• [one-line summary per detected item]
Skip the block entirely if the input didn't carry any of those.
STOP. Do not chain into another skill automatically.
Critical guardrails
-
Generator, not author. This skill writes whole files from inputs the user has already approved. It does not invent business logic, success criteria, user stories, or FR priorities. Missing content goes to ## Open Questions verbatim. The spec's ## Business Logic section is the single most-policed area: if there is no one-sentence rule in the input, the section reads # TODO: domain rule — see Open Questions. No exceptions.
-
Schema is the contract. ../discover/references/product-spec-schema.md defines frontmatter keys, section names, and section order. Re-read it at every invocation. Re-validate the in-memory spec against it in Step 3c before writing. Drift between this skill and the schema is the failure mode this skill exists to prevent.
-
Stack openness is binding — and broader than just stack names. The forbidden vocabulary in a generated spec covers seven categories, not just frameworks:
- Frameworks, databases, hosting platforms, specific libraries — the original rule.
- Vendor / hosted-service names — OpenRouter, Stripe, Auth0, Supabase, Firebase, Vercel, Cloudflare, AWS/GCP/Azure, OpenAI, Anthropic, and any other proper-noun product or service.
- Schema / ORM notation — field-level lists,
(FK), nullable, _hash columns, password_hash, cascade-delete, soft-delete, hard-delete, migration, backfill. (Entities surface naturally in FRs and User Stories; column-level schema is a downstream concern.)
- Runtime location —
client-side, server-side, on the edge, in the cache, in the worker. The spec describes what must be true at the product's outer boundary, not where in the stack it's enforced.
- Enforcement mechanism —
per IP, per user-agent, token bucket, rate-limit per <axis>. The NFR is the property; the mechanism is a downstream design choice.
- UI affordance in NFRs —
spinner, progress bar, streaming response, modal, toast. NFRs name the user-observable quality (e.g. "continuous feedback during long operations"); the affordance is downstream.
- Transport / protocol —
WebSocket, gRPC, GraphQL, REST endpoint, webhook, SSE. The spec describes information flow as the user experiences it, not the wire format.
Spec frontmatter is product-level only (product_type, target_scale, timeline_budget + metadata); language family, frameworks, deployment, team profile, and any technology avoid-list belong to downstream steps, NOT the spec. If the input contains forbidden vocabulary, leave it in discover-notes' ## Forward: ... blocks for the downstream step to consume — do NOT translate it into spec frontmatter or sections. Exception: brownfield ## Current System Overview may name the existing stack and vendors since it describes the current state, not a stack choice. Step 3c's content lint enforces this guardrail mechanically.
-
Collisions favor history. The collision prompt recommends versioned save (product-spec-vN.md) over overwrite. Lost prior versions are an unrecoverable failure mode; a duplicate file in docs/ is not.
-
Self-review aborts on drift. If the in-memory spec is missing a section, has a misordered section, or lacks a frontmatter key, the write is ABORTED — not patched up silently. The error names the specific drift so a maintainer can reconcile schema and skill.
-
Never chain automatically. The hand-off is an announcement, not an invocation. The user picks when (and whether) to run the next step. Auto-chaining would skip the human's review of the generated spec.
Notes
- This is a document generator skill. Output is
docs/product-spec.md (or product-spec-vN.md), period.
- The schema reference (
../discover/references/product-spec-schema.md) is the single source of truth. Any field name, section name, or frontmatter key referenced in this body MUST exist in the schema doc — if it doesn't, fix the schema doc first.
- The thin-input heuristic (Step 2) is intentionally conservative. False positives (warning on shaped input) are recoverable via the "Proceed anyway" override; false negatives (silently generating from thin input) produce hollow specs that mislead the user. Tune the heuristic toward warning more, not less.
- The
# TODO: <field-name> — see Open Questions pattern is load-bearing. Downstream tooling can grep for ^# TODO: to count unresolved gaps and decide whether the spec is review-ready.