Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions docs/concepts/complex-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@ independently specified sub-features. Each sub-feature gets its own
`spec.md`, `plan.md`, and `tasks.md`, and runs through its own
specify/plan/tasks/implement cycle.

This is the "spec of specs" approach: the first iteration breaks a massive
feature into smaller, self-contained specs that can each be implemented without
overwhelming the model. It adds the most overhead, so reserve it for features
that are too large to handle any other way.
This is the "spec of specs" approach: a first pass breaks a massive feature into
smaller, self-contained specs that can each be implemented without overwhelming the
model. It adds the most overhead, so reserve it for features that are too large to
handle any other way.

See [Spec of Specs](spec-of-specs.md) for the full procedure — how to run the
roadmap pass, structure the roadmap artifact, link sub-specs back to it, and a worked
example.

## Which Approach to Choose

Expand Down
171 changes: 171 additions & 0 deletions docs/concepts/spec-of-specs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Spec of Specs

When a feature is too large to run through a single
`/speckit.specify` → `/speckit.plan` → `/speckit.tasks` → `/speckit.implement`
cycle without the model losing track mid-implementation, you can break it into a
**roadmap** of smaller, independently-specified sub-features. This is the "spec of
specs" approach: one up-front pass decomposes a massive feature into self-contained
specs, and each of those runs through its own specify/plan/tasks/implement cycle.

> **When to reach for this.** Decomposition adds the most overhead of any strategy
> in [Handling Complex Features](complex-features.md). Use it **only when the lighter
> options there are insufficient** — first try limiting how many tasks run per
> `/speckit.implement` invocation, then sub-agent delegation, then a combination.
> Reach for a spec of specs only when even a single phase is too large to handle in
> one run.

The rest of this page describes *how* to do it with the tools you already have. No
new commands or extensions are required.

## The roadmap pass

Before writing any sub-spec, do a single decomposition pass to produce a roadmap.
Treat this as a lightweight planning conversation with your agent, not a full spec:

1. **State the whole feature.** Describe the large feature (the "epic") in a
sentence or two so the agent has the full picture up front.
2. **Identify independent slices.** Ask the agent to propose a small set of
sub-features that each deliver a coherent piece of the epic and can be specified
on their own. Aim for slices that are independently testable — implementing just
one should leave you with something demonstrable.
3. **Draw the boundaries.** For each slice, write one line of intent and an explicit
scope boundary (what is in, what is deferred to a sibling slice). Sharp
boundaries are what keep each sub-spec small enough to fit in context.
4. **Order by dependency.** Note which slices depend on others and sequence them so
prerequisites come first. Slices with no dependency on each other can be built in
any order. To build independent slices in parallel, use separate worktrees so each
run has isolated active-feature state.
5. **Record the result as a roadmap.** Capture the slices in a durable roadmap file
(below) so every later sub-spec can point back to it.

The roadmap is deliberately shallow: it names and orders the sub-features but does
**not** design them. The design happens when each slice runs through its own
`/speckit.specify`.

## The roadmap artifact

The roadmap is an ordinary Markdown file you author and keep under version control —
there is no special tooling behind it. Put it where the sub-specs can find it:

- For a feature-scoped epic: `specs/<epic-slug>/roadmap.md`.
- For a larger, cross-cutting epic: a top-level `ROADMAP.md`.

Each roadmap entry carries a stable id (used later for linking), a name, its intent,
its scope boundary, its dependencies, a status, and — once the sub-spec exists — a
link to it. A minimal template:

```markdown
# Roadmap: <epic name>

<One or two sentences: what the epic is and why it is being decomposed.>

**Status legend**: planned · in-progress · done

| ID | Sub-feature | Intent | Scope boundary | Depends on | Status | Sub-spec |
|----|-------------|--------|----------------|-----------|--------|----------|
| R1 | <name> | <one line> | <in / deferred> | — | planned | — |
| R2 | <name> | <one line> | <in / deferred> | R1 | planned | — |
| R3 | <name> | <one line> | <in / deferred> | R1 | planned | — |
```

Keep the `ID` column immutable once a sub-spec references it — it is the anchor for
traceability. Fill in the `Sub-spec` column with the path to each sub-feature's spec
directory as you create it, and update `Status` as work progresses.

## Specifying each sub-feature

With the roadmap in hand, work through the entries one at a time using the normal
Spec Kit flow — nothing new to learn:

1. Pick the next roadmap entry whose dependencies are already `done` (or have none).
2. Run `/speckit.specify` for just that slice, describing only its intent and scope
from the roadmap entry. Because the slice is bounded, its spec, plan, and tasks
stay well within the context window.
3. Run `/speckit.plan`, `/speckit.tasks`, and `/speckit.implement` for that slice as
usual.
4. Mark the roadmap entry `done` and move to the next one.

Each slice is a complete, independent Spec Kit feature with its own
`spec.md`/`plan.md`/`tasks.md`. The roadmap is what ties them together.

## Linking sub-specs to the roadmap

To keep scope and intent from drifting across separate runs, every sub-spec
references its roadmap entry, and the roadmap links back — a simple, greppable,
bidirectional convention:

- **Sub-spec → roadmap.** In the sub-feature's `spec.md`, name the parent roadmap
and entry id in the `Input` / summary line, for example:

```markdown
**Input**: Parent roadmap: `specs/<epic>/roadmap.md` → entry **R3**. <feature description>
```

- **Roadmap → sub-spec.** In the roadmap table, set the entry's `Sub-spec` column to
the sub-feature's directory, e.g. `specs/<epic>-part-3/`.

Because both directions are plain text, you can trace any sub-spec back to its place
in the epic (and find its siblings) with a quick search — no tooling, no metadata
schema.

## Keeping the roadmap and sub-specs in sync

The roadmap is a living document. As you learn more, keep it and the sub-specs
aligned:

- **Roadmap first, then reconcile.** When scope shifts, update the roadmap entry
first, then update any sub-specs it affects. The roadmap is the source of truth for
how the epic is divided.
- **Respect dependencies and ordering.** If a slice depends on another, build the
prerequisite first and cross-reference the dependent sub-spec so the relationship
is visible from both sides.
- **Recurse when a slice is still too big.** If a sub-feature turns out to be too
large to specify in one cycle, give it its own roadmap and decompose it further —
the same approach applies one level down. Recursion adds overhead, so only go as
deep as the context problem actually requires.

## Worked example

Suppose the epic is **"Add a self-service billing portal"** — far too large for a
single cycle. The roadmap pass breaks it into three independently-specifiable
slices.

`specs/billing-portal/roadmap.md`:

```markdown
# Roadmap: Self-service billing portal

Let customers view invoices, manage payment methods, and change plans without
contacting support. Too large for one cycle, so it is split into independent slices.

**Status legend**: planned · in-progress · done

| ID | Sub-feature | Intent | Scope boundary | Depends on | Status | Sub-spec |
|----|--------------------|------------------------------------------|---------------------------------------------|-----------|---------|----------|
| R1 | Invoice history | Customers view and download past invoices | Read-only; no payment actions | — | done | specs/billing-invoices/ |
| R2 | Payment methods | Add, remove, and set a default card | No plan changes; assumes invoices exist | R1 | in-progress | specs/billing-payment-methods/ |
| R3 | Plan changes | Upgrade/downgrade the subscription plan | Uses R2's default payment method | R1, R2 | planned | — |
```

Each slice is then specified on its own. For example, the **R2** sub-feature's
`spec.md` opens with a back-reference:

```markdown
# Feature Specification: Billing — payment methods

**Input**: Parent roadmap: `specs/billing-portal/roadmap.md` → entry **R2**.
Let customers add, remove, and set a default payment method in the billing portal.
```

From here a reader can trace **R2** back to the roadmap, see that it depends on
**R1** (invoice history, already `done`), and see that **R3** (plan changes) is
waiting on it. Building R1, then R2, then R3 keeps every run small while the roadmap
preserves the shape of the whole epic.

## For automation (optional)

If you would rather automate roadmap capture and consistency checks than maintain
the file by hand, the community-maintained
[Spec Roadmap extension](https://github.com/srobroek/speckit-roadmap) explores that
direction. It is a third-party extension and is not required — the manual convention
above is enough on its own.
2 changes: 2 additions & 0 deletions docs/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
href: concepts/spec-persistence.md
- name: Handling Complex Features
href: concepts/complex-features.md
- name: Spec of Specs
href: concepts/spec-of-specs.md

# Development workflows
- name: Development
Expand Down