OpenSpec artifact shape
OpenSpec (Fission-AI/OpenSpec) is a spec-driven workflow that keeps a project’s accepted behavior in Markdown under an openspec/ directory. This page documents the precise artifact shape the Design lens can render: the directory layout, the requirement grammar, the change lifecycle, and which parts are reliably structured versus freeform prose. The corpus of record is this repo’s own openspec/ tree; upstream docs fill in the CLI and validation rules.
Directory layout
Section titled “Directory layout”openspec init creates one openspec/ directory at the project root. Two subtrees carry the substance:
openspec/ config.yaml # schema: spec-driven specs/ # the accepted, current truth <capability>/ spec.md # one capability = one spec file changes/ # proposed, not-yet-promoted work <change-name>/ proposal.md # why + what changes design.md # technical approach (optional) tasks.md # implementation checklist specs/ <capability>/ spec.md # delta against the capability archive/ <YYYY-MM-DD-change-name>/ # completed changes, datedTwo facts a lens must model:
specs/is the current state;changes/is the diff against it. A capability underspecs/<capability>/spec.mdis a full, self-contained document. A capability underchanges/<change>/specs/<capability>/spec.mdis a delta — it only contains the requirements being added, modified, or removed, never the full spec.- Archiving promotes deltas and moves the change. When a change is archived, its deltas are merged into
specs/and the change directory is moved tochanges/archive/<date>-<name>/. Archived changes retain their original proposal/design/tasks/deltas as a historical record.
Upstream (openspec init) also scaffolds AI-guidance files — a root AGENTS.md/project.md describing conventions — but those are guidance, not part of the spec corpus a lens renders. This repo’s corpus carries only config.yaml (schema: spec-driven) plus specs/ and changes/.
Requirement grammar
Section titled “Requirement grammar”A capability spec.md is a flat Markdown document with a fixed heading spine. From openspec/specs/harness-discovery/spec.md:
# Harness discovery specification
## PurposeDefine how Rennet finds and probes model harnesses ...
## Requirements### Requirement: Discovery resolves the harness without asking a shell to resolve a binaryDiscovery SHALL harvest the login-shell PATH ... and SHALL NOT use `which` ...
#### Scenario: The GUI-inherited PATH omits the real location- **WHEN** the login-shell PATH does not contain the directory holding `claude`- **THEN** discovery still finds the binary via a known location and reports itThe load-bearing rules:
# <title>— one H1, the capability name.## Purpose— a short prose statement of what the capability is for. Freeform.## Requirements— the container heading for all requirements.### Requirement: <name>— one H3 per requirement. TheRequirement:prefix is the parse anchor. The name is a short imperative sentence.- Requirement body — one or more prose paragraphs stating behavior in normative keywords:
SHALL/MUST(hard requirement),SHALL NOT/MUST NOT(prohibition),SHOULD(strong recommendation),MAY(optional). Upstream guidance: “One statement, oneSHALL/MUST”, reach forMUST/SHALLby default. In practice a requirement body packs severalSHALLclauses. #### Scenario: <name>— one H4 per scenario, nested under a requirement. Every requirement must have at least one scenario that exercises it — this is a validation rule, not a style preference.- Scenario body — a bullet list of
GIVEN/WHEN/THEN/ANDsteps. Upstream docs show bare-keyword bullets (- WHEN 30 minutes pass ...); this repo’s corpus bolds them (- **WHEN** ...,- **THEN** ...,- **AND** ...). A lens should treat both**WHEN**andWHENas the same token.
Everything at requirement-body and purpose level is freeform prose. Everything at the heading and step level is structured.
Change deltas
Section titled “Change deltas”A change’s specs/<capability>/spec.md uses the same requirement/scenario grammar, but wraps requirements in delta operation headers that say what the change does to the base spec. From openspec/changes/wsl-daemon-runtime/specs/wsl-daemon-runtime/spec.md:
## ADDED Requirements
### Requirement: The daemon bundle is delivered into the distro once per versionFor a WSL-locus project, the shell SHALL ensure the daemon bundle exists ...
#### Scenario: First launch for a version delivers the bundle- **WHEN** a WSL-locus project is opened and no ... exists in the distro- **THEN** the shell copies the bundle to that path and spawns the daemon from itThe three delta headers, confirmed across this corpus (107 ADDED, 21 MODIFIED, 4 REMOVED occurrences):
| Header | Meaning | Body requirement |
|---|---|---|
## ADDED Requirements | Brand-new behavior | Full requirement + scenarios |
## MODIFIED Requirements | Existing behavior changes | The full new version of the requirement, not a patch |
## REMOVED Requirements | Behavior going away | The requirement plus a line on why |
Notes for a renderer:
- A single change spec can carry more than one delta header (e.g. a
## MODIFIED Requirementsblock followed by## ADDED Requirements). Seeopenspec/changes/archive/2026-08-16-add-windows-support/specs/packaged-editor-resolution/spec.md. - Rennet renders that mixed file as one capability root and card, with exact per-operation sections in source order beneath it. Each requirement and its nearest operation section retain the operation badge; the card shows the ordered unique badge roll-up and uses the added-capability edge whenever
ADDEDis present. MODIFIEDis a whole-requirement replacement, so a lens cannot show an intra-requirement diff from the change file alone — it must diff the modified requirement against the basespecs/requirement of the same name.- A change that creates a new capability may open its delta with
## Purposebefore the delta headers (the promoted spec inherits it). RENAMEDis not part of this corpus or the current writing guide; treatADDED/MODIFIED/REMOVEDas the complete set.
Proposal, design, tasks
Section titled “Proposal, design, tasks”The other three change artifacts are looser. A lens can exploit their headings but should treat the bodies as prose.
proposal.md — the why-and-what. This corpus uses ## Why, ## What Changes (bulleted), ## Capabilities (with ### New Capabilities / ### Modified Capabilities sub-lists that name the affected capability slugs), and ## Impact. See openspec/changes/wsl-daemon-runtime/proposal.md. The ## Capabilities block is the reliable machine-readable link from a change to the capability specs it touches.
design.md — optional technical approach. Freeform, but conventionally ## Context, ## Goals / Non-Goals, ## Decisions, ## Risks / Trade-offs, ## Migration Plan, ## Open Questions.
tasks.md — a GitHub-flavored Markdown checklist, grouped under numbered ## sections, with x.y task numbers:
## 1. Distro paths and bundle delivery- [x] 1.1 In `core`, add pure helpers: `wslServerBundlePath(version)` ...- [ ] 5.1 Full `pnpm check` green ...- [x] is done, - [ ] is open. This is the single richest structured signal in the whole format for progress rendering: parse the checkboxes for a completion ratio, and the ## groups for phase structure.
Change lifecycle
Section titled “Change lifecycle”- Propose — scaffold
changes/<name>/withproposal.md,specs/deltas,design.md,tasks.md. - Apply — implement, checking off
tasks.mdas you go, keeping the deltas aligned with the code. - Archive —
openspec archive <change>validates the change, merges its accepted deltas intoopenspec/specs/, and moves the directory intochanges/archive/under a dated name. A retired partial change is archived with--skip-specsso its unimplemented scope is not promoted as accepted contract.
Newer OpenSpec versions expose this through slash commands (/opsx:propose, /opsx:apply, /opsx:archive) rather than typed CLI verbs, but the on-disk artifacts are identical.
CLI and validation
Section titled “CLI and validation”The commands that produce or check the artifact shape (see the OpenSpec CLI reference):
| Command | Purpose |
|---|---|
openspec init [path] | Scaffold openspec/ and AI-tool configs |
openspec list [--specs|--changes] [--json] | List specs or changes |
openspec show [item] [--json] [--requirements] [--deltas-only] | Render one spec or change, optionally as JSON |
openspec validate [item] [--strict] [--json] [--all] | Check structural conformance |
openspec archive <change> [--skip-specs] [--yes] [--no-validate] | Promote deltas and move the change to archive |
openspec update | Regenerate AI-guidance files after a CLI upgrade |
openspec validate is what makes the format trustworthy for a lens. It checks the structural grammar — a requirement has the ### Requirement: shape, every requirement has at least one #### Scenario:, scenarios have step bullets, delta headers are recognized — and --strict tightens the checks. Crucially, openspec show --json and openspec validate --json mean a lens does not have to reimplement the Markdown parser: it can shell out to the CLI and consume structured output for CI or a renderer.
Rendering affordances
Section titled “Rendering affordances”What the Design lens can reliably exploit, ranked by how machine-parseable it is:
- Requirement cards (reliable). Every
### Requirement:is a titled, addressable unit with a normative body. Render each as a card; theSHALL/SHOULD/MAYkeyword gives a strength badge (hard / recommended / optional) for free. - Scenario blocks (reliable). Each
#### Scenario:under a requirement is a titled GIVEN/WHEN/THEN block. Render as a labeled step list; the WHEN/THEN split is a natural two-column or trigger-outcome layout. Bold-vs-bare keyword variance is the only normalization needed. - Delta badges (reliable).
## ADDED / MODIFIED / REMOVED Requirementsmaps directly to added/changed/removed badges on each requirement card in a change view. Counts per header give a change-size summary. - Task progress (reliable).
tasks.mdcheckboxes give an exact completion ratio and per-phase grouping — a progress bar with no heuristics. - Coverage mapping (semi-reliable).
proposal.md## Capabilitiesnames the affected capability slugs, and each change delta lives underspecs/<capability>/, so a lens can draw change to capability edges. A requirement-without-a-scenario is a validate failure, so scenario coverage per requirement is a renderable health signal. - Modified-requirement diffs (requires two files). Because
MODIFIEDcarries the full new requirement, an intra-requirement diff needs the basespecs/<capability>/spec.mdrequirement of the same name diffed against the change’s copy. Match on the requirement name afterRequirement:. - Purpose / proposal / design prose (freeform).
## Purpose,## Why,## Decisionsand requirement bodies are prose — render as readable Markdown, not as structured fields.
The single highest-leverage move for a lens: consume openspec show --json / openspec validate --json rather than re-parsing Markdown, and reserve custom parsing for the parts the CLI does not surface.
Sources
Section titled “Sources”- Corpus: this repo’s
openspec/tree (specs/,changes/,changes/archive/,config.yaml). - Fission-AI/OpenSpec README — workflow, slash commands, directory demo.
- OpenSpec CLI reference — command and flag list.
- Writing good specs — requirement/scenario grammar and delta headers.