跪拜 Guibai
← Back to the summary

OpenSpec: The Markdown Spec Layer That Makes AI-Generated Code Safe to Ship

AI-made changes too risky to ship? OpenSpec spec-driven development in practice

I recently read an article about "Spec programming + visual Skills / code maps". The core point struck a chord: when faced with a legacy codebase of hundreds of thousands of lines, both humans and AI lack an overview map, so nobody dares to review, let alone ship, AI-modified code. The article's answer is "use skills to generate an interactive code map".

My addition is: the map solves the "seeing" part, but the "describing clearly and verifying" part is still missing. Seeing the structure is not the same as agreeing on what counts as "done". That step requires specs. This article discusses an open-source tool that does this lightly: OpenSpec.

1. The problem isn't that AI writes poorly; it's that requirements never land

Let me lay out three typical scenarios:

  1. Requirements live only in chat logs. You say three sentences to AI, it changes eight files. Three days later, when you need to recall "why was this designed that way", you dig through conversation records, but those were compressed, cleared, or lost when switching sessions.
  2. AI output cannot be accepted. Without pre-agreed acceptance criteria, review degenerates into "it doesn't error, so it's fine". After the change, you have to read an implementation you didn't write, which is more tiring than writing it yourself, especially in an existing legacy codebase.
  3. The system has no single source of truth. When a newcomer asks "how many columns does this list show by default, and under what conditions does it remember the user's choice?", the answer is scattered across code, a Wiki page, some group chat, and the memory of a departed colleague.

These three together constitute spec drift: code runs forward while the shared understanding of the system stays still.

Spec-Driven Development (SDD) has a simple idea: write "what behavior the system should exhibit" as structured, reviewable, Git-committed Markdown, so humans and AI align before the first line of code is written. The spec is the contract; code is just one implementation of it.

There is a division of labor often overlooked, which matches the "code map" article I mentioned:

Layer Question Answered Typical Carrier
Skills / Workflows What steps does this task follow skill packs, slash commands
Code map / overview Where is the code, how do the links flow, how big is the impact interactive HTML, flowcharts, sequence diagrams
Spec contracts What counts as done, and how to verify it openspec/specs/, changes/

The three complement each other; they do not replace each other. Maps and skills make AI "able to work"; specs allow humans "to make the call".

2. What is OpenSpec

OpenSpec is a lightweight specification framework open-sourced by Fission-AI (MIT license), adding a "spec layer" to AI coding assistants. Its design philosophy, as stated in the README, largely sets it apart from similar tools:

→ fluid not rigid            流动,而非僵化
→ iterative not waterfall    迭代,而非瀑布
→ easy not complex           简单,而非复杂
→ built for brownfield       为存量工程而生,不只服务新项目
→ scalable from personal projects to enterprises   个人到团队都能用

Key characteristics:

3. Up and running in five minutes

3.1 Installation and initialization

The official installation methods are varied (refer to the README for the latest):

# macOS / Linux: standalone binary
curl -fsSL https://openspec.dev/cli/install.sh | sh

# Windows PowerShell
irm https://openspec.dev/cli/install.ps1 | iex

# Or via package manager (npm requires Node.js 22.12.0+)
npm install -g @fission-ai/openspec@latest
brew install openspec

Inside your project, initialize and choose your AI tool:

cd your-project
openspec init                          # interactive tool selection
openspec init --tools claude,cursor    # non-interactive, specify directly
openspec init --tools none             # only the openspec/ structure, no tool files

After initialization, the project gains these files:

your-project/
├── openspec/
│   ├── config.yaml      # project-level config: schema, context, rules
│   ├── specs/           # single source of truth: current system behavior, organized by capability
│   │   └── <capability>/spec.md
│   └── changes/         # in-progress changes, one folder per requirement
│       └── <change-name>/
│           ├── .openspec.yaml
│           ├── proposal.md
│           ├── specs/    # delta spec
│           ├── design.md
│           └── tasks.md
├── .claude/skills/      # or .cursor/, .codebuddy/, etc., generated per chosen tool
└── AGENTS.md            # entry point for AI

Tip: After installing, restart IDE / session; slash commands are loaded at startup. Command syntax varies by tool: for Claude Code, CodeBuddy it's /opsx:apply, for Cursor, Copilot it's /opsx-apply, for Amazon Q it's @opsx-apply, and for Codex it's the skill name $openspec-apply-change. openspec init prints the correct syntax for your chosen tool; don't copy someone else's.

The default profile is core, containing explore / propose / apply / archive. For finer-grained workflows (/opsx:new, continue, ff, verify, bulk-archive, onboard), run:

openspec config profile    # interactive selection
openspec update            # make new workflows effective

3.2 A five-step loop

Every OpenSpec change follows the same loop:

  1 Explore  →  2 Propose  →  3 Review  →  4 Apply  →  5 Archive
   想清楚        AI 出计划     人改计划     AI 按任务实现   规约吸收变更
      ↑                                                        │
      └──────────────────── 下一个 change ←────────────────────┘

① Explore — think first, don't rush to write

/opsx:explore  帮我梳理一下「表格列偏好」在这个后台系统里应该怎么设计

This is a "think only, don't implement" mode: AI reads code, offers options, questions your assumptions, and writes no code and no artifact files. For legacy codebases of hundreds of thousands of lines, this step is especially necessary — letting AI lay out the impact surface first is safer than having it start coding right away. Once you've thought it through, saying "write up the proposal we just discussed" carries the context into Propose.

② Propose — produce a reviewable plan

/opsx:propose add-column-preferences

AI creates openspec/changes/add-column-preferences/ with its four artifacts, but stop there — no business code yet. This is OpenSpec's most important gate: before code exists, the plan must be seen by a human.

③ Review — you're editing text, not code

Following the officially recommended order:

  1. proposal.md: is the problem correct? Is the scope too broad?
  2. specs/: the highest-value file. Can you accept these requirements as the definition of "done"?
  3. tasks.md: do the tasks cover all specs, and does it contain any extra stuff?

To change something, directly edit the Markdown, or tell AI "the spec is missing the not-logged-in scenario, add it". At this point the cost of change is a few minutes; once code is spread out, it becomes hours.

④ Apply — execute per the checklist

/opsx:apply add-column-preferences

The official advice is practical: open a new session (a clean context window leads to higher-quality implementation), and track progress only via the checkboxes in tasks.md. If you get interrupted or the context blows up, open a new session and apply again; AI resumes from the first unchecked task, with no hidden state. If you find the plan is wrong mid-way, go back and edit the artifacts, then continue applying — no need to start over.

⑤ Archive — bring the spec up to date with the code

/opsx:archive add-column-preferences

Archiving does two things: merge the delta spec into openspec/specs/, and move the entire change folder to openspec/changes/archive/ for audit history. The merge semantics are:

ADDED    → append to the main spec
MODIFIED → replace the same-named Requirement in the main spec (entire block replacement)
REMOVED  → delete from the main spec
RENAMED  → only rename, with FROM:/TO:

At this point, openspec/specs/ is once again the single source of truth for the system's current behavior, and the next change starts from it. This is the value of the whole loop: the spec doesn't rot over time.

4. The four artifacts and their responsibilities

The default schema is called spec-driven, and the dependency order of the four artifacts is:

             ┌─ specs ──┐
proposal ────┤          ├── tasks ── apply
             └─ design ─┘
Artifact Answers Key Constraint
proposal.md Why do it, what changes, impact surface Only why/what; implementation details go to design
specs/<capability>/spec.md What counts as "done" Behavioral contract; no class names, frameworks, or steps
design.md How to build it technically Technical decisions, trade-offs, risks; may be omitted if not triggered
tasks.md How many steps to finish Checkboxes - [ ]; progress is state

proposal.md template:

# Proposal

## Why
<!-- 1-2 句:解决什么问题,为什么现在做 -->

## What Changes
<!-- 具体列出新增/修改/移除,破坏性变更标 **BREAKING** -->

## Capabilities

### New Capabilities
- `<capability-path>`: <这个能力覆盖什么>

### Modified Capabilities
- `<existing-capability-path>`: <哪条需求要变>

## Impact
<!-- 受影响的代码、API、依赖、系统 -->

The biggest trap here is Capabilities naming: name it after a persistent system behavior (like user-auth, column-preferences), not after this dev task (like add-login-button). The official advice is to first run openspec list --specs to inventory existing capabilities, reuse exact paths, and avoid creating near-synonyms that duplicate capabilities. Pure refactors, documentation, and tooling changes with no behavioral change should set skip_specs: true in .openspec.yaml; don't force a requirement just to pass validation.

Delta spec: the soul of the framework

Specs are not rewritten in full each time; they describe increments relative to the current spec. The standard format:

# Spec Delta

## Purpose
<!-- 仅新建 capability 需要,50+ 字符说明这个能力是干什么的 -->

## ADDED Requirements

### Requirement: 保存列偏好
系统 SHALL 在用户调整列显示后持久化该选择,并在下次进入同一列表时恢复。

#### Scenario: 用户勾选新列后重新进入列表
- **WHEN** 用户在「订单列表」勾选「下单时间」列并离开页面
- **THEN** 再次进入「订单列表」时该列保持可见

#### Scenario: 未登录用户
- **WHEN** 未登录用户调整列显示
- **THEN** 选择仅在当前会话生效,且不发起任何写接口请求

Several hard rules; getting them wrong causes silent failures or lost information during archiving:

design.md and tasks.md templates are restrained:

# Design

## Context
## Goals / Non-Goals
**Goals:**
**Non-Goals:**
## Decisions
## Risks / Trade-offs
# Tasks

## 1. <阶段名>
- [ ] 1.1 <具体任务>
- [ ] 1.2 <具体任务>

## 2. <阶段名>
- [ ] 2.1 <具体任务>

A complete small example

Requirement: the admin order list allows users to customize columns and remembers their choices.

openspec/changes/add-column-preferences/
├── .openspec.yaml
├── proposal.md        Why: 用户每次进列表都要重勾列
│                      Capabilities: New = column-preferences
│                                    Modified = list-view(默认列需求变更)
├── specs/
│   ├── column-preferences/spec.md   ADDED: 保存列偏好、未登录降级
│   └── list-view/spec.md            MODIFIED: 默认列展示规则
│                                    REMOVED: 旧的「固定 8 列」要求 + Reason/Migration
├── design.md          localStorage 优先 + 用户配置接口兜底;评估 SSR 首屏闪动
└── tasks.md           1. 数据层 / 2. UI 层 / 3. 测试(含未登录、跨页面恢复)

The original requirement "list always shows 8 columns" in openspec/specs/list-view/spec.md is MODIFIED in this change to "show according to the user's saved preferences, falling back to the default 8 columns when there is no preference". After archiving, the main spec updates automatically, and the next person who picks this up sees the new rule, not a logged conversation.

5. CLI quick reference

Agents call these themselves during the workflow; humans only need to know the common ones:

Command Purpose
openspec init [--tools <ids>] Initialize project and tool integration
openspec update [--force] [--dry-run] Refresh AI instruction files; must run after upgrading CLI
openspec list / openspec list --specs List in-progress changes / list existing capabilities
openspec show <id> --type spec [--json] View a change or spec in full
openspec status --change <name> --json View completion status of the four artifacts
openspec instructions <artifact> --change <name> --json Get generation instructions for an artifact (mostly used by agents)
openspec validate [name] --all --strict Validate structure and format; --strict treats warnings as errors
openspec archive <name> --yes Merge delta and archive
openspec view View specs and changes overview in terminal/page
openspec config profile / config list Switch workflow sets, view global config
openspec schemas / openspec schema fork spec-driven my-workflow View / fork custom schema
openspec store / doctor / context / workset Multi-repo spec store (beta) related
openspec version / install / completion Version, standalone binary install, completion

Always add --json for programmatic calls. Also, openspec change / openspec spec are old nominal commands, deprecated; don't use them in new scripts.

6. Team adoption: from "personal habit" to "engineering constraint"

For a single user, OpenSpec mainly solves "alignment with AI". At the team level, the value shifts to collaboration and knowledge transfer. These are practical practices:

  1. Commit the code and the change folder together in the same PR. Reviewers read proposal.md and specs/ first, then the diff. The review object changes from "are these 800 lines of code correct" to "is the requirement definition correct + does the implementation cover it", cutting cognitive load by an order of magnitude.
  2. Run openspec validate --strict in CI. Problems like miswritten scenario levels, missing scenarios, or overly long descriptions are caught before merging.
  3. Use openspec/config.yaml to codify team conventions. This is the lightest customization layer, injecting tech stack, architecture constraints, and test requirements into every workflow, e.g., "tasks must include a test task" or "proposal must state a rollback plan":
schema: spec-driven
context: |
  技术栈:TypeScript + Vue 3 + Vite
  测试:Vitest + Playwright
rules:
  tasks:
    - 每个 change 的 tasks 必须包含对应的单测/E2E 任务
  proposal:
    - 涉及接口变更时必须写明兼容与回滚方案
  1. Fork the schema when conventions aren't enough. config.yaml can only add on top of the standard process; it can't change artifact structure or file names. If you want to drop design or add custom sections, use openspec schema fork spec-driven my-workflow to derive your own schema and templates.
  2. Use Stores (beta) for cross-repo requirements. When a feature spans API, Web, and component-library repos, put openspec/ in a dedicated spec repo that each code repo reads from, avoiding "the platform team writes it once in Wiki, and each of the three repos interprets it differently".
  3. New session + high-reasoning model. The official recommendation is to do implementation in a clean context window and to prefer models with strong reasoning for planning and implementation. Context hygiene has a big impact on the framework's effectiveness.
  4. Decide whether to keep the archive after archiving. openspec/changes/archive/ keeps the full proposal / design / tasks / delta. If Git is your spec history, commit the archive first, then delete the specs/ subdirectory to reduce search noise; openspec validate --archived still works.

7. Choosing among similar solutions

Solution Strengths Costs
No spec (pure prompt) Zero cost, fastest start Requirement drift, unpredictable results, unreviewable
GitHub Spec Kit Complete process, clear phases Heavy phase gates, many Markdown artifacts, Python dependency
Kiro Complete in-IDE experience Tied to their IDE and model
OpenSpec Light, fluid, tool/model agnostic, built for brownfield Relies on self-discipline and process constraints; doesn't force "write spec before code"

This is the comparison from OpenSpec's official README, and I largely agree: it doesn't try to be the most rigorous one; it tries to be the one that's easiest to stick with in a real old project. The biggest failure mode of spec frameworks is never being too weak; it's the team giving up after two weeks because it's too annoying.

And to return to the "code map" article mentioned at the start: the two can be joined into a pipeline:

代码地图 / Skills  →  摸清工程结构与影响面
        ↓
proposal.md        →  把影响面写进 Impact,划定能力边界
        ↓
delta spec         →  定义「改成什么样算完成」与验收场景
        ↓
tasks.md + apply   →  按清单施工,代码地图复核改动范围是否越界
        ↓
archive            →  行为共识回写主 spec,成为下一轮的起点

The map tells AI "where to touch"; the spec tells humans "how to know it's right". The former is visualization, the latter is contract; without either half, you can't say "safe to ship".

8. Pitfall checklist

9. Summary

AI coding has driven down the cost of "writing code", so the bottleneck shifts to "alignment" and "acceptance". OpenSpec doesn't invent a new language or a platform; it simply uses a few Markdown files plus a five-step loop to turn "requirements → plan → implementation → archive" back into a reviewable, traceable, diffable engineering chain.

For an existing legacy codebase of hundreds of thousands of lines, the benefit is especially real: no need to start over; you can begin with the next requirement and bake in one capability at a time. After running three to five changes, openspec/specs/ becomes the most valuable document for a new hire to read — and it's always synchronized with the code.


References

This article is based on official documentation from November 2026; please refer to the latest version of the project README / docs for command and artifact formats.

Comments

Top 1 from juejin.cn, machine-translated. The original thread is authoritative.

落魄前端工程师

We've already adopted this, and it's a long, cumbersome mess.