Skip to main content
AcademytutorialOpenSpec tutorial series — Part 1: What is OpenSpec?

OpenSpec tutorial series — Part 1: What is OpenSpec?

A short introduction to OpenSpec, the spec-first framework underneath almost every Conduction project. What a spec is, what a change is, and why we write things down before we build them. First of two short modules.

TutorialOpenSpecSpec-firstTutorial seriesDocumentation
14 min read

OpenSpec is a lightweight framework for spec-driven development: first write down what a feature should do, only then write the code. In this twelve-minute module you'll learn what OpenSpec is, which concepts it builds on, and why we use it underneath almost every Conduction project. By the end you're ready for Part 2, where you'll actually write your first change.

In one sentence

OpenSpec is a lightweight convention for keeping requirements alongside your code. The spec lives in your repo, in Markdown, and evolves change by change as a clean spec-delta — just like your code evolves commit by commit.

No Confluence page drifting out of sync with the code. No Jira ticket only you understand. No stray README with the real rules buried between examples. One place for the what, one place for the how.

Why spec-first?

In practice, writing code is faster than coming up with a spec. So why the extra step?

  1. Surface assumptions early. What looks like one feature often turns out to be four features at once. By writing the scenarios first, you spot gaps and contradictions before the code is written — not after, in a PR review.
  2. Make AI agents productive. Our Hydra pipeline builds code from specs. The sharper the spec, the less course-correction afterwards. The same rule applies to a human teammate picking up a handover.
  3. An audit trail that doesn't lie. Every change lives in its own folder with a proposal, a spec-delta, a design and a task list. Six months later you can still see exactly what was decided and why.
  4. One source for reviewers. The reviewer sees the spec-delta and the code side by side. Does the code match the spec? Not "what did the developer mean", but "what does it say".

The four core concepts

OpenSpec rests on four words you'll keep seeing across projects. Half an hour invested in these terms saves a lot of confusion later.

How they fit together

openspec/
├── specs/                          ← stable — capabilities
│   └── publications/spec.md        ← contains REQ-001, REQ-002, … and scenarios
└── changes/                        ← temporary — in-flight changes
    └── add-publication-search/
        ├── proposal.md             ← why and what
        ├── specs/publications/     ← ADDED / MODIFIED / REMOVED requirements
        │   └── spec.md
        ├── design.md               ← how (technical)
        └── tasks.md                ← implementation checklist

A change adds, modifies or removes requirements in a spec. While the change is open, the delta lives in changes/. On archive, the delta merges into the main spec and the entire folder moves to changes/archive/YYYY-MM-DD-<name>/. That keeps the full history intact.

A requirement in the wild

To make it concrete — here is an actual requirement of the kind you'll meet in our repos:

### REQ-001: Full-text search
The system MUST support full-text search across publication titles
and content using PostgreSQL's tsvector.

#### Scenario: Search query returns matching publications
- GIVEN publications with titles "Climate Report 2026" and "Budget Overview"
- WHEN a user searches for "climate"
- THEN the results MUST contain "Climate Report 2026"
- AND the results MUST NOT contain "Budget Overview"
- AND the results MUST be sorted by relevance score

Three things to notice:

  • MUST is not a stylistic choice. RFC 2119 distinguishes MUST (absolute), SHOULD (strongly recommended, exceptions allowed) and MAY (optional). A reviewer reads that word and knows the intent.
  • The scenario is concrete enough that you could write a test from it. Someone else should be able to build the same behaviour without checking back with you.
  • Implementation-free. No class names, no controllers, no table names. Those belong in design.md, not here. The spec describes behaviour, not implementation.

Spec versus README versus issue

A common question from newcomers: "so what do I still put in the README, and what goes in an issue?" Simple rule of thumb:

DocumentContainsReaderUpdate frequency
openspec/specs/The what — requirements and scenarios. Stable behaviour of the app.Reviewers, AI agents, future developersPer change
openspec/changes/A change — proposal, delta, design and tasks for one in-flight change.Reviewers during the PROnce per change
README.mdHow to set up the app. How to run it. How to contribute.New developers, first sessionWhen a setup step breaks
GitHub IssueOne concrete task — tied to a change. Status, blocker, discussion.Whoever picks up the taskContinuously during the work

The golden rule: behaviour of the app belongs in the spec. If you find yourself explaining the same thing in a ticket, a Slack message and the PR description, you're probably missing a spec.

When not to use OpenSpec?

Spec-first is not a religion. Two situations where we skip it:

  • A one-off tweak — typos, adding a single row to a translation file, bumping a dependency. The spec overhead outweighs the tweak itself.
  • An exploratory prototype — when you don't even know whether the idea works yet. Test your assumptions with a throwaway prototype first; only write the spec for the version that's actually going into production.

For everything in between — a new feature, a breaking change, a refactor that affects consumers — OpenSpec earns its keep.

What does a Conduction repo look like?

All our projects share the same OpenSpec folder layout. One look at openregister/openspec gives you a good picture:

  • openspec/specs/ — the capabilities of this app, one folder per domain
  • openspec/changes/ — in-flight changes (often one per open PR)
  • openspec/changes/archive/ — completed changes, date-prefixed, for the history
  • openspec/architecture/app-specific ADRs (architectural decisions that only apply to this app)
  • openspec/config.yaml — project config (points at the shared Conduction schema)
  • project.md — high-level description of the app, in the repo root (not under openspec/)

Company-wide ADRs do not live in every app — they live in hydra/openspec/architecture. Building inside a Conduction app? Then those ADRs apply to you too — the Hydra pipeline loads them into every build. Rule of thumb: only something that is truly unique to this app goes in openspec/architecture/; everything else belongs company-wide in hydra/openspec/architecture/.

Who writes the specs?

Two patterns, side by side:

  1. Manually, by a developer or architect — use /opsx-new to create the change folder, then /opsx-ff (everything in one run) or /opsx-continue (artefact by artefact) in Claude Code. That's what you'll do in Part 2.
  2. Retrofit on an existing app — for legacy code that predates OpenSpec. See retrofit.md. Not your first job.

Whichever route you take, the end result is identical: a clean change in openspec/changes/ with the four expected artefacts.

The OpenSpec pipeline at a glance

A complete change moves through four stages, each with its own /opsx- command. Source: spec-driven-development.md.

StageCommandWhat it does
Obtain/opsx-exploreExplore a topic or problem before starting a change (optional)
Specify/opsx-newCreate an empty change with metadata
/opsx-ffFast-forward: proposal + specs + design + tasks in one run
/opsx-continueBuild artefact by artefact instead of all at once
/opsx-plan-to-issuesTasks → tracking issue + one GitHub issue per task
Build/opsx-applyImplement the tasks one by one, against the spec
Validate/opsx-verifyCheck that the code actually delivers the spec
Done/opsx-archiveSpec-delta → main spec; change → archive/
/opsx-syncMerge only the delta into the main spec (without archiving). /opsx-archive also does this — running sync separately is for when you want the spec updated before closing the change.

The golden rule from the Conduction docs: most changes only need /opsx-ff/opsx-apply/opsx-verify. The rest is opt-in. Fully automated runs are also an option — see /opsx-apply-loop (one change, containerized) and /opsx-pipeline (multiple changes in parallel).

Visual — the pipeline end to end

                ┌─────────────────┐
                │  /opsx-explore  │   (optional — explore, no artefacts)
                └────────┬────────┘
                         │
                         ▼
                ┌─────────────────┐
                │  /opsx-new      │   start the change (metadata only)
                └────────┬────────┘
                         │
                         ▼
        ┌────────────────────────────────────┐
        │  /opsx-ff        OR  /opsx-continue│
        │  (all in one)         (per step)   │   generate artefacts:
        │                                    │   proposal → specs → design → tasks
        └────────────────┬───────────────────┘
                         │
                         ▼
                ┌─────────────────────────┐
                │ openspec validate       │   spec-syntax check (CLI, no slash)
                │   --strict              │
                └────────────┬────────────┘
                             │
                             ▼              ── PR for the spec (review specs, not code) ──
                             │
                             ▼
                ┌─────────────────────────┐
                │ /opsx-plan-to-issues    │   tasks → GitHub issues + tracking epic
                └────────────┬────────────┘
                             │
                             ▼
                ┌─────────────────────────┐
                │ /opsx-apply             │   implement (per task: backend + UI + tests)
                └────────────┬────────────┘
                             │
                             ▼
                ┌─────────────────────────┐
                │ /opsx-verify            │   code-vs-spec check (CRITICAL/WARNING/SUGGESTION)
                └────────────┬────────────┘
                             │
                             ▼
                ┌─────────────────────────┐
                │ /opsx-archive           │   sync delta → main spec, change → archive/
                └─────────────────────────┘

Part 2 of this track walks through this pipeline end to end, starting at /opsx-explore (optional) and finishing at /opsx-archive.

Test yourself

Five short questions to check that this part landed. Stuck? Click Hint. Curious about the answer? Click Answer.

1. What is the core difference between a spec and a change?

Hint

One lives for years, the other from proposal to merge. Where does each live in the repo?

Answer
  • A spec describes a stable capability — a coherent piece of app behaviour. Long-lived, one spec per capability. All requirements + scenarios sit inside it. Lives in openspec/specs/<capability>/spec.md.
  • A change is a temporary edit to one or more specs. Contains a proposal, spec-delta (ADDED/MODIFIED/REMOVED), design and tasks. Lives in openspec/changes/<name>/ as long as it's open, and moves on archive to openspec/changes/archive/YYYY-MM-DD-<name>/.

A spec lives for years; a change lives from proposal to merge and then becomes history.

2. Why write a spec-delta before changing code?

Hint

Think about: assumptions, AI agents, audit trail, and reviewers. What does each one buy?

Answer

Four benefits from this module:

  • Surface assumptions early. What looks like one feature often turns out to be four features at once. Writing the scenarios brings gaps and contradictions to the surface before the code instead of after, in a PR review.
  • Make AI agents productive. Our Hydra pipeline builds code from specs — the sharper the spec, the less course-correction afterwards. The same applies to a human teammate picking up a handover.
  • An audit trail that doesn't lie. One folder per change with proposal, delta, design, tasks. Six months later you can still see exactly what was decided and why.
  • One source for reviewers. The reviewer sees the spec-delta and the code side by side — "does the code match the spec" instead of "what did the developer mean".

3. What are the three building blocks for describing behaviour in OpenSpec, and what's the role of a scenario?

Hint

Top-down: from capability, to rule, to concrete example. The last one makes the rule testable.

Answer

The three building blocks (top-down):

  • Spec — the stable capability description, top level.
  • Requirement — one rule inside a spec, written with MUST / SHOULD / MAY (RFC 2119). Numbered: REQ-001, REQ-002, …
  • Scenario — concrete GIVEN / WHEN / THEN example for a requirement. At least one per requirement.

The scenario makes the requirement testable: someone else should be able to build or test the same behaviour without consulting you. Not a description, but a test.

4. Where do in-flight changes live in a Conduction project, and what happens to a change when it's done?

Hint

Two folders: one for work in progress, one for completed history. And what moves where?

Answer
  • In-flight changes live in openspec/changes/<name>/ — proposal, spec-delta in specs/, design and tasks.
  • On archive (after implementation + merge into development) three things happen:
    1. The spec-delta is synced into the main spec at openspec/specs/.
    2. The whole change folder moves to openspec/changes/archive/YYYY-MM-DD-<name>/.
    3. The history stays complete — you can trace which change introduced which rule.

Steps 1 and 2 you don't do by hand — /opsx-archive (Part 2) handles it.

5. Name a situation where you specifically do NOT use OpenSpec and just code directly.

Hint

The overhead has to be smaller than the win. Which two situations clearly fail that test?

Answer

Two situations from this module:

  • One-off tweak — typos, adding a single row to a translation file, bumping a dependency. The spec overhead outweighs the tweak itself.
  • Exploratory prototype — when you don't even know whether the idea works yet. Test your assumptions with a throwaway prototype first; only write the spec for the version that's actually going into production.

For everything in between — a new feature, a breaking change, a refactor that affects consumers — OpenSpec does earn its keep.

Next step

Now that you know the concepts, in Part 2 we write a real change from beginning to end.