OpenSpec 实践指南

面向没用过 OpenSpec 的工程师:读完即可在自己的项目里跑通"探索 → 提案 → 实现 → 归档"的完整闭环。本文基于对 Fission-AI/OpenSpec 仓库(README、docs/、schemas/、skills/ 以及其自用的 openspec/ 目录)的实际阅读整理,所有命令与文件路径均来自仓库本身。

1. 项目定位与核心理念

OpenSpec 是一个规范驱动开发(SDD)的轻量框架:在你和 AI 编码助手之间加一层"规格层",让双方在写任何代码之前先就"要构建什么"达成一致。README 里写的理念(philosophy)直接决定了它的设计:

→ fluid not rigid                流动,不僵硬
→ iterative not waterfall        迭代,不瀑布
→ easy not complex               简单,不复杂
→ built for brownfield not just greenfield   为存量项目而生,不只服务绿地项目
→ scalable from personal projects to enterprises

落地为三个关键设计决策:

1.1 Delta spec(增量规格)模式

这是 OpenSpec 最核心的概念。规格分两层存放:

变更完成归档时,delta 被合并回主规格:ADDED 追加、MODIFIED 替换、REMOVED 删除。于是主规格始终与代码同步演进,而每一次演进的历史都保留在 openspec/changes/archive/ 里。

1.2 动作而非阶段(actions, not phases)

传统 SDD 工具(README 点名对比了 GitHub Spec Kit)把工作流锁死成"规划阶段 → 实现阶段 → 完成"的线性门禁。OpenSpec 认为真实工作不是这样:你实现到一半发现设计错了,就得回去改规格再继续。所以它的命令是随时可做的动作,工件之间的依赖关系(proposal → specs → design → tasks)只是"使能器"(enabler),不是强制门禁——任何工件任何时候都可以回头修改。

1.3 规格随代码演进,而非一次性文档

变更文件夹本身就是审计轨迹:proposal 记录意图,delta spec 记录行为变化,design 记录技术决策,tasks 记录执行进度。归档后整个文件夹移入 archive/YYYY-MM-DD-<name>/,形成可追溯的历史。OpenSpec 仓库自己就是这么用的——它的 openspec/specs/ 下有 30 多个能力域规格,openspec/changes/archive/ 下积累了从 2025-01 到 2026-01 的几十个已归档变更,可以直接当真实样例浏览。

2. 安装与初始化

2.1 安装 CLI

前置条件:Node.js 20.19.0 或更高(用 node --version 确认)。

npm install -g @fission-ai/openspec@latest
# 也支持 pnpm / yarn(1.x) / bun / deno / nix,见 docs/installation.md

2.2 在项目里初始化

cd your-project
openspec init
# 或非交互地指定工具(多个用逗号):
openspec init --tools claude,cursor

init 做三件事:

  1. 创建 openspec/ 目录骨架:specs/、changes/archive/、config.yaml;已有 openspec/ 目录时会刷新但不碰你的 specs 和 changes。
  2. 为你选中的 AI 工具生成接入文件。以 Claude Code 为例,生成 .claude/skills/openspec-*/SKILL.md(技能)和 .claude/commands/opsx/<id>.md(斜杠命令),之后你在对话里直接输入 /opsx:propose 即可。不同工具拼写不同:Cursor / GitHub Copilot 是 /opsx-propose,Amazon Q 是 @opsx-propose,Codex 是 $openspec-propose——init 会打印你这套工具的正确写法。
  3. 引导你创建项目配置 openspec/config.yaml(可选但推荐)。
两套命令别搞混(官方文档称这是新手最常见的绊脚点):openspec ... 在终端里跑;/opsx:... 在 AI 助手的对话框里输入。没有单独的"交互模式"要启动,直接在聊天框敲斜杠命令即可。

2.3 工作流 profile:core 与 expanded

默认安装的是 core profile,包含 6 个命令:propose、explore、apply、update、sync、archive——对大多数人够用。如果想要展开式工作流命令(new、continue、ff、verify、bulk-archive、onboard),执行:

openspec config profile   # 选择工作流配置
openspec update           # 重新生成 agent 指令与命令文件

另外,升级 OpenSpec 版本后也应在每个项目里跑一次 openspec update 刷新 agent 指令。

2.4 config.yaml:给 AI 注入项目上下文

openspec/config.yaml 的 context 会被注入到所有工件的生成指令里(包在 <context> 标签中),rules 按工件类型注入(包在 <rules> 标签中)。OpenSpec 仓库自己的 config 就是一个好例子:

# openspec/config.yaml
schema: spec-driven

context: |
  技术栈:TypeScript、React、Node.js
  API 约定:RESTful,JSON 响应
  测试:Vitest 单测,Playwright 端到端

rules:
  proposal:
    - 必须包含回滚方案
    - 标明受影响的团队
  specs:
    - 场景使用 Given/When/Then 格式
  design:
    - 复杂流程要画时序图

注意:context 上限 50KB;文件名必须是 config.yaml 而不是 .yml;改动即时生效。

3. 完整工作流

两条路径,按需选择:

快路径(core,默认):
  /opsx:explore(可选)──► /opsx:propose ──► /opsx:apply ──► /opsx:sync(可选)──► /opsx:archive

展开路径(expanded):
  /opsx:new ──► /opsx:ff 或 /opsx:continue ──► /opsx:apply ──► /opsx:verify ──► /opsx:archive
命令做什么读 / 写什么文件
/opsx:explore不写任何工件的"思考伙伴":读代码、比较方案、收敛模糊想法只读代码库;不创建任何文件
/opsx:propose <名字或描述>一步创建变更文件夹并生成全部规划工件(默认快路径)写 changes/<name>/ 下的 proposal.md、specs/、design.md、tasks.md
/opsx:new只搭变更脚手架(展开路径)写 changes/<name>/ 目录与元数据
/opsx:continue按依赖图一次创建"下一个就绪的"工件,可反复调用增量写各个工件;通过 openspec status --json 判断就绪状态
/opsx:fffast-forward:一次生成全部规划工件(想清楚时用)同 propose 的工件集合
/opsx:apply [name]按 tasks.md 逐项实现,边做边勾掉 checkbox;实现中发现计划不对就改工件再继续读全部工件;写代码;更新 tasks.md 勾选状态
/opsx:update <name>修订已有规划工件并保持彼此一致(只改计划,不碰代码,也不补建缺失工件)改 proposal/specs/design/tasks
/opsx:verify归档前校验实现是否与 specs / tasks / design 一致(展开路径)读工件 + 代码,产出核对结论
/opsx:sync不归档,直接把当前变更的 delta 合并进主 specs/,变更保持激活写 openspec/specs/<capability>/spec.md
/opsx:archive完工归档:提示先 sync,然后把变更文件夹移入 archive合并 delta 到主 specs;移动到 changes/archive/YYYY-MM-DD-<name>/
/opsx:bulk-archive / /opsx:onboard批量归档多个已完成变更 / 端到端引导式教学—

3.1 探索:/opsx:explore

官方文档的原话:"如果只从文档里带走一个习惯,那就是——不确定的时候,先 explore 再 propose。"AI 助手很"勤快",需求含糊时它会自信地构建出错误的东西。explore 是一场零成本的对话:它读你的代码、摆出几种方案的取舍、帮你把模糊想法收敛成可构建的范围,不创建变更文件夹、不写任何工件、不改代码。想清楚之后自然过渡到 propose。适合的场景:知道问题但不知道方案、在多个技术路线间犹豫、刚接手一个代码库、怀疑工作量比看起来大或小。

3.2 提案:/opsx:propose(或 new → continue / ff)

知道要做什么之后:

You: /opsx:propose add-dark-mode

AI:  Created openspec/changes/add-dark-mode/
     ✓ proposal.md — 为什么做、改什么
     ✓ specs/       — 需求与场景(delta)
     ✓ design.md    — 技术方案
     ✓ tasks.md     — 实现清单
     Ready for implementation!

展开路径把它拆成两步:/opsx:new 只建脚手架;然后想得清楚就 /opsx:ff 一次生成全部规划工件,边想边定就反复 /opsx:continue 一次建一个(它会按依赖图告诉你哪些工件已就绪、创建哪个、建完解锁了什么)。

底层机制:skill 通过 openspec status --change "<name>" --json 查询工件状态(done / ready / blocked),再用 openspec instructions <artifact> --json 拿到模板与上下文,一次只建一个工件。依赖图是 DAG:proposal → specs、design → tasks,apply 阶段要求 tasks 存在。

规划与实现有明确边界。propose 这个 skill 自带"planning boundary"规则:它只创建规划工件,即使你最初的请求是"帮我构建 XX",它生成完工件也会停下来,等你明确说开始才进入 apply。这是刻意设计——你拿到计划后应先评审,再放行写代码。

3.3 实现:/opsx:apply

/opsx:apply 按 tasks.md 逐项实现并勾选 checkbox。多个变更并行时可以 /opsx:apply <name> 指定;不指定时它从对话上下文推断,推断不出来就列出候选让你选。这是"流动"理念体现得最明显的环节:实现中发现设计错了,直接改 design.md 或 specs,然后让 apply 从断点继续,不需要推倒重来。结构性的大改可以用 /opsx:update 修订规划工件并保持它们彼此一致(一个设计改动可能反向波及 proposal)。

什么时候该 update、什么时候该新开一个 change?文档给了启发式:意图相同、范围收窄、学习驱动的修正 → update;意图根本变了、范围爆炸、原变更本身可以独立完工 → 先归档原变更,再新开一个。"Update 保留上下文,new 提供清晰度",类比 git 分支:同一个特性内持续提交,真正不同的工作才开新分支。

3.4 验证:/opsx:verify(展开路径)

归档前用 /opsx:verify 核对实现与工件是否一致:规格里的场景是否都实现了、tasks 是否全部完成、设计决策是否被遵守。发现问题就回到 apply(修实现)或 update(修计划)。

3.5 同步与归档:/opsx:sync、/opsx:archive

/opsx:sync 是可选步骤:把当前变更的 delta 合并进主 openspec/specs/,但变更保持激活、不归档。它应用整个 delta——REMOVED 段落下的需求会从主规格中删除、改名的需求就地换标题,delta 没提到的内容原样保留。适用场景:想让主规格先反映新行为、另一个并行变更需要建立在本次新增的规格之上、归档前想先 review 合并结果。

/opsx:archive 是收尾:如果你还没 sync 它会先提示你 sync;确认后把变更文件夹移入 openspec/changes/archive/YYYY-MM-DD-<name>/(名字已有日期前缀就不叠加)。归档语义:ADDED 追加进主规格、MODIFIED 整体替换、REMOVED 删除。CLI 等价物是 openspec archive <change-name> --yes(跳过确认,仍会先校验再应用 delta 并归档)。

4. 工件格式详解

默认 schema 叫 spec-driven,定义在仓库的 schemas/spec-driven/schema.yaml,四个工件的依赖关系与生成指令都在其中。你也可以用 openspec schema fork spec-driven my-workflow 复制一份自定义(比如加一个 research 工件放在 proposal 之前),schema 存放在 openspec/schemas/(项目级,纳入版本控制)。

4.1 proposal.md —— 为什么做、做什么

模板(schemas/spec-driven/templates/proposal.md)固定四个段落:

保持 1–2 页,聚焦 why 而非 how(实现细节属于 design.md)。

零 delta 校验:每个变更必须声明至少一个能力(新增或修改),否则 openspec validate 会拒绝——除非你在变更目录的 .openspec.yaml 里显式设置 skip_specs: true(仅用于纯重构、工具、文档类不变更行为的场景)。不要为了满足校验而编造需求。

4.2 specs/ 下的 delta spec —— 行为契约

spec 是行为契约,不是实现计划。写:用户或下游系统依赖的可观察行为、输入输出与错误条件、外部约束(安全/隐私/兼容性)、可测试的场景。不写:内部类名/函数名、库与框架选型、逐步实现细节。快速判据:如果实现换了而外部可见行为不变,那它就不该出现在 spec 里。

格式规则(来自 schema.yaml 的生成指令,条条都是硬约束):

4.3 design.md —— 怎么做(按需创建)

只在以下情况创建:跨模块/跨服务的横切变更或新架构模式;引入新的外部依赖或重大数据模型变更;安全、性能、迁移复杂度;开工前需要先把技术决策定下来的歧义点。段落:Context(只写解释方案所需的现状与约束)、Goals / Non-Goals、Decisions(关键技术选择及理由、考虑过的备选)、Risks / Trade-offs(格式:[风险] → 缓解)、Migration Plan(部署与回滚,如适用)、Open Questions(可以安全延后的未知项——会改变 specs、方案或任务拆分的问题不能留作 open question,应当场问用户)。

4.4 tasks.md —— 实现清单

硬性格式要求:相关任务归在 ## 编号标题 下;每个任务必须是 checkbox:- [ ] X.Y 任务描述——apply 阶段靠解析这个格式跟踪进度,不用 - [ ] 的任务不会被跟踪。任务要小到能在一个会话内完成、按依赖排序,且每个任务都要写明如何验证完成(一个测试、一条命令、一个可观察行为)。

5. 中文示例:添加深色模式

以下示例以仓库 README / getting-started 文档中的 add-dark-mode 示例和真实变更(如 openspec/changes/add-change-stacking-awareness/)的结构为底,改写为中文。

5.1 发起变更

You: /opsx:propose add-dark-mode
AI:  Created openspec/changes/add-dark-mode/
     ✓ proposal.md  ✓ specs/  ✓ design.md  ✓ tasks.md

5.2 proposal.md

## Why

用户反馈夜间使用应用时眼睛疲劳,希望提供深色模式。
目前界面只有亮色主题,长期夜间使用体验差。

## What Changes

- 在设置页新增主题切换开关(亮色 / 深色)
- 支持跟随系统偏好(prefers-color-scheme)作为默认主题
- 用户选择持久化到 localStorage,跨会话生效

## Capabilities

### New Capabilities
- `ui-theming`: 应用的主题选择能力,包括手动切换、
  系统偏好检测与偏好持久化

### Modified Capabilities
(无——本次不修改任何既有能力的需求级行为)

## Impact

- 代码:全局样式表、设置页、应用入口组件
- 依赖:无新增依赖(纯 CSS 自定义属性 + React context)
- 兼容性:不支持 CSS 自定义属性的旧浏览器保持亮色主题

5.3 specs/ui-theming/spec.md(delta spec)

注意这是新能力的 delta,所以以 ## Purpose 开头:

## Purpose

让用户可以在亮色与深色主题之间切换,并记住自己的选择。

## ADDED Requirements

### Requirement: 主题选择
系统 SHALL 允许用户在亮色与深色主题之间切换,
未做选择时 SHALL 默认跟随系统偏好。

#### Scenario: 手动切换主题
- **WHEN** 用户点击主题切换开关
- **THEN** 界面立即切换到所选主题
- **AND** 该选择被持久化,跨会话生效

#### Scenario: 跟随系统偏好
- **WHEN** 没有任何已保存偏好的用户打开应用
- **THEN** 使用操作系统的 prefers-color-scheme 作为当前主题

#### Scenario: 持久化偏好优先于系统偏好
- **WHEN** 已保存主题偏好的用户打开应用
- **THEN** 使用已保存的偏好,忽略系统设置

如果这次变更同时要修改一个既有能力(例如把既有 settings 能力里"设置项即时生效"的需求改掉),就在 changes/add-dark-mode/specs/settings/spec.md 里写(注意用既有能力的精确路径,且不加 Purpose):

## MODIFIED Requirements

### Requirement: 设置项即时生效
系统 SHALL 在用户修改任何设置项后立即应用,
(此前为:仅在外观类设置上即时生效,其余需刷新页面。)

#### Scenario: 修改外观设置
- **WHEN** 用户修改主题或字体设置
- **THEN** 界面无刷新立即反映新设置

5.4 design.md

## Context
见 proposal.md - Why。样式目前是硬编码颜色值,集中在全局 CSS。

## Goals / Non-Goals
- Goals: 无新依赖;切换无闪烁;偏好持久化
- Non-Goals: 自定义配色(用户自选色值)、按页面分别设置主题

## Decisions
- 用 CSS 自定义属性承载主题色,亮/暗两套变量:
  理由是不引入 CSS-in-JS 依赖,且旧浏览器可自然降级。
  备选方案(Tailwind dark: 前缀)被否决:项目未使用 Tailwind。
- 用 React context 管理主题状态,入口组件挂载时读取
  localStorage → 回退到 matchMedia('(prefers-color-scheme: dark)')。

## Risks / Trade-offs
- [SSR 首屏闪烁] → 在 <head> 内联一段读取偏好的同步脚本
- [硬编码颜色的遗漏组件] → apply 阶段全量检索 hex 颜色字面量

## Migration Plan
纯前端变更,直接发布即可;异常时回滚入口组件的主题注入。

5.5 tasks.md

## 1. 主题基础设施

- [ ] 1.1 创建 ThemeContext(light/dark 状态),
       用单元测试验证默认读取系统偏好
- [ ] 1.2 在全局 CSS 定义亮/暗两套自定义属性,
       浏览器 DevTools 中切换 data-theme 可见颜色变化
- [ ] 1.3 实现 localStorage 持久化,
       刷新页面后主题保持

## 2. UI 组件

- [ ] 2.1 实现 ThemeToggle 组件并渲染两种状态
- [ ] 2.2 把开关接入设置页,
       点击后界面立即切换(对应场景:手动切换主题)

## 3. 收尾

- [ ] 3.1 全量检索硬编码颜色值并替换为变量,
       grep 结果为空
- [ ] 3.2 端到端测试覆盖三个 spec 场景并全部通过

5.6 实现与归档

You: /opsx:apply
AI:  Working through tasks...
     ✓ 1.1 Created ThemeContext ... ✓ 3.2 端到端测试通过
     All tasks complete!

You: /opsx:archive
AI:  Archiving add-dark-mode...
     ✓ Merged specs into openspec/specs/ui-theming/spec.md
     ✓ Moved to openspec/changes/archive/2026-09-15-add-dark-mode/

归档后,openspec/specs/ui-theming/spec.md 成为主规格(delta 的 Purpose 被复制进来,ADDED 的需求去掉 "ADDED" 段落头、挂到 ## Requirements 下),样子像这样——以下是 OpenSpec 仓库真实主规格(openspec/specs/cli-init/spec.md)的节选,展示了归档后的实际形态:

# CLI Init Specification

## Purpose

The `openspec init` command SHALL create a complete OpenSpec
directory structure in any project, ...

## Requirements

### Requirement: Directory Creation

The command SHALL create the OpenSpec directory structure with
config file.

#### Scenario: Creating OpenSpec structure

- **WHEN** `openspec init` is executed
- **THEN** create the following directory structure:
  openspec/
  ├── config.yaml
  ├── specs/
  └── changes/
      └── archive/

对照可见:主规格 = # 标题 + ## Purpose + ## Requirements(不再有 ADDED/MODIFIED 字样),需求与场景的写法与 delta 完全一致。后续的变更再用新的 delta 在它基础上叠加。

6. 目录结构示例

一个采用 OpenSpec 的项目,初始化并完成若干变更后长这样:

your-project/
├── .claude/
│   ├── commands/opsx/          # init 生成的斜杠命令(/opsx:propose 等)
│   └── skills/openspec-*/      # init 生成的技能(SKILL.md)
├── openspec/
│   ├── config.yaml             # 项目配置:schema / context / rules
│   ├── specs/                  # 主规格:系统当前行为的 source of truth
│   │   ├── auth/
│   │   │   └── spec.md
│   │   └── ui-theming/
│   │       └── spec.md
│   └── changes/                # 进行中的变更(每个一个文件夹)
│       ├── add-order-idempotency-key/
│       │   ├── proposal.md
│       │   ├── design.md
│       │   ├── tasks.md
│       │   └── specs/          # delta spec(只写变化的部分)
│       │       └── orders/
│       │           └── spec.md
│       └── archive/            # 已完成变更的审计历史
│           ├── 2026-09-15-add-dark-mode/
│           └── 2026-08-02-add-user-auth/
└── src/ ...                    # 你的代码

7. CLI 速查(终端里用)

命令用途
openspec init [--tools <ids>]初始化项目、配置 AI 工具接入
openspec update升级后刷新 agent 指令与命令文件
openspec list [--specs]列出进行中的变更;加 --specs 列出能力清单
openspec show <name> [--type spec]查看某个变更或某条主规格的详情
openspec validate <name>校验变更的 spec 格式(场景层级、delta 段落等)
openspec status --change <name> [--json]查看工件状态(done / ready / blocked)
openspec view交互式 dashboard
openspec archive <name> --yes命令行归档(跳过确认,仍先校验并应用 delta)
openspec schemas / openspec schema fork|init|validate|which管理自定义工作流 schema
openspec config profile切换 core / expanded 工作流命令集

8. 实践建议与常见坑

场景标题必须恰好四个 #。#### Scenario: 写成 ### 或用 bullet 列表会静默失败——不报错,但场景没被解析。写完后跑 openspec validate <change> 是最便宜的保险。

9. 与 AI-Native SDLC Playbook 工件链的映射

The AI-Native SDLC playbook 提出的工件链是 intent.md → spec.md → plan.md → diff+tests → PR+review findings → incident record。OpenSpec 的产物大致对应:

Playbook 环节OpenSpec 对应物说明
intent.mdproposal.md(Why / What Changes)捕获意图与范围;/opsx:explore 相当于意图成形前的对话式探索
spec.mdchanges/<name>/specs/ 的 delta specWHEN/THEN 场景是可验证的行为契约;归档后并入主 specs/ 成为系统行为的长期事实来源
plan.mddesign.md + tasks.mddesign 回答 how(技术决策与取舍),tasks 是可勾选的执行清单
diff + tests/opsx:apply 产出的代码 + tasks.md 勾选进度tasks 要求每项写明验证方式,天然把测试纳进工件
PR + review findings/opsx:verify + 人工评审 proposal/planOpenSpec 把 review 重心前移到"写代码前评审计划",verify 做归档前的一致性核对
incident record / 审计轨迹changes/archive/YYYY-MM-DD-*/每次变更的完整工件原样归档,构成可追溯历史

差异也值得注意:playbook 强调循环(生产环境违规触发新的 intent),OpenSpec 的循环体现在"主规格 → 新 delta → 合并回主规格"的规格演进上,尚未内建生产信号回流的机制;playbook 的控制分层(skill 建议性 / hook 强制性)在 OpenSpec 里表现为 skill 给 AI 的生成指令 + openspec validate 的确定性格式校验,人类审批则落在 propose 之后的计划评审 gate。

一句话上手:npm i -g @fission-ai/openspec@latest → 项目里 openspec init --tools claude → 对话框里 /opsx:explore 想清楚 → /opsx:propose <name> 出计划并评审 → /opsx:apply 实现 → /opsx:archive 归档。主规格随每次归档自动演进,历史留在 archive 里。