Skip to content

OpenSpec: Spec-Driven Development for AI Agents

A practical guide to writing OpenSpec requirements that give AI coding agents stable context about system intent, from first install to working proposals.


Content Table


Purpose

AI coding agents write code well. They don't know why the code should exist or what constraints it must satisfy. Ask an agent to "add a session timeout" and it will write code, but it has to guess the rules. Every guess is a hidden decision you'll find in a diff rather than a conversation.

OpenSpec is a tool for practicing Spec-Driven Development (SDD) within the Intent-Driven Development philosophy. See Background for a full explanation of both.

By the end of this guide you will have:

  • A working OpenSpec installation in your repository
  • A first spec capturing real requirements
  • A generated change proposal ready for review
  • A clear adoption approach for both new and existing projects

Audience

Software developers using AI coding agents who want better output through structured requirements. Comfortable with git and markdown. No prior experience with SDD needed.


Prerequisites

  • Node.js LTS or later
  • npm (included with Node.js)
  • An AI coding agent, for example, GitHub Copilot
  • A git repository (new or existing)

Install the OpenSpec CLI:

bash
npm install -g @fission-ai/openspec@latest

Verify the installation:

bash
npx @fission-ai/openspec --version

OpenSpec requires no API keys, no external services, and no lock-in. It is markdown files and a CLI.

First time? Run /opsx:onboard in your AI coding agent after installing and it will walk you through the full setup in about 15 minutes and help you create your first spec interactively. You can follow this guide alongside it or use it as a reference once you're up and running.


Background

What Is a Spec?

A spec is a markdown file describing what a feature must do but not how to build it. It captures observable behavior using GIVEN/WHEN/THEN scenarios, lives in version control alongside your code, and changes only through a reviewed proposal process.

What Is Spec-Driven Development?

Spec-Driven Development (SDD) puts requirements before code. You write the spec, the agent builds against it, and the implementation is validated before merge. The design conversation happens before the diff exists, so your team reviews intent as a document, not guesses it from code.

What Is Intent-Driven Development?

Intent-Driven Development is the broader philosophy SDD sits inside. Beyond capturing what the system does, it captures why decisions were made, through specs (functional intent), proposals (change intent), and ADRs (architectural intent). The goal is making the developer's full reasoning legible to AI agents across the development lifecycle.

How They Relate

Spec-Driven DevelopmentIntent-Driven Development
What it isA methodologyThe philosophy SDD implements
Primary questionWhat must this feature do?Why was it built this way?
Core artifactSpecSpecs + Proposals + ADRs
LifecycleDefine → Implement → ValidatePropose → Apply → Verify → Archive

Schemas

A schema tells OpenSpec which artifact types your project uses and how to validate them. It controls what folders the CLI recognises, what gets generated, and what the validator checks. The schema is declared in openspec/config.yaml at the root of your repository.

OpenSpec ships with several schemas:

SchemaArtifactsBest for
defaultSpecs + proposals (design, tasks, spec delta)Starting point - covers the full core workflow
minimalistSpecs + tasks onlySolo devs or fast iteration without a team review gate
behaviour-drivenSpecs with GIVEN/WHEN/THEN requirements and scenariosTeams already practising BDD who want spec-driven structure
intent-drivenProposals + specs + design + ADRs + tasksTeams who want the full workflow including durable architectural decisions from day one
spec-driven-with-adrDefault + ADRsTeams adding ADR persistence on top of an established default workflow
event-drivenEvent Storming + AsyncAPI specsSystems built around events and async messaging
linearizedDefault specs mirrored to Linear Project DocumentsTeams using Linear who want specs synced with their issue tracker

The default schema

The default schema is what most teams use throughout their OpenSpec lifecycle. It recognises openspec/specs/ and `openspec/changes/. See Repository Structure for the full directory layout.

Each artifact has a defined role and lifetime:

ArtifactRoleLifetime
openspec/specs/<name>/spec.mdSource of truth for what the system doesPermanent - grows with the codebase
openspec/changes/<id>/proposal.mdIntent document reviewed before code is writtenTemporary - archived after merge
openspec/changes/<id>/design.mdBehavioral decisions and alternatives consideredTemporary - archived after merge
openspec/changes/<id>/tasks.mdImplementation breakdown for the agentTemporary - archived after merge
openspec/changes/<id>/specs/<name>/spec.mdSpec delta exact lines being added or removedTemporary - folded into source-of-truth on archive

The validator checks that every change folder contains all four required files and that spec deltas use the correct +/- line marker format. Missing files or malformed deltas are flagged before apply runs.

Start with default. You can change the schema later in openspec/config.yaml. Switch only when the team has run several full cycles and has a specific reason to expand.

yaml
# openspec/config.yaml
schema: default

Schemas and what each one adds are documented in full at intent-driven-dev/openspec-schemas. Run openspec schema validate to check that your project's artifacts match the active schema.


Core Concepts

Reference: openspec.dev Official documentation, getting started, and command reference

ConceptWhat it is
SpecA markdown file describing what a feature must do. Lives at openspec/specs/<name>/spec.md.
RequirementA single observable behavior the system must guarantee, written as "The system SHALL..."
ScenarioA concrete example proving a requirement works, using GIVEN / WHEN / THEN structure.
ProposalA package of documents describing a change to requirements before any code is written. Lives at openspec/changes/<id>/.
Spec DeltaThe part of a proposal showing exactly which requirement lines change (a diff, but for requirements).
ExploreAI-assisted spec drafting /opsx:explore [description] investigates the codebase and drafts the spec grounded in real code, before any proposal is written.

OpenSpec organizes every change through a Propose → Apply → Archive lifecycle:

PhaseCommandWhat happens
Propose/opsx:proposeAgent reads the source-of-truth specs and generates a proposal package: spec delta, design, tasks
Review intent(manual)Team reviews what changes and why before any code is written - this is the critical gate
Apply/opsx:applyAgent implements the change in an isolated branch, using the spec as the build target
Verify/opsx:verifyAgent checks that the implementation matches the proposal artifacts; flags any drift
Archive/opsx:archiveSpec delta is merged into the source-of-truth spec; the change folder is archived

The source-of-truth spec in openspec/specs/ is the canonical record of what the system does. Spec deltas in openspec/changes/ are temporary, they exist only for the duration of a change, then get folded in on archive.


Repository Structure

OpenSpec adds two directories to your repository:

your-repo/
└── openspec/
    ├── specs/              ← feature specifications
    │   ├── auth-session/
    │   │   └── spec.md
    │   ├── checkout-cart/
    │   │   └── spec.md
    │   └── user-profile/
    │       └── spec.md
    └── changes/            ← change proposals (written before code)
        └── add-remember-me-sessions/
            ├── proposal.md
            ├── design.md
            ├── tasks.md
            └── specs/
                └── auth-session/
                    └── spec.md   ← spec delta

Naming convention: spec directories use <domain>-<feature> in kebab-case. Use domain prefixes to group related specs:

Domain prefixExamples
auth-auth-session, auth-login, auth-password-reset
checkout-checkout-cart, checkout-payment, checkout-shipping
user-user-profile, user-notifications, user-settings
api-api-rate-limiting, api-versioning

Commit both specs/ and changes/ to your repository. Specs evolve alongside code via normal PRs.


Writing a Spec

This section is for understanding. In practice you rarely write specs from scratch, /opsx:explore [description] has an AI agent draft one for you, which you then review and edit before committing. The format below is what that output looks like, and what the agent reads when running /opsx:propose. See Using OpenSpec with AI Agents for how this fits the full workflow.

Reference: OpenSpec Documentation

The Spec Format

markdown
# Auth Session Specification

## Purpose
Users need to stay authenticated across page loads without logging in repeatedly.
Sessions must expire automatically to protect accounts on shared or unattended devices.

## Requirements

### Requirement: Session Persistence
The system SHALL maintain a user's authenticated state across page loads for the
duration of an active session.

#### Scenario: User navigates between pages
- GIVEN a user has successfully authenticated
- WHEN they navigate to a different page in the application
- THEN the system SHALL display the page in their authenticated context without
  prompting for credentials

#### Scenario: Expired session rejected on navigation
- GIVEN a user's session has expired
- WHEN they attempt to navigate to a protected page
- THEN the system SHALL redirect them to the login page
- AND display a message indicating their session has expired

### Requirement: Idle Session Expiration
The system SHALL expire sessions after a configurable period of user inactivity.

#### Scenario: Session expires after idle timeout
- GIVEN a user has been authenticated and idle for longer than the configured timeout
- WHEN the idle timeout period is reached
- THEN the system SHALL invalidate the session
- AND redirect the user to the login page on their next interaction

#### Scenario: Activity resets the idle timer
- GIVEN a user has an active session
- WHEN the user performs any interaction in the application
- THEN the system SHALL reset the idle timeout counter

Format rules at a glance:

ElementFormat
File title# [Feature Name] Specification
Purpose## Purpose 1–3 sentences explaining why this feature exists
Requirement heading### Requirement: [Name]
Requirement bodyThe system SHALL [observable behavior].
Scenario heading#### Scenario: [Name]
Scenario body- GIVEN / - WHEN / - THEN / - AND

Writing Good Requirements

Requirements describe what the system does from the outside, observable behavior, not implementation.

✓ Behavior - belongs in a spec✗ Implementation - does not belong
"The system SHALL expire sessions after a configurable idle timeout.""The system SHALL use Redis TTL to expire sessions."
"The system SHALL return an error for files over 10 MB.""The system SHALL use multipart chunking for large files."
"The system SHALL maintain cart contents across page reloads.""The system SHALL store cart state in localStorage."

Use SHALL non-negotiable, not optional. One behavior per requirement; if you need "and", split it into two.

Writing Good Scenarios

Each scenario proves a requirement can pass or fail:

KeywordRole
GIVENStarting state - who is the user, what state is the system in?
WHENOne triggering event (if you need two, write two scenarios)
THENObservable result - what does the user see or the system guarantee?
ANDAdditional results from the same WHEN

Always include at least one failure or edge case per requirement, because happy-path-only specs leave agents without guidance where it matters most.

Before you commit: review the spec

Before committing a new spec, run /openspec-review(available in the current repository, for how to add it see the Skill Brief) in OpenCode/Copilot/ Claude Code. It checks that every requirement is independently testable, flags any requirement that describes implementation rather than behavior, and identifies missing failure scenarios, returning findings as Critical / Important / Suggestions with an offer to apply fixes directly.

This is especially useful when you are new to writing requirements: the review catches the most common mistakes (vague SHALL statements, bundled behaviors, happy-path-only scenarios) before the spec becomes source of truth.


Proposing Changes

When requirements need to change, write a proposal first. A proposal documents what is changing and why before any code is touched, letting your team review the decision, not just the implementation.

The Proposal Package

These files are generated by /opsx:propose. The examples below are for understanding what each file contains and what to look for when reviewing and are not a template you have to fill in yourself.

openspec/changes/add-remember-me-sessions/
├── proposal.md     ← what and why
├── design.md       ← how it will be done
├── tasks.md        ← implementation breakdown
└── specs/
    └── auth-session/
        └── spec.md ← spec delta

proposal.md the entry point for review:

markdown
# Proposal: Add Remember Me Sessions

## Summary
Add an opt-in persistent session lasting 30 days, allowing users to stay
authenticated across browser restarts on personal devices.

## Motivation
Users frequently re-authenticate on personal devices, creating friction for
daily users. This is the most-requested missing feature in support tickets.

## Scope
- Specs affected: auth-session
- Estimated size: medium

design.md technical decisions (behavior-focused, not code):

markdown
# Design: Add Remember Me Sessions

## Approach
Introduce a persistent session type that survives browser restarts. The user
opts in explicitly at login. Persistent sessions are renewed on activity.

## Alternatives Considered
- Always-on persistence: rejected for security risk on shared devices.
- Configurable duration: deferred as it adds complexity not needed for v1.

## Open Questions
- Should persistent sessions appear in a session management page?

tasks.md actionable breakdown:

markdown
# Tasks: Add Remember Me Sessions

## Phase 1: Session model
- [ ] Add persistent session type
- [ ] Update session creation to accept opt-in flag

## Phase 2: Login UI
- [ ] Add "Remember me" checkbox to login form
- [ ] Pass opt-in flag on submit

Spec Deltas

The specs/ folder in a proposal shows exactly which requirement lines change. Lines with no prefix are unchanged. + marks additions, - marks removals.

markdown
# Auth Session Specification

## Purpose
[unchanged]

## Requirements

### Requirement: Idle Session Expiration
- The system SHALL expire sessions after a configurable period of user inactivity.
+ The system SHALL expire sessions after a configurable idle timeout (default: 30 minutes).
+ Standard sessions are subject to idle expiration. Persistent sessions (see below) are not.

### Requirement: Remember Me
+ The system SHALL allow users to opt into a persistent 30-day session at login.
+
+ #### Scenario: User enables remember me
+ - GIVEN a user is on the login page
+ - WHEN they check "Remember me" and submit valid credentials
+ - THEN the system SHALL create a session persisting for 30 days
+ - AND maintain that session across browser restarts

This format makes it obvious what changed so that reviewers do not need to diff two versions of a spec manually.

Choosing Your Propose Command

Two approaches can create a proposal. Which to use depends on how much control you need over artifact generation:

ApproachCommandsWhat it doesWhen to use
Core/opsx:proposeGenerates the full proposal package (proposal.md, design.md, tasks.md, spec delta) in one stepDefault for most work; fastest end-to-end path
Extended/opsx:new/opsx:continue/opsx:ffInitializes a scaffold, then creates artifacts one at a time or fast-forwards through remaining onesComplex or high-stakes changes where you want to inspect and adjust each artifact before the agent moves to the next

Default to /opsx:propose. It produces the full review package in one step. It is the fastest end-to-end path and the command the core workflow is built around.

Use the extended approach (/opsx:new + /opsx:continue) when the change is complex enough that you want to read and refine the design.md or tasks.md before generation continues. Use /opsx:ff to finish any remaining artifacts once you are satisfied with the direction.


Greenfield vs Brownfield

Greenfield means starting a new project from scratch with no existing code, so specs can be written before any implementation begins. Brownfield means adopting OpenSpec on a codebase that already exists, where behavior and constraints are already baked into the code rather than written down anywhere. If you are on an existing project proposal-first is the better fit as it requires no upfront complete spec coverage and pays off immediately on the next change.

Greenfield - spec-first, new project

Write specs before writing code. The spec becomes the source of truth the AI agent builds against.

Recommended flow:

  1. For each feature, write a spec in openspec/specs/<name>/spec.md
    • Not sure how to structure it? Run /opsx:explore [description] first so that the agent drafts a spec from your description, which you then review and edit before committing
    • Already know what the feature must do? Write the spec directly and skip explore
  2. Commit the spec
  3. Run /opsx:propose [description] to generate the proposal package against the committed spec
  4. Review code against the spec, not just against your mental model

Start with the most foundational features: think auth, core data models, primary user flows. You do not need specs for everything on day one.

Brownfield - existing code, adopting later

Existing systems have undocumented constraints and decisions baked into the code. Agents working without that context silently re-open settled choices. Don't try to spec the whole system, but rather spec the area you're about to change, then enforce proposals from that point on.

PhaseWhat to do
ExploreRun /opsx:explore on the target area and share the relevant source files with your AI agent so the spec reflects actual behavior, not a hallucination
ReviewValidate the output with someone who knows the code so that gaps reveal undocumented behavior or bugs
ContinueWrite the spec for current behavior, enforce proposals for all future changes, repeat for the next area

Two rules for brownfield specs:

  • Document what the system does, not what you wish it did as aspirational specs mislead the agent
  • If the code is buggy, the spec reflects the bug, think of fixing it as a separate proposal

Architectural Decision Records (ADRs) (optional)

Once your team is comfortable with specs and proposals, ADRs let you capture why architectural decisions were made, and in doing so, preserve reasoning that would otherwise disappear when design.md is archived. See the companion guide: OpenSpec ADRs.


Parallel Feature Development

Advanced. This section assumes you are comfortable with git worktrees and multi-agent workflows. If you are new to OpenSpec, skip this section and return once the core Propose → Apply → Archive cycle feels natural.

Reference: OpenSpec, Git WorkTrees and OpenCode - Full walkthrough of the parallel development workflow

OpenSpec supports parallel feature development through Git WorkTrees combined with SubAgents. Each proposal runs in its own isolated branch; multiple proposals can be applied simultaneously without interference.

The rule is: propose on main → apply inside worktrees → merge → archive.

Why proposals must be created on main before branching:
OpenSpec's proposal step reads the source-of-truth specs to detect conflicts and gaps between the proposed change and what already exists. If you propose inside a worktree, you miss changes from sibling branches. Creating all proposals on main first ensures each agent gets a consistent view of the current state before it starts implementing.

How it works in practice:

StepWhereWhat
1. Create proposal Amain/opsx:propose [description-A]
2. Create proposal Bmain/opsx:propose [description-B]
3. Apply A in isolationWorktree branch feat/ASubAgent implements against spec
4. Apply B in isolationWorktree branch feat/BSubAgent implements against spec
5. Verify AWorktree feat/ACheck implementation matches proposal
6. Verify BWorktree feat/BCheck implementation matches proposal
7. Merge A, then BmainNormal PR flow
8. ArchivemainDelta specs merged into source-of-truth

Commit discipline: Commit at every logical boundary: after proposal creation, after each artifact, after implementation, after archive. WorkTree branches can be discarded and recreated from the proposal on main; frequent commits on main protect against losing proposal state.

WorkTrees share the same git object store so there is no repository duplication. The isolation is branch-level, not filesystem-level.


Using OpenSpec with AI Agents

Reference: openspec.dev - Full CLI reference and command documentation

Once specs are committed, AI agents reference them automatically via /opsx:* slash commands. The core commands, in the order you will use them:

PhaseCommandPurpose
Setup/opsx:onboardGuided 15-minute setup walkthrough - run this first on a new project
Explore/opsx:exploreAI investigates the codebase and drafts the spec before any proposal is written
Propose/opsx:proposeGenerate the full proposal package in one go - the core propose command
Implement/opsx:applyImplement the change in an isolated branch against the spec
Finish/opsx:syncMerge delta specs into source-of-truth specs without archiving the change
Finish/opsx:archiveMerge spec delta into source-of-truth and close the change
(extended)/opsx:newInitialize a change scaffold - starting point for step-by-step artifact creation
(extended)/opsx:continueCreate the next artifact in dependency order
(extended)/opsx:ffFast-forward through all remaining proposal artifacts at once
(extended)/opsx:verifyValidate implementation against specifications - checks completeness, correctness, and coherence
(extended)/opsx:bulk-archiveArchive multiple completed changes at once

For the Propose phase: use /opsx:propose to generate everything at once (core workflow). For step-by-step control, use the extended approach: /opsx:new to initialize, /opsx:continue to build artifacts one at a time, or /opsx:ff to fast-forward through remaining ones.

Example workflow:

# 1. Investigate the codebase and draft a spec
/opsx:explore Add remember-me checkbox with 30-day sessions

# 2. Generate the full proposal package (spec delta, design, tasks)
/opsx:propose

# 3. Review proposal.md and the spec delta with your team
#    This is the critical gate must agree on intent before any code is written

# 4. Implement the change in an isolated branch against the spec
/opsx:apply

# 5. Verify the implementation matches the proposal
/opsx:verify

# 6. Merge spec delta into source-of-truth and close the change
/opsx:archive

Supported tools (v1.3.1): Claude Code, Cursor, GitHub Copilot, Windsurf, Gemini CLI, Amazon Q, Kiro, Junie, Cline, RooCode, and 30+ others. All use native slash commands.


Tips and Pitfalls

Do

  • Start with the features your agent touches most adopting OpenSpec incrementally, beginning with high-traffic areas, gives the fastest return on investment without requiring you to spec everything upfront.
  • Commit specs alongside code in the same PR specs that live in a wiki or separate repo drift out of sync within weeks. Co-location in git keeps them honest.
  • Review spec deltas the same way you review code the proposal review is where misalignment surfaces cheaply. Treat it as seriously as a PR review.
  • Push for at least one failure scenario per requirement the edge cases you write scenarios for are the ones your agent will handle correctly. Everything else is a guess.
  • In brownfield, document current behavior first even if it's wrong. Accurately capturing what the system does makes it safe to reason about what to change.
  • If using ADRs, write them for constraints that look arbitrary if a spec has a requirement that seems odd or unnecessarily restrictive, an ADR explaining the reason prevents agents (and future devs) from "cleaning it up" and breaking something.

Avoid

  • Writing requirements that describe implementation "The system SHALL use Redis TTL to expire sessions" makes your spec stale the moment you switch caching libraries. Describe the observable behavior instead.
  • Bundling multiple behaviors in one SHALL statement if you need "and", split it into two requirements. Each requirement should be independently testable and changeable.
  • Vague SHALL statements "The system SHALL handle it properly" passes by default because it cannot be falsified. Make it specific: "The system SHALL return a 401 response for requests with expired session tokens."
  • Writing aspirational specs in brownfield projects documenting what you wish the system did misleads the agent about what the code actually does. Capture reality first, then propose changes.
  • Skipping proposals for "small" changes the discipline of writing a proposal before touching code pays off most on changes that seem simple but turn out to have edge cases. The proposal takes five minutes; undoing a wrong implementation takes longer.
  • Nesting requirements inside fenced code blocks OpenSpec's validator will detect and flag them. Keep all The system SHALL statements as plain text, never inside code fences.

References

OpenSpec

Intent-Driven Development

Copyright © MSG Systems Romania AI Labs