---
title: Spec-driven planning files (schema 1.0.0)
description: "This is the file-level reference for spec-driven mode: what the planr-spec, planr-plan, and planr-ship skills write under .planr/specs/, and what the openplanr…"
url: https://openplanr.dev/docs/reference/spec-schema
updated: 2026-10-29
related: []
---

# Spec-driven planning files (schema 1.0.0)

This is the file-level reference for spec-driven mode: what the `planr-spec`,
`planr-plan`, and `planr-ship` skills write under `.planr/specs/`, and what the
`openplanr spec` commands validate. The canonical JSON Schemas live in
[`packages/protocol/schemas/v1.0.0`](https://github.com/openplanr/OpenPlanr/tree/main/packages/protocol/schemas/v1.0.0);
the CLI and the `planr-pipeline` package read the same contract with no
conversion layer.

When authoring these files by hand, use the templates below verbatim.

> **Schema version:** `1.0.0`
> **Pinning rule:** the `schemaVersion` field on every artifact MUST match the
> reader's expected version. Breaking changes start in the Protocol schema source
> and are mirrored here before release.

---

## Directory layout

Every spec is a self-contained directory under `.planr/specs/`:

```
.planr/specs/SPEC-NNN-{slug}/
├── SPEC-NNN-{slug}.md            # the functional spec (one per directory)
├── design/                       # optional — UI mockups + design-spec
│   ├── *.png                     # PNG mockups attached via `openplanr spec attach-design`
│   └── design-spec.md            # written by the designer agent
├── stories/
│   └── US-NNN-{slug}.md          # user stories scoped to this spec
├── tasks/
│   └── T-NNN-{slug}.md           # tasks scoped to this spec
├── qa-report.md                  # written by the QA agent after planr-ship
├── error-report.md               # written if a task fails 3 iterations
└── .pipeline-shipped             # written by planr-ship — proof of execution
```

**ID scoping rule:** in spec-driven mode, `US-NNN` and `T-NNN` are scoped to
their parent SPEC, **not project-globally unique**. Two specs can each have
their own `US-001`. Disambiguate via path or via the `specId` frontmatter field.

---

## SPEC frontmatter

```yaml
---
id: "SPEC-001"                    # required · format: SPEC-\d{3}
title: "User authentication"      # required · human-readable title
slug: "auth"                      # required · URL-safe lowercase, used in path
schemaVersion: "1.0.0"            # required · pinned to reader version
status: "pending"                 # required · pending | shaping | shaped | decomposing | decomposed | in-pipeline | done
priority: "P0"                    # required · P0 | P1 | P2 | P3
milestone: "v1.0"                 # optional · links to a milestone or release
po: "product-owner"               # optional · spec owner (Product Owner)
created: "2026-04-26"             # required · ISO date (YYYY-MM-DD)
updated: "2026-04-26"             # required · ISO date, bumped on edit
ui_files: []                      # required · array of PNG paths under design/ (empty if none)
tech_dependencies: []             # required · array of strings; informational only
---
```

### Field meanings

| Field | Type | Meaning |
|---|---|---|
| `id` | string | Project-globally unique. Format `SPEC-NNN` (3-digit). The directory MUST be `SPEC-NNN-{slug}/`. |
| `title` | string | Display title. Human-readable. |
| `slug` | string | URL-safe lowercase. Used in the directory name and in story and task slugs. |
| `schemaVersion` | string | Pinned schema version. `1.0.0` currently. Readers MUST refuse mismatched versions. |
| `status` | enum | Lifecycle marker: `pending` (just created) → `shaping` (questions in progress) → `shaped` (body authored) → `decomposing` (planning in progress) → `decomposed` (stories and tasks written) → `in-pipeline` (ship in progress) → `done` (shipped). |
| `priority` | enum | `P0` (must) / `P1` (should) / `P2` (nice) / `P3` (defer). |
| `milestone` | string? | Optional release/milestone tag. |
| `po` | string? | Optional Product Owner identifier (email or username). |
| `created` / `updated` | string | ISO 8601 date. Bumped automatically by `openplanr spec` commands. |
| `ui_files` | array | List of PNG file paths under `design/`. Non-empty lists route the designer agent. |
| `tech_dependencies` | array | Free-form list of upstream tech dependencies. Informational; not consumed automatically. |

### SPEC body sections (in order)

The spec body uses the following H2 sections. `openplanr spec shape` writes them
from interactive questions; the `planr-plan` skill reads them.

1. **`## Context & Goal`** — 2-5 sentences on the user need + outcome
2. **`## Functional Requirements`** — bullet list, action verbs
3. **`## Business Rules`** — constraints and validations
4. **`## User Flows`** — numbered step-by-step flows
5. **`## Out of Scope`** — explicit non-goals
6. **`## Acceptance Criteria`** — Given/When/Then bullets
7. **`## Notes for Decomposition`** *(optional)* — hints for planning, NOT requirements

The `## Notes for Decomposition` section is freeform prose hinting at the
intended story split, special attention areas, or files to preserve. The
`planr-plan` skill reads it to bias its output. Example:

```markdown
- Suggested US split: auth flow (UI), session management (Tech)
- Special attention: the delete flow needs a confirmation dialog
- Preserve: do not modify the existing UserService
```

---

## Story frontmatter

Stories live at `.planr/specs/SPEC-NNN-{slug}/stories/US-NNN-{story-slug}.md`.

```yaml
---
id: "US-001"                      # required · format: US-\d{3} (scoped to parent SPEC)
title: "Login form"               # required
specId: "SPEC-001"                # required · MUST match parent spec id
slug: "login-form"                # required · URL-safe; matches filename suffix
schemaVersion: "1.0.0"            # required
status: "pending"                 # required · pending | in-progress | done
priority: "P0"                    # required · P0 | P1 | P2 | P3
created: "2026-04-26"             # required
updated: "2026-04-26"             # required
---
```

### Story body sections

```markdown
# US-001 — Login form

> **Spec:** SPEC-001

## Story
As a **<role>** I want **<action>** so that **<benefit>**.

## Scope
<2-3 sentences describing what's in scope for this story>

## Acceptance Criteria

- [ ] Given <context>, when <action>, then <observable outcome>.
- [ ] Given …, when …, then …

## Dependencies
<Optional: tasks or stories this depends on, by id>
```

---

## Task frontmatter

Tasks live at `.planr/specs/SPEC-NNN-{slug}/tasks/T-NNN-{task-slug}.md`.

```yaml
---
id: "T-001"                       # required · format: T-\d{3} (scoped to parent SPEC)
title: "Build login form component" # required
storyId: "US-001"                 # required · MUST match a story id under the same spec
specId: "SPEC-001"                # required · MUST match the parent spec id
slug: "login-form-component"      # required · URL-safe; matches filename suffix
schemaVersion: "1.0.0"            # required
type: "UI"                        # required · "UI" | "Tech"
agent: "frontend-agent"           # required · subagent that owns this task
status: "pending"                 # required · pending | in-progress | done
created: "2026-04-26"             # required
updated: "2026-04-26"             # required
---
```

### Type → agent mapping

| `type` | Default `agent` | Notes |
|---|---|---|
| `UI` | `frontend-agent` | UI-focused work: components, pages, styles |
| `Tech` | `backend-agent` | Backend, services, controllers, DTOs, migrations |

The `frontend-agent` reads tasks where `type: UI`; the
`backend-agent` reads tasks where `type: Tech`. Setting `agent` to a different
value (e.g. a custom subagent name) overrides the default routing.

### Task body sections

```markdown
# T-001 — Build login form component

> **User Story:** US-001
> **Spec:** SPEC-001
> **Type:** UI
> **Agent:** `frontend-agent`

## Objective
<1-2 sentences: what does this task accomplish?>

## Files

### Create
- `src/components/auth/LoginForm.tsx`
- `src/components/auth/LoginForm.test.tsx`

### Modify
- `src/app/login/page.tsx` (mount the component)

### Preserve
- `src/app/layout.tsx` (do not touch)
- `package.json` (do not add dependencies)

## Technical Spec
<Implementation detail: libraries, patterns, integration points.
The frontend or backend agent reads this verbatim.>

## Test Requirements
<Build / test commands and DoD checks. The qa-agent reads this to
verify completion.>

## Definition of Done
- [ ] Code compiles (`npm run build`)
- [ ] Tests pass (`npm test`)
- [ ] Lint clean
- [ ] All Create files exist
- [ ] All Modify files updated only as described
- [ ] All Preserve files unchanged (verified via `git diff`)
```

The **`### Create` / `### Modify` / `### Preserve`** lists are NOT optional —
the qa-agent verifies them via `git diff` before accepting the task as done.
A task that touches files outside these lists fails QA.

---

## `.pipeline-shipped` marker

Written by the `planr-ship` skill at the end of a successful (or
partially successful) run. This is the canonical proof that the work was
shipped through the workflow, not a hand-authored Markdown file.

```yaml
shipped_at: "2026-04-26T22:30:00Z"
pipeline_version: "0.4.0"
mode: "spec-driven"
feature: "auth"
tasks_executed: 3
tasks_failed: 0
qa_gate_status: "passed"
duration_seconds: 287
agents_invoked:
  - frontend-agent
  - backend-agent
  - qa-agent
  - devops-agent
  - doc-gen-agent
devops_status: "generated"
docs_status: "generated"
snapshot_status: "refreshed"
error_reports: []
```

If the marker is absent, the work was not shipped through the workflow. Audit
trails reference this file by path: `.planr/specs/SPEC-NNN-{slug}/.pipeline-shipped`.

---

## Status lifecycle (the full path)

A spec moves through these states, in order:

```
pending → shaping → shaped → decomposing → decomposed → in-pipeline → done
```

| State | Set by | Meaning |
|---|---|---|
| `pending` | `openplanr spec create` | Spec directory exists; body is the empty template |
| `shaping` | `openplanr spec shape` (in progress) | Q&A flow active |
| `shaped` | `openplanr spec shape` (complete) | Spec body has Context/FRs/Rules/AC sections filled |
| `decomposing` | `planr-plan` skill (in progress) | Stories and tasks are being written |
| `decomposed` | `planr-plan` skill (complete) | `stories/` and `tasks/` are populated |
| `in-pipeline` | `planr-ship` skill (in progress) | Implementation running |
| `done` | `planr-ship` skill (complete) | Tasks executed, QA passed, marker written |

---

## Schema version compatibility

The CLI, the skills, and the `planr-pipeline` package produce and consume schema
`1.0.0`. Breaking changes bump `schemaVersion` in lockstep. Keep them aligned:

```bash
npm install -g openplanr@latest
openplanr setup
openplanr upgrade status
```

---

## See also

- [`openplanr` package README](https://github.com/openplanr/OpenPlanr/blob/484d7322350865c78bdf4ac0be6e116c2af4c2b7/packages/cli/README.md)
- [CLI reference](https://openplanr.dev/docs/cli)
- [`planr-pipeline` package](https://github.com/openplanr/OpenPlanr/tree/main/packages/pipeline)
- [Pipeline rules](https://github.com/openplanr/OpenPlanr/blob/main/packages/pipeline/docs/rules.md)
