Netspective Logo
Spec-Driven Development with OpenSpec

The Artifacts and Their Grammar

What lands on disk and who owns each half, the exact headings a specification needs, the four delta operations, and the two configuration files.

Every file OpenSpec produces is plain Markdown or YAML, in your own repository, reviewable in a pull request like anything else. This page is what each one is for, and the exact shape each has to take.

4.1 What lands on disk

What OpenSpec puts on your disk, and which half of it is yours

FIGURE 11 — What lands on disk, and who owns each half. The split matters on upgrade day: one side is rewritten wholesale, the other is never touched.

openspec init creates exactly four directories and one file:

openspec/
openspec/specs/
openspec/changes/
openspec/changes/archive/
openspec/config.yaml

Everything else appears as you work. The generated instruction files for your AI assistants land in each tool's own directory — .claude/, .cursor/, .gemini/ and so on — and those are the half that openspec update replaces wholesale.

Avoid: hand-editing anything under a tool's skills or commands directory. Those files are generated, and the next openspec update overwrites them without asking. If you want different output, fork the schema and edit its templates instead.

Note — two files you may have read about elsewhere are legacy. openspec/project.md and openspec/AGENTS.md belonged to an older version. Modern OpenSpec actively cleans up the second and never creates the first; the context: field in config.yaml replaced them. If you are following an older tutorial, that is the difference you have hit.

Commit the whole openspec/ directory to git. Specifications that live outside version control drift from the code they describe, and then nobody trusts either.

4.2 The main specification format

A file at openspec/specs/<capability-path>/spec.md. Two headings are required, and the parser throws if either is missing.

# Draft Publishing Specification

## Purpose
Files each generated article as a draft for a person to approve, so that
nothing this pipeline produces can reach the public unattended.

## Requirements

### Requirement: Draft-only publication
The system MUST NOT make any article publicly visible. It SHALL create
every article in an unpublished state.

#### Scenario: A finished article is filed
- **WHEN** the writer produces a contract-valid article
- **THEN** it is created with status Draft
- **AND** it is not publicly visible

Four rules govern that file, and three of them fail loudly:

  • ## Purpose is required — "Spec must have a Purpose section" — and under 50 characters it is reported as too brief.
  • ## Requirements is required — "Spec must have a Requirements section".
  • A requirement header is ### Requirement: <name>, and names must be unique within the file.
  • A scenario header is exactly four hashes. Three fails silently: the scenario is simply not counted, and the requirement then fails the "must have at least one scenario" check for a reason that is not obvious from looking at it.

A main specification must never contain a delta operation header. After a merge, every requirement lives under the single ## Requirements section.

4.3 The delta format

The four delta operations, what each one looks like, and exactly what archiving does with it

FIGURE 12 — The four delta operations, and what archiving does with each. A delta says what is changing, not what the system is. This is the grammar that makes brownfield work clean.

A delta lives at changes/<name>/specs/<capability-path>/spec.md and uses four level-two headings:

HeadingMeansWhat archiving does
## ADDED RequirementsNew behaviourAppends the requirement to the main spec
## MODIFIED RequirementsChanged behaviour — must carry the whole blockReplaces the existing block entirely
## REMOVED RequirementsDeprecated — give a **Reason** and a **Migration**Deletes it from the main spec
## RENAMED RequirementsA header rename only, in FROM:/TO: formApplied before MODIFIED

## Purpose may also appear, but only on a delta introducing a brand-new capability — it seeds the Purpose of the main spec that archive creates. On an existing capability it is ignored, because that spec already has one.

The rename form is exact:

## RENAMED Requirements

- FROM: `### Requirement: Old name`
- TO: `### Requirement: New name`

The MODIFIED rule is the one that bites. A MODIFIED block is not a patch — it replaces the whole requirement, so it must repeat every scenario that survives the change, including the ones you did not touch. Both openspec validate and openspec archive refuse a block that would silently drop one, and both name the missing scenario in the error. This is demonstrated on a real change in day two.

Two placement rules are worth knowing before you hit them: a spec.md sitting directly at the specs/ root is ignored when the change is applied or archived, so a delta must live under a capability directory; and a stray ### header that is not a ### Requirement: is skipped by the reader and reported as an informational note rather than silently swallowed.

4.4 The proposal

proposal.md requires two headings — ## Why and ## What Changes — and the template adds two more:

## Why
Editorial staff miss items because nobody remembers to check six sites
every morning, and writing from scratch is slower than correcting a draft.

## What Changes
- Watch a configured set of sources on a schedule
- Draft one synthesised article per run
- File it as a Draft, never published automatically
- Notify a person when a run fails

## Capabilities

### New Capabilities
- `source-collection`: find new items; discard ones already seen
- `article-drafting`: produce one contract-valid article per run
- `draft-publishing`: create the article unpublished
- `run-notification`: tell a person when a run fails

### Modified Capabilities
(none — nothing exists yet to modify)

## Impact
- New service, new deployment target, one new service account

The Capabilities section is the load-bearing one. It is the contract between the proposal phase and the specifications phase: each capability named there obliges exactly one delta spec file. Getting it wrong here means the specs phase writes the wrong files. This is traced through on a real change in specs and deltas.

## Why must be at least 50 characters and should not exceed 1000.

4.5 The design

design.md is optional and has four sections in its template — ## Context, ## Goals / Non-Goals, ## Decisions, ## Risks / Trade-offs — with ## Migration Plan and ## Open Questions named in the instruction as well.

The section that earns its keep is Decisions, because the schema instruction asks for the alternatives considered against each one. That is what turns the file from a description into a record: not "we chose X" but "we chose X over Y and Z, for this reason".

Note — OpenSpec's own instruction is strict about Open Questions: they are for genuinely deferrable unknowns, not decisions you skipped. If a question would change the specifications, the approach, or the task breakdown, it must be resolved now rather than parked.

4.6 The tasks

## 1. Setup

- [ ] 1.1 Create the module structure
- [ ] 1.2 Add dependencies to the manifest

## 2. Core implementation

- [ ] 2.1 Implement the collection step

The checkbox form is load-bearing. The apply phase parses - [ ] X.Y to track progress; a task written any other way is not tracked. Group related tasks under numbered ## headings, keep each small enough for one session, and order them by dependency.

4.7 The two configuration files

openspec/config.yaml is per project:

FieldPurpose
schemarequired — which workflow to use, e.g. spec-driven
contextInjected into every artifact instruction, wrapped in <context>. 50 KB limit
rulesPer-artifact rules, wrapped in <rules>, applied only to the artifact that names them
operationsAdvisory guidance for the apply and archive operations
storeA declared default store — a fallback root, never an override
referencesStores this repo draws on, read-only

.openspec.yaml sits inside a change folder and carries that change's own metadata: schema (required), created (YYYY-MM-DD), an optional goal and affected_areas, and two escape hatches.

skip_specs: true declares that a change intentionally has no spec deltas — a pure refactor, tooling, or documentation. Validation rejects a zero-delta change without it. Setting it while delta files exist is itself an error, and so is setting it in metadata the parser cannot read: the tool fails closed rather than honouring a marker it could not verify.

retire_capabilities: true permits archive to delete a capability's main spec when its last requirement is removed. Without it, a removal that would empty a spec stops and reports the blocking condition rather than deleting the file.

4.8 Custom schemas

If the four built-in artifacts do not fit how your team works, fork them:

openspec schema fork spec-driven my-flow
openspec schema validate my-flow --verbose
openspec schema init my-flow --artifacts proposal,specs,review,tasks

A schema.yaml declares a name, a version, an optional description, an artifacts list, and an optional apply block. Each artifact declares id, generates (a relative path or glob — absolute paths and .. are rejected), description, template, an optional instruction, and requires.

Because templates are ordinary Markdown files, editing one changes what the AI produces on the very next run, with no rebuild.

How is this guide?

Last updated on

On this page