为什么需要 OpenSpec

在 AI 编程时代,真正的难点往往不是“AI 会不会写代码”,而是“AI 能不能稳定写出你真正想要的代码”。问题往往不在模型能力,而在于需求、边界、约束和验收标准没有被稳定地表达出来。当意图没有沉淀为可复用的工程事实,AI 就只能在模糊上下文里“猜”。

OpenSpec 解决的正是这个问题。它的核心思想可以概括成一句话:先对齐规范,再生成代码(align before code)。与其把 AI 当成一个只看提示词的即时执行器,不如把它放进一套可追溯、可迭代、可沉淀的规范工作流里。

OpenSpec 既不是重量级流程平台,也不是传统瀑布式文档系统。从实践上看,它更像一套轻量的仓库内协议:

  • specs/ 保存系统当前已经成立的事实;
  • changes/ 保存本次准备引入的未来变化;
  • 用 proposal、spec、design、tasks 把“为什么改、改成什么、怎么实现”拆开表达;
  • 用 sync 和 archive 把一次变更逐步沉淀为下一次变更的上下文。

它的设计哲学,基本可以概括为四点:

  • Fluid not rigid:规范是活文档,不是一次性文书。
  • Iterative not waterfall:支持边做边校正,而不是阶段锁死。
  • Easy not complex:只保留真正影响实现质量的文档。
  • Brownfield-first:优先服务已有项目,而非只适配全新仓库。

这也是 OpenSpec 最值得重视的地方:它不要求团队先建立一套庞大的流程,再允许 AI 参与;它的目标恰恰相反——用最小的文档负担,换取更稳定的人机对齐质量

命令分工与推荐工作流

如果只先记一条主线,记住下面这条工作流就够了:

1
2
3
4
5
6
7
8
9
10
11
12
13
init

propose / new → continue

[人工审查 proposal / spec / design / tasks]

apply

verify

sync

archive

这条主线概括了 OpenSpec 中较常见的一种节奏:

  1. 先建立工作目录
  2. 再为一次变更生成工件
  3. 在实现前完成人类审查
  4. 随后按任务清单实现代码
  5. 最后按需校验、沉淀并归档

安装与初始化

1
2
3
npm install -g @fission-ai/openspec@latest
cd your-project
openspec init

初始化后,项目里通常会出现一个 openspec/ 工作目录:

1
2
3
4
5
your-project/
└── openspec/
├── config.yaml # 可选的项目上下文与规则
├── specs/ # 主 Specs:当前事实
└── changes/ # 变更目录:未来变化

有些 AI 工具还会在仓库根目录维护自己的配置文件,例如 AGENTS.mdCLAUDE.md。这类文件更适合视为AI 工具层配置,而不是 OpenSpec 自身的流程工件。它们可以和 openspec/ 共存,但职责不同,后文会单独说明。

核心命令怎么分工

命令 作用 何时使用 典型产出
openspec init 初始化工作目录 第一次接入 OpenSpec openspec/ 基础骨架
/opsx:new 创建 change 容器并给出首个工件模板 想逐步推进时 一个新的 change 目录
/opsx:continue 按依赖顺序继续生成下一个工件 new 之后继续补齐工件 单个新增工件
/opsx:propose 一次生成到可实现状态 需求比较清楚,想快速进入审查 proposal.mdspecs/design.mdtasks.md
/opsx:ff 快速生成到 apply-ready 与 propose 类似,强调更快进入实现 同上
/opsx:explore 通常用于调研、澄清、比较方案,可按需回写工件 需求不清、方案摇摆、边界不明 分析结论,可选的工件更新
/opsx:apply 按任务清单实现代码 工件已审查通过后 代码变更、任务勾选
/opsx:verify 扩展工作流中的一致性核对动作 希望在归档前补充核查时 completeness / correctness / coherence 报告
/opsx:sync 扩展工作流中的 spec 合并动作 想在归档前单独预览或执行 spec 合并时 更新后的主 Specs
/opsx:archive 默认工作流里的收口动作:验证变更、更新主 Specs 并归档 change 一个变更收口时 更新后的主 Specs + 归档目录

两个 Profile:默认命令集与扩展命令集

在理解命令分工之前,有一个关键背景必须先说清楚:OpenSpec 的命令集分为两个 profile,它们决定了你能用哪些命令。

core profile(默认):安装后开箱即用,只包含 4 个命令:

1
/opsx:propose   /opsx:explore   /opsx:apply   /opsx:archive

这是大多数用户的日常路径。archive 是唯一的收口动作,它会在归档时自动处理 spec 合并。

expanded workflow(扩展命令集):需要手动切换,才能解锁额外命令:

1
/opsx:new   /opsx:continue   /opsx:ff   /opsx:verify   /opsx:sync   /opsx:bulk-archive

切换方式:执行 openspec config profile,选择 workflows,再执行 openspec update

这两个 profile 的区别,就是本文后续提到"默认工作流"和"扩展工作流"时所指的实质差异。如果你只用默认 core profile,syncverify 命令根本不会出现在你的工具集里;它们是扩展命令集里的可选动作,专门服务于需要更精细控制的场景。但这并不意味着 core 用户永远用不到 openspec/specs/——archive 在收口时会自动把 Delta Specs 合并进主 openspec/specs/,core 用户每次归档都在更新它,只是这个合并动作由 archive 内部完成,不需要单独调用 sync

更稳妥的理解:命令是动作,不是关卡

OpenSpec 的强项不在于把你锁进固定阶段,而在于把一次变更拆成几个可回退、可重做、可校正的动作:

  • propose 负责形成可讨论、可审查的变更表达;
  • explore 通常用于澄清不确定性、调查问题和比较方案;
  • apply 负责把已确认的规范转成实现;
  • verify(扩展命令)负责检查"做出来的东西"是否真的对齐"说清楚的东西";
  • archive 负责完成一次变更的收口,内部会自动处理 spec 合并;sync(扩展命令)则可以把这个合并动作单独提前执行。

因此,更稳妥的理解是:默认主流程通常围绕 propose → explore/apply → archive 展开;如果你希望在归档前补充核查或单独处理 spec 合并,则需要先切换到扩展命令集,再引入 verifysync。如果你把这条主线记住,后面的大部分细节都会自然落位。

核心概念

要真正把 OpenSpec 用顺,需要抓住的其实只有三组概念:当前事实与未来变化、变更工件、沉淀动作

1. specs/:系统当前事实

openspec/specs/ 存放的是系统已经成立的行为契约。它回答的问题是:

这个系统现在已经具备什么能力?

典型写法会使用 SHALL / MUST 一类语义约束词,再配合 Gherkin 风格场景描述行为边界:

1
2
3
4
5
6
7
8
9
10
## Requirement: Create Order
The system SHALL accept POST /orders requests and create an order record.

### Scenario: Successful order creation
- **WHEN** user submits a valid order with sufficient inventory
- **THEN** the system creates the order and returns HTTP 201 with order details

### Scenario: Insufficient inventory
- **WHEN** inventory service returns insufficient stock
- **THEN** the system returns HTTP 409 and does NOT create an order record

这里描述的不是“打算做什么”,而是“已经应该成立的行为”。因此,specs/ 更像长期知识库,而不是临时草稿区。

2. changes/:本次准备引入的未来变化

openspec/changes/ 不是单个文件,而是一组变更目录。每一个 change 目录代表一次独立的需求、重构或能力调整。对应地,它回答的问题是:

这次准备把系统改成什么样?

可以把两者记成一句话:

  • specs/ = current truth
  • changes/ = proposed future state

这也是 OpenSpec 区别于很多“需求文档 + 代码实现”松散流程的关键:它把当前事实未来变更明确分层了。

3. change 目录里的四类工件

一个典型的 change 目录通常长这样:

1
2
3
4
5
6
7
8
9
openspec/
└── changes/
└── add-create-order/
├── proposal.md
├── design.md
├── tasks.md
└── specs/
└── order-creation/
└── spec.md

四类工件的职责分别是:

工件 作用 重点问题
proposal.md 说明为什么改、改什么、影响什么 Why / What / Impact
specs/.../spec.md 把需求翻译成可验证的行为契约 系统应该如何表现
design.md 记录实现方案、技术权衡、关键设计 准备怎么做
tasks.md 把实现拆成 AI 可执行任务清单 先做什么,后做什么

一个常见误解,是把 proposal、spec、tasks 当成彼此重复的文档。实际上,它们承担的是不同语义:

1
2
proposal.md   →   specs/   →   tasks.md
为什么改 改成什么 怎么落地

OpenSpec 之所以通常先产出 proposal,再产出 spec 和 tasks,不是因为 spec 不重要,而是因为没有先对齐方向,再精确描述行为,往往只会得到“方向错误但表达严谨”的产物

4. Delta Specs:只描述这次变更的增量

change 目录里的 specs/ 不是主 Specs 的完整拷贝,而是增量规范。它通常围绕三类动作展开:

  • ADDED:新增需求
  • MODIFIED:修改需求
  • REMOVED:删除需求

这种设计有两个直接好处:

  1. 一次变更的边界更清楚;
  2. AI 更容易理解“这次到底改哪里”。

5. syncarchive 的关系,以及主 specs/ 是怎么更新的

这是全文里最容易混淆的一组动作,也是最值得说清楚的地方。

先回答一个根本问题:什么命令会把内容写入 openspec/specs/(主 Specs)?

答案是 syncarchive,而且两者做的是同一件事的不同触发时机:把 change 目录里的 Delta Specs(增量规范)合并进主 openspec/specs/ 目录。具体来说,Delta Specs 里的 ADDED 段落会追加到主 Specs,MODIFIED 段落会替换主 Specs 中对应的需求,REMOVED 段落会从主 Specs 中删除对应内容。

两个命令的区别在于:

  • /opsx:sync(扩展命令集):只做合并,不归档。执行后,Delta Specs 的内容被合并进 openspec/specs/,但 change 目录本身保持不动,仍然是活跃状态。适合长期运行的变更、多个并行变更需要共享最新 specs 基线,或者想在归档前单独预览合并结果的场景。

  • /opsx:archive(默认命令集):先合并,再归档。执行时,如果 Delta Specs 还没有被 sync 过,archive 会先提示你确认是否执行合并(或自动执行),把增量内容写入 openspec/specs/;然后再把整个 change 目录(包含 proposal、design、tasks、Delta Specs 等所有工件)移动openspec/changes/archive/YYYY-MM-DD-<name>/,作为历史记录保留。

因此,archive 之后,openspec/specs/ 里存放的是合并了本次变更的最新主 Specs;而 openspec/changes/archive/ 里存放的是整个 change 目录的完整历史快照,包括 Delta Specs 在内的所有工件都会随之归档。两个目录各司其职,不会混淆。

用一句话总结:sync 是把"增量合并进主 Specs"这个动作单独提前执行;archive 是在收口时自动完成这个合并,然后再把整个 change 目录移入历史存档。 因此,core profile 用户同样会更新 openspec/specs/——只是这个动作由 archive 在收口时一并完成,而不需要单独调用 sync

下图展示了两条路径下各目录的文件流向:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
openspec/
├── specs/ ← 主 Specs(系统当前事实的长期知识库)
│ └── *.md 由 sync 或 archive 合并写入

├── changes/
│ ├── <name>/ ← 活跃 change 目录
│ │ ├── proposal.md
│ │ ├── design.md
│ │ ├── tasks.md
│ │ └── specs/ ← Delta Specs(增量规范,仅描述本次变更)
│ │ └── *.md
│ │
│ └── archive/
│ └── YYYY-MM-DD-<name>/ ← 归档后的完整 change 快照
│ ├── proposal.md (archive 执行后,整个 change 目录移动至此)
│ ├── design.md
│ ├── tasks.md
│ └── specs/
│ └── *.md

└── config.yaml ← 可选的项目配置入口

core profile 路径/opsx:archive):Delta Specs → 合并进 openspec/specs/ → 整个 change 目录移动到 archive/,一步到位。

expanded workflow 路径/opsx:sync + /opsx:archive):先单独执行 sync 把 Delta Specs 合并进 openspec/specs/,change 目录保持活跃;之后再执行 archive 完成归档(此时 sync 已做过,archive 跳过合并步骤,直接移动目录)。

6. config.yaml:可选的项目配置入口

openspec/config.yaml 不是强制前置条件,但它通常值得填写。官方文档里,它不只是项目上下文入口,还常用于补充 schema 与工件规则。常见用途包括:

  • context:项目背景、技术栈、架构模式、领域信息;
  • rules:团队规则、接口约束、错误处理约定等;
  • schema 相关配置:指定当前项目使用哪套工作流 schema(即工件类型、依赖顺序、生成模板)。注意:schema 定义的是"生成什么工件、按什么顺序",而不是"工件内容的 Markdown 格式"——specsproposaldesigntasks 等 md 文件的内部结构(如 ## Requirements### Requirement: 等标题层级)在当前版本是硬编码的,无法通过 schema 配置修改。如果你想自定义工作流(比如增减工件类型、调整依赖关系),可以用 openspec schema initopenspec schema fork 创建自定义 schema,但工件内容的格式约定仍然固定。

例如:

1
2
3
4
5
6
7
8
9
context:
description: "订单服务,负责订单全生命周期管理"
tech_stack: "Spring Boot 3.x, Java 17, MySQL 8.0"
architecture: "微服务架构,Controller-Service-DAO 分层"

rules:
- "Controller 层只做参数校验和响应封装"
- "统一异常处理通过 GlobalExceptionHandler"
- "响应格式:{ code, message, data }"

它的作用并不神秘:为 OpenSpec 工作流提供结构化、稳定、可复用的项目配置。如果项目简单,AI 也许能从代码库自行推断出足够信息;如果项目复杂,config.yaml 往往能明显改善 proposal 或其他工件草案的质量。

7. 目录结构应该怎么读

把前面的概念合起来,OpenSpec 目录可以这样理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
your-project/
└── openspec/
├── config.yaml # 可选:项目上下文与规则
├── specs/ # 主 Specs:系统当前事实
│ └── <capability>/
│ ├── spec.md
│ └── design.md # 可选
└── changes/ # 变更目录:未来变化
├── <change-id>/
│ ├── proposal.md
│ ├── design.md
│ ├── tasks.md
│ └── specs/
└── archive/

如果只用一句话总结:specs/ 是沉淀层,changes/ 是工作层,config.yaml 是上下文层。

完整实操步骤(以“新增下单接口”为例)

把前面的概念放进一次具体变更里,整条链路就会清楚很多。下面用一个常见的后端需求演示一遍:新增 POST /orders 接口,下单时同步扣减库存,库存不足返回 409

步骤一:初始化并补充上下文

先执行:

1
openspec init

如果项目已经比较复杂,建议补充 config.yaml。你可以让 AI 先生成初稿,再手动审查:

1
2
请根据当前项目代码结构,帮我补充 openspec/config.yaml,
包括项目描述、技术栈、架构模式和关键规则。

这一步的目标不是“写文档”,而是减少后续 proposal 的理解偏差。

步骤二:创建 change

如果你想一步到位生成主要工件,可以直接使用 /opsx:propose

1
2
3
帮我创建一个 OpenSpec 变更提案:
新增用户下单接口(POST /orders),
下单时同步调用库存服务扣减库存,库存不足时返回 409,订单不创建。

生成后的 change 目录可能类似这样:

1
2
3
4
5
6
openspec/changes/add-create-order/
├── proposal.md
├── design.md
├── tasks.md
└── specs/
└── order-creation/spec.md

如果你更想逐步控制,也可以先 /opsx:new,再通过 /opsx:continue 一件件补齐。

步骤三:审查工件

这是整条链路中最关键的人类审查节点。在这里不要急着进入实现,而是先问四个问题:

  1. proposal.md 是否把范围和影响说清楚了?
  2. spec.md 是否覆盖了关键场景和边界情况?
  3. design.md 是否解释了关键技术选择?
  4. tasks.md 是否足够具体,能被 AI 顺序执行?

例如,一个简洁的 proposal.md 可以长这样:

1
2
3
4
5
6
7
8
9
10
11
## Why
当前系统支持订单查询,但缺少订单创建能力。

## What Changes
- 新增 POST /orders 接口
- 下单时同步调用库存服务扣减库存
- 库存不足时返回 409,订单不创建

## Impact
- Affected specs: order-creation
- Affected code: OrderController, OrderService, InventoryClient

对应的增量 spec 可以这样表达:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## ADDED Requirements

### Requirement: Create Order
The system SHALL accept POST /orders requests and create an order record.

#### Scenario: Successful order creation
- **WHEN** user submits a valid order with sufficient inventory
- **THEN** the system creates the order and returns HTTP 201 with order details

#### Scenario: Insufficient inventory
- **WHEN** inventory service returns insufficient stock
- **THEN** the system returns HTTP 409 and does NOT create an order record

#### Scenario: Missing required fields
- **WHEN** required parameters are missing from the request
- **THEN** the system returns HTTP 400 with validation error details

tasks.md 则应该把实现拆成可执行步骤:

1
2
3
4
5
6
7
8
9
10
11
## 1. Implementation
- [ ] 1.1 创建 CreateOrderRequest DTO
- [ ] 1.2 创建 OrderController,暴露 POST /orders
- [ ] 1.3 创建 OrderService,编排下单逻辑
- [ ] 1.4 集成 InventoryClient,执行库存扣减
- [ ] 1.5 实现库存不足时返回 409

## 2. Tests
- [ ] 2.1 测试库存充足时下单成功
- [ ] 2.2 测试库存不足时返回 409
- [ ] 2.3 测试参数缺失时返回 400

如果这里发现问题,先修工件,再进入实现。不要把这一步省略掉。

步骤四:执行实现

确认工件可接受之后,再进入 apply

1
/opsx:apply

或者直接用自然语言告诉 AI:

1
请按照 openspec/changes/add-create-order/ 中的 proposal.md 和 tasks.md 实现代码。

这时 AI 的工作重点,不再是“理解需求”,而是“按已确认的任务清单落地实现”。这也正是前面先补齐 proposal、spec、tasks 的价值。

步骤五:在需要时补充实现对账

如果你希望在归档前额外做一次结构化核对,可以执行:

1
/opsx:verify add-create-order

verify 更适合被理解为扩展工作流里的核对动作。它关注的不是单纯"测试跑没跑",而是至少三个维度:

  • Completeness:任务与需求是否完整落地;
  • Correctness:实现是否偏离 spec;
  • Coherence:实现是否违背设计或项目既有模式。

如果报告中还有关键问题,先修掉,再进入下一步。

/opsx:verify 深度解析

verify 是 OpenSpec 工作流中最容易被低估的命令。它不是一个简单的"跑测试"按钮,而是一次结构化的人机协作对账——让 AI 扮演审计员,系统性地检查"你说要做的"和"你实际做的"之间是否存在偏差。

什么时候应该用 verify

verify 的核心使用时机是:apply 完成之后、archive 之前

1
/opsx:apply  →  /opsx:verify  →  /opsx:archive

更具体地说,以下几种场景特别适合触发 verify:

  • AI 完成了较大范围的实现:任务清单超过 5 项,或涉及多个模块的联动;
  • 实现过程中出现过反复修改:apply 期间有过多次迭代,担心局部修复引入了新的不一致;
  • spec 在 apply 过程中被更新过:规范和实现的同步状态不确定;
  • 准备归档前的最后一道防线:希望在 change 正式成为历史记录之前,做一次完整性确认。

反过来,以下情况可以酌情跳过 verify:

  • 极小的改动(如修改一行注释、调整一个配置值);
  • 紧急热修复,时间窗口极短;
  • 实验性变更,本来就不打算归档。

但即便如此,verify 本身执行很快,不会阻塞后续操作,跑一遍通常利大于弊。

三个验证维度详解

verify 的核心价值在于它把"实现是否正确"这个模糊问题,拆解成三个可独立评估的维度:

Completeness(完整性)

所有任务是否完成?所有需求是否落地?所有场景是否覆盖?

这一维度检查的是数量层面的覆盖

  • tasks.md 中的所有复选框是否已勾选;
  • spec 中定义的每一个 Scenario 是否都有对应的实现;
  • 是否存在"写了一半"的功能——代码存在但逻辑不完整。

典型问题tasks.md 里有 8 项任务,但 apply 只完成了 6 项,剩余 2 项被遗漏。

Correctness(正确性)

实现是否符合 spec 的意图?边界情况是否被正确处理?错误状态是否与 spec 定义一致?

这一维度检查的是语义层面的对齐

  • 接口返回码是否与 spec 中定义的一致(如库存不足应返回 409,而非 400);
  • 边界场景的处理逻辑是否与 spec 描述的行为吻合;
  • 错误处理路径是否覆盖了 spec 中所有的异常场景。

典型问题:spec 定义"库存服务超时返回 503,订单不创建",但实现中超时时仍然创建了订单。

Coherence(一致性)

设计决策是否体现在代码结构中?命名约定是否与 design.md 一致?模式是否保持统一?

这一维度检查的是风格层面的统一

  • design.md 中提到的架构模式(如事件驱动、分层结构)是否在代码中体现;
  • 命名约定是否与 design.md 或项目既有风格一致;
  • 是否出现了"局部正确但整体不协调"的实现——功能对但风格乱。

典型问题design.md 说明使用 CSS 变量管理主题色,但实现中直接硬编码了颜色值。

verify 的输出格式

verify 会输出一份结构化报告,问题按严重程度分为三级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
COMPLETENESS
✓ All 8 tasks in tasks.md are checked
✓ All requirements in specs have corresponding code
⚠ Scenario "Inventory service timeout" has no test coverage

CORRECTNESS
✓ Implementation matches spec intent
✓ Edge cases from scenarios are handled
✓ Error states match spec definitions

COHERENCE
✓ Design decisions reflected in code structure
⚠ Design mentions "event-driven" but implementation uses polling

SUMMARY
─────────────────────────────
Critical issues: 0
Warnings: 2
Ready to archive: Yes (with warnings)
Recommendations:
1. Add test for inventory service timeout scenario
2. Update design.md to reflect polling approach, or refactor to event-driven

三个问题级别的含义:

级别 含义 是否阻塞归档
CRITICAL 必须修复,实现与规范存在根本性偏差
WARNING 建议修复,存在潜在风险或不一致
SUGGESTION 可选改进,不影响功能正确性

verify 失败后的正确处理顺序

发现问题后,处理顺序至关重要:

1
2
3
4
5
6
7
8
9
10
查看 verify 报告

区分:是实现问题,还是规范问题?

实现偏离了 spec → 直接修代码
规范本身有缺陷 → 先修 spec / design / tasks,再修代码

重新运行 /opsx:verify 确认

通过后再 /opsx:archive

这个顺序的关键在于:如果只修代码不修规范,下次 AI 再读取过时的 spec,旧问题会重新出现。这就是 spec drift 的典型成因。

与 profile 的关系

需要注意的是,/opsx:verify 属于 OpenSpec 的扩展工作流,在默认的 core profile 下不一定开箱即用。如果你发现命令不可用,可以通过以下方式启用:

1
2
openspec config profile
openspec update

这也解释了为什么文章前面把 verify 描述为"扩展工作流"里的动作——它是可选的增强层,而非默认主流程的强制步骤。

/opsx:explore 深度解析

explore 是 OpenSpec 命令族中最"轻"的一个——它不创建任何文件,不修改任何目录结构,甚至不要求你已经有一个 change。它的全部价值在于:在你还没想清楚之前,给你一个有结构的思考空间。

explore 到底做了什么

当你执行:

1
/opsx:explore 我想给订单系统加一个退款功能,但不确定应该放在哪个服务里

AI 会做以下几件事:

  1. 读取项目上下文:加载 config.yaml、现有的 specs/、以及项目结构
  2. 围绕你的问题展开分析:不是直接给答案,而是列出可能的方案、各自的 trade-off、以及需要进一步澄清的问题
  3. 不产生任何文件输出:整个过程是纯对话式的,所有内容只存在于当前会话中

这和直接问 AI "帮我想想退款怎么做"有什么区别?区别在于 explore 会把项目的现有 spec 和架构约束纳入思考范围。它不是在真空中头脑风暴,而是在你的项目上下文里做有约束的探索。

什么时候应该用 explore

1
2
3
4
5
6
7
8
9
10
explore 的适用场景:
✓ 需求还很模糊,不确定要不要做
✓ 有多个技术方案,想先比较再决定
✓ 想理解某个改动对现有系统的影响范围
✓ 在创建 change 之前,先做一次可行性分析

explore 不适合的场景:
✗ 需求已经明确,直接 propose 或 new 即可
✗ 想要留下持久化的分析记录(explore 不写文件)
✗ 需要团队协作审查的场景(没有工件可供审查)

explore 与 propose 的关系

一个常见的工作模式是 explore → propose

1
2
3
/opsx:explore 退款功能应该怎么设计?
↓ (AI 分析后,你决定了方案)
/opsx:propose 新增退款接口,采用异步退款 + 状态机模式

explore 帮你收窄选项,propose 帮你正式启动。两者之间没有技术上的依赖关系——你完全可以跳过 explore 直接 propose——但在复杂场景下,先 explore 再 propose 能显著减少后续返工。

explore 的局限性

最大的局限是没有持久化。如果你在 explore 中得出了重要结论,需要手动记录下来,或者立即转入 propose/new 流程。一旦会话结束,explore 的分析结果就消失了。

这是一个有意的设计选择:explore 的定位就是"零成本试探",如果它也要创建文件和目录,就失去了轻量级的优势。

/opsx:propose 深度解析

propose 是 OpenSpec core profile 下的默认启动命令。如果说 explore 是"想清楚要不要做",那 propose 就是"决定做了,一步到位把规划文档全部生成出来"。

propose 的一站式行为

当你执行:

1
/opsx:propose 新增用户下单接口(POST /orders),下单时同步扣减库存

AI 会在一次操作中完成以下所有动作:

  1. 创建 change 目录openspec/changes/<change-id>/
  2. 生成 proposal.md:描述变更的 Why、What、Impact
  3. 生成 Delta Specsspecs/<capability>/spec.md,用 ADDED/MODIFIED/REMOVED 标记增量变化
  4. 生成 design.md:技术方案和架构决策
  5. 生成 tasks.md:可执行的实现任务清单

换句话说,propose = new + 一次性生成所有工件。这是它和 expanded profile 下 new + ff 组合的核心区别。

propose 与 new + ff 的对比

维度 /opsx:propose /opsx:new + /opsx:ff
所属 profile core(默认) expanded(需切换)
操作步骤 一步完成 两步:先 new 创建骨架,再 ff 填充工件
中间审查机会 无,一次性全部生成 有,new 之后可以先审查再 ff
适用场景 需求明确,想快速启动 需求复杂,想分步控制
change 命名 AI 自动生成 new 时可以手动指定

在实际使用中,大多数场景用 propose 就够了。只有当你需要在创建 change 骨架后、生成工件前做一些手动调整(比如先修改 config.yaml 的上下文信息),才需要切换到 expanded profile 使用 new + ff。

propose 生成工件的质量控制

propose 一次性生成的工件质量,高度依赖两个因素:

  1. 你的 prompt 质量:越具体的需求描述,生成的工件越精准。“加个下单接口"和"新增 POST /orders 接口,下单时同步调用库存服务扣减库存,库存不足返回 409”,生成结果差异巨大。

  2. config.yaml 的完整度:如果你在 config 中声明了技术栈、架构模式、命名规范,AI 生成的 design 和 tasks 会自动遵循这些约束。反之,它只能靠猜测。

这也是为什么前面"步骤一"强调要先补充 config.yaml——它不是可选的锦上添花,而是直接影响 propose 输出质量的关键输入。

propose 的幂等性问题

需要注意的是,propose 不是幂等的。每次执行都会创建一个新的 change 目录。如果你对第一次 propose 的结果不满意,不要再执行一次 propose(这会创建第二个 change),而应该直接修改已有 change 中的工件文件。

/opsx:new/opsx:continue 深度解析

newcontinue 是 OpenSpec expanded profile 下的一对搭档命令。如果说 propose 是"一键生成所有",那 new + continue 就是"先搭骨架,再逐件填充"。

new:只建目录,不生成工件

当你执行:

1
/opsx:new add-refund-api

AI 只做一件事:创建 change 的文件夹结构和元数据

1
2
3
openspec/changes/add-refund-api/
├── metadata.yaml # change 的元信息(创建时间、状态等)
└── specs/ # 空目录,等待后续填充

注意这里没有 proposal.mddesign.mdtasks.md。这些工件需要通过后续的 continueff 来生成。

这个设计的意图是:给你一个干净的起点,让你在生成工件之前有机会做准备工作。比如:

  • 先手动调整 config.yaml,补充这次变更特有的上下文
  • 先在 specs/ 目录下手动放入一些参考文档
  • 先和团队讨论确认方向,再让 AI 生成正式工件

continue:按依赖链逐步生成

continue 的行为是每次只生成依赖链中的下一个工件。OpenSpec 的工件依赖关系是:

1
2
3
4
5
6
7
proposal(根节点,无依赖)

specs(依赖 proposal)

design(依赖 proposal + specs)

tasks(依赖 specs + design)

所以当你连续执行 continue 时,生成顺序是:

1
2
3
4
/opsx:continue  →  生成 proposal.md
/opsx:continue → 生成 specs/
/opsx:continue → 生成 design.md
/opsx:continue → 生成 tasks.md

每一步之间,你都有机会审查和修改上一步的输出。这是 continue 相比 propose 的核心优势:你可以在每个工件生成后介入,确保下一个工件基于正确的输入。

continue 的审查-修正循环

一个典型的 continue 工作流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/opsx:new add-refund-api

/opsx:continue → 生成 proposal.md
↓ 审查:发现 proposal 遗漏了"部分退款"场景
↓ 手动修改 proposal.md,补充部分退款描述

/opsx:continue → 基于修正后的 proposal 生成 specs
↓ 审查:specs 看起来没问题

/opsx:continue → 生成 design.md
↓ 审查:design 中的数据库方案需要调整
↓ 手动修改 design.md

/opsx:continue → 基于修正后的 design 生成 tasks.md

这种"生成 → 审查 → 修正 → 继续"的循环,特别适合以下场景:

  • 需求复杂度高:一次性生成容易遗漏细节
  • 团队协作:每个工件需要不同角色审查
  • 学习阶段:想逐步理解 OpenSpec 的工件是怎么关联的

ff:批量生成的快捷方式

/opsx:ff(fast-forward)是 continue 的批量版本:一次性生成所有剩余工件

1
2
3
/opsx:new add-refund-api

/opsx:ff → 一次性生成 proposal + specs + design + tasks

ffpropose 的区别在于:ff 需要先有一个通过 new 创建的 change,而 propose 是从零开始。如果你已经通过 new 创建了 change 并做了一些手动准备,ff 可以帮你快速补齐剩余工件。

ff 也支持从中间状态继续。比如你已经通过 continue 生成了 proposal 和 specs,此时执行 ff 只会生成 design 和 tasks。

三种启动模式的选择指南

1
2
3
4
5
6
7
8
9
10
11
需求明确 + 想快速启动
→ /opsx:propose(一步到位)

需求明确 + 想分步控制
→ /opsx:new → /opsx:ff(两步完成)

需求复杂 + 需要逐步审查
→ /opsx:new → /opsx:continue × N(逐件生成)

还没想清楚要不要做
→ /opsx:explore(先探索再决定)

/opsx:apply 深度解析

apply 是 OpenSpec 工作流中从规划到实现的转折点。前面的所有命令(explore、propose、new、continue、ff)都在生成"说明书",而 apply 是第一个真正"动手写代码"的命令。

apply 的核心行为

当你执行:

1
/opsx:apply

AI 会做以下事情:

  1. 定位当前活跃的 change:找到 changes/ 目录下未归档的 change
  2. 读取 tasks.md:这是 apply 的核心输入,它按照任务清单逐项执行
  3. 逐项实现:按照 tasks 中的 checkbox 列表,依次编写代码
  4. 标记完成:每完成一个任务,在 tasks.md 中将对应的 [ ] 改为 [x]

这个过程的关键在于:apply 不是"理解需求后自由发挥",而是"严格按照已审查的任务清单执行"。这也是为什么前面反复强调 tasks.md 的质量——它直接决定了 apply 的输出质量。

apply 的输入优先级

apply 在实现时会参考多个工件,但优先级不同:

1
2
3
4
5
tasks.md        → 第一优先级:做什么、做到什么程度
specs/spec.md → 第二优先级:需求的精确定义和场景覆盖
design.md → 第三优先级:技术方案和架构约束
proposal.md → 第四优先级:变更的背景和动机
config.yaml → 始终生效:项目级的规则和约束

如果 tasks.mdspec.md 之间存在矛盾,apply 会优先遵循 tasks.md。这是一个重要的设计决策:tasks 是经过人类审查确认的执行计划,spec 是 AI 生成的需求描述,前者的可信度更高。

apply 的中断与恢复

apply 最实用的特性之一是支持中断恢复。如果 AI 在执行到第三个任务时因为上下文窗口耗尽或其他原因中断,你只需要再次执行:

1
/opsx:apply

AI 会检查 tasks.md 中哪些任务已经被标记为 [x],然后从第一个未完成的任务继续。这个机制依赖于 tasks.md 中的 checkbox 状态,所以不要手动修改已完成任务的标记,除非你确实想让 AI 重新实现某个任务。

apply 与直接让 AI 写代码的区别

你可能会问:为什么不直接告诉 AI “按照这个需求写代码”,而要走 apply 这条路?

区别在于上下文的结构化程度

维度 直接让 AI 写代码 /opsx:apply
上下文来源 你的 prompt + AI 的理解 tasks + specs + design + config
任务粒度 一整块需求 拆分好的 checkbox 列表
进度追踪 tasks.md 中的 checkbox 状态
中断恢复 需要重新描述上下文 自动从断点继续
一致性保证 依赖 AI 的记忆 依赖持久化的工件文件

简单来说,apply 把"AI 写代码"这件事从一次性的对话行为,变成了可追踪、可恢复、可审计的结构化过程

apply 的常见陷阱

  1. tasks 粒度过粗:如果一个 task 是"实现整个订单模块",apply 的输出质量会急剧下降。好的 task 应该是"创建 CreateOrderRequest DTO"这样的粒度。

  2. 跳过审查直接 apply:如果 proposal 或 spec 有问题,apply 会忠实地按照错误的规划去实现。垃圾进,垃圾出。

  3. 在 apply 过程中手动改代码:apply 依赖 tasks.md 的状态来判断进度。如果你在 apply 过程中手动修改了代码但没有更新 tasks.md,可能导致 AI 重复实现或跳过某些步骤。

/opsx:sync 深度解析

sync 是 OpenSpec 工作流中一个容易被忽视但非常实用的命令。它做的事情很简单:把 change 中的 Delta Specs 合并到主 specs/ 目录,但不归档 change。

sync 与 archive 的区别

这是理解 sync 的关键:

1
2
/opsx:sync     →  合并 Delta Specs 到主 specs/  +  change 保留在 changes/ 中
/opsx:archive → 合并 Delta Specs 到主 specs/ + change 移动到 changes/archive/

两者都会执行 spec 合并,但 sync 之后 change 仍然是"活跃"的,你可以继续修改它。而 archive 之后 change 就"关闭"了。

什么时候应该用 sync 而不是 archive

sync 的典型使用场景是长周期变更

1
2
3
4
5
6
7
8
9
sync 的适用场景:
✓ 变更还在进行中,但已完成的部分需要让其他 change 看到
✓ 多个 change 并行开发,需要及时同步 spec 变化以避免冲突
✓ 想先验证 spec 合并是否有冲突,再决定是否归档
✓ 变更分多个阶段交付,每个阶段完成后同步一次

sync 不适合的场景:
✗ 变更已经完全完成,应该直接 archive
✗ 只是想看看 spec 会怎么变化(sync 会实际修改文件)

sync 的合并机制

sync 不是简单的文件复制。它的合并逻辑基于 Delta Specs 中的语义标记:

1
2
3
ADDED 部分    → 追加到主 spec 中
MODIFIED 部分 → 替换主 spec 中对应的段落
REMOVED 部分 → 从主 spec 中删除对应内容

这意味着 sync 是一个有意图的合并操作,而不是盲目的文件覆盖。如果 Delta Spec 中标记了 MODIFIED: Requirement X,sync 会找到主 spec 中的 Requirement X 并替换它,而不是覆盖整个文件。

sync 后的状态

执行 sync 后,项目状态变成:

1
2
3
4
5
6
7
8
9
10
11
12
openspec/
├── specs/ # ← 已更新,包含了 change 的增量变化
│ └── order-creation/
│ └── spec.md # ← 已合并 Delta Specs
└── changes/
└── add-create-order/ # ← 仍然存在,未归档
├── proposal.md
├── design.md
├── tasks.md
└── specs/
└── order-creation/
└── spec.md # ← Delta Spec 仍然保留

change 中的 Delta Spec 文件不会被删除——它们会一直保留到你执行 archive 为止。这意味着你可以多次 sync(比如每完成一个阶段 sync 一次),每次 sync 都会把最新的 Delta Spec 状态合并到主 specs。

/opsx:archive 深度解析

archive 是 OpenSpec 工作流的终点命令——它标志着一次变更的正式完结。但"归档"这个词容易让人误解为"删除"或"隐藏",实际上 archive 做的事情比这丰富得多。

archive 的完整行为

当你执行:

1
/opsx:archive add-create-order

AI 会按顺序执行以下操作:

  1. 合并 Delta Specs:将 changes/add-create-order/specs/ 中的增量规范合并到主 openspec/specs/(和 sync 相同的逻辑)
  2. 移动 change 目录:将整个 changes/add-create-order/ 移动到 changes/archive/add-create-order/
  3. 保留完整历史:归档后的 change 包含所有原始工件(proposal、design、tasks、specs),作为"为什么这么改"的历史记录

archive 后的目录状态

1
2
3
4
5
6
7
8
9
10
11
12
openspec/
├── specs/ # ← 已更新为最新状态
│ └── order-creation/
│ └── spec.md # ← 包含了 change 的增量变化
└── changes/
├── archive/ # ← 归档区
│ └── add-create-order/ # ← 完整保留
│ ├── proposal.md
│ ├── design.md
│ ├── tasks.md
│ └── specs/
└── (其他活跃的 change)

archive 的不可逆性

需要特别注意的是,archive 是一个有副作用的操作

  • Delta Specs 已经合并到主 specs,这个合并不会自动回滚
  • change 目录已经移动到 archive,不再出现在活跃 change 列表中
  • 如果你想"撤销"一次 archive,需要手动把 change 从 archive 移回 changes,并手动回滚主 specs 的变化

这也是为什么建议在 archive 之前先执行 verify——确保实现和规范一致后再归档,避免把有问题的 spec 合并到主线。

bulk-archive:批量归档

当你有多个已完成的 change 需要归档时,可以使用:

1
/opsx:bulk-archive

bulk-archive 会自动发现所有可归档的 change,并按顺序逐个归档。它的关键行为是:

  • 按创建时间排序:先创建的 change 先归档,确保 spec 合并顺序正确
  • 冲突检测:如果两个 change 修改了同一个 spec 的同一部分,bulk-archive 会在冲突处停下来,要求你手动解决
  • 原子性:每个 change 的归档是独立的,一个 change 归档失败不会影响其他 change

archive 与 Git 的关系

一个常见的疑问是:archive 和 git commit 是什么关系?

答案是:它们是正交的。archive 操作的是 OpenSpec 的文件结构,git commit 操作的是版本控制。一个推荐的实践是:

1
2
3
4
5
/opsx:archive add-create-order

git add -A .
git commit -m "feat(order): archive add-create-order change"
git push

把 archive 的结果作为一个独立的 commit,这样在 git 历史中也能清晰地看到"这次 commit 归档了哪个 change"。

步骤六:在需要时单独同步主 Specs

1
/opsx:sync add-create-order

这一步会把 changes/add-create-order/specs/ 中的 Delta Specs 合并进主 openspec/specs/。它更适合在你希望不归档、只先处理 spec 合并时使用。重点不是整文件覆盖,而是按意图更新主规范。

步骤七:归档本次变更

1
/opsx:archive add-create-order

归档意味着这次变更结束了,但它不会抹去上下文。相反,整个 change 目录会进入 changes/archive/,成为以后回溯“为什么这么改”的历史依据;与此同时,future-state specs 也会在归档过程中并入主 specs/,成为新的 current truth。

到这里,一个完整的 change 才算真正闭环。若采用默认主流程,可以理解为:

1
proposal → review → apply → archive

如果团队需要更强的中间检查,也可以在归档前补充:

1
proposal → review → apply → verify → sync → archive

人机交互边界与修正工作流

要把 OpenSpec 用稳,关键不在于第一次 propose 是否成功,而在于发现问题后能否按正确顺序回退和修正。最常见的问题不是命令不会用,而是:

  • proposal / spec 有问题时该改哪里;
  • apply 之后发现结果不对,应该先改代码还是先改 spec;
  • 人到底能不能直接改这些 Markdown 工件。

原则一:人负责意图与判断,AI 负责结构化与执行

在 propose 阶段,更合理的协作边界是:

  • 人类提供目标、上下文、约束、边界和取舍;
  • AI把这些信息整理成 proposal、spec、design、tasks;
  • 人类再审查这些工件是否真的表达了自己的意图。

因此,proposal、spec、design、tasks 都不是神圣不可改的中间产物,而是可迭代的人机协作载体。

原则二:发现问题时,先分清是“实现问题”还是“规范问题”

apply 之后如果出现缺陷,根因通常只有两类:

1
2
3
4
5
发现缺陷
├── 实现偏离了 spec
│ └── spec 是对的,代码写错了
└── spec 本身就有缺陷
└── spec 漏了场景、描述不精确,或逻辑定义错误

这个区分非常重要,因为它直接决定修复顺序。

三种常见修正路径

1. 用 /opsx:explore 先澄清,再决定是否回写工件

当问题本质上是“需求没想清楚”或“方案还在摇摆”时,/opsx:explore 是很自然的入口。官方文档对它的强调重点是思考、调研、澄清和收敛问题,因此更稳妥的理解是:它通常先服务于分析与澄清,而不是把它当作默认实现入口。

典型场景包括:

  • 需求范围需要收缩或扩展;
  • 某个边界场景在 proposal / spec 中表达得不够准确;
  • 技术方案需要先比较几种实现路径;
  • apply 前审查发现工件之间存在不一致。

如果 explore 的结论稳定了,再让 AI 把结论落实到 proposal、spec、design、tasks 中。

2. 用自然语言直接更新特定工件

如果你已经很明确知道问题在哪里,也可以直接告诉 AI 修改具体文件,例如:

1
2
3
请更新 openspec/changes/add-create-order/specs/order-creation/spec.md,
新增“库存服务超时时返回 503,订单不创建”的场景,
并同步检查 design.md 与 tasks.md 是否需要更新。

这种方式的优点是精确,缺点是你需要主动关注跨文件一致性。

3. 人工直接编辑 Markdown

这也是允许的。OpenSpec 的工件本质上就是普通 Markdown 文件,你完全可以手工修改它们。更稳妥的做法是:

  1. 先明确你改动了哪些工件;
  2. 再让 AI 检查 proposal / spec / design / tasks 是否仍然一致;
  3. 在实现后用 verify 再做一次对账。

Apply 后的正确修复顺序

如果问题来自实现偏离 spec,那就直接修代码即可。

但如果问题来自spec 本身有缺陷,顺序必须反过来:

1
2
3
4
5
先修 spec
→ 必要时修 design
→ 更新 tasks
→ 再修代码
→ verify

例如,下单接口最初只定义了“库存充足”和“库存不足”两个场景。后来测试发现:库存服务超时时,系统仍然创建了订单。此时真正的问题,不一定是代码写错,而可能是 spec 根本没有定义“库存服务超时”这个行为。

这类情况下,先补 spec 才有意义:

1
2
3
4
#### Scenario: Inventory service timeout
- **WHEN** inventory service does not respond within 3 seconds
- **THEN** the system returns HTTP 503
- **AND** the order is NOT created

然后再去更新 design、tasks 和实现代码。原因很简单:spec 是 AI 后续工作的标准答案。如果你只改了代码,却不改 spec,那么下次 AI 再读规范时,仍然会基于错误前提继续工作。这就是典型的 spec drift。

一条更实用的修正循环

把上面的原则浓缩起来,更实用的修正循环其实很简单:

1
2
3
4
5
6
7
8
9
10
review / test 发现问题

先判断:实现错了,还是规范错了?

规范错了:先修 spec / design / tasks,再修代码
实现错了:直接修代码

verify

通过后再 sync / archive

OpenSpec 的价值,不只是“帮助 AI 开工”,更在于帮助你在问题暴露后,仍然用同一套规范体系把事情收回来。

什么时候看哪些文件,什么时候改哪些文件

这一节可以当作速查表使用。

生命周期速查表

阶段 重点文件 你要做什么
初始化 openspec/config.yaml 可选地补充项目上下文与规则
理解现状 openspec/specs/*/spec.md 了解系统当前已经承诺的行为
创建提案 changes/<id>/proposal.md 审查变更范围、动机与影响
审查需求 changes/<id>/specs/**/spec.md 审查新增或修改的行为场景
审查方案 changes/<id>/design.md 审查关键设计和实现权衡
审查执行计划 changes/<id>/tasks.md 确认任务拆分是否足够具体
实现 代码文件 + tasks.md 让 AI 按任务顺序落地并更新勾选状态
对账 verify 报告 判断实现是否完整、正确、一致
沉淀 specs/ + changes/archive/ sync 更新事实,archive 保留历史

决策速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
想了解系统现状?
→ 读 openspec/specs/

想开始一个新功能?
→ /opsx:propose
或 /opsx:new → /opsx:continue

想澄清需求或比较方案?
→ /opsx:explore

想让 AI 真正开始实现?
→ /opsx:apply

实现完了想检查是否对齐?
→ /opsx:verify

想把本次变更沉淀为长期事实?
→ /opsx:sync → /opsx:archive

注意事项与最佳实践

1. 不要跳过人工审查

OpenSpec 的核心收益来自“在编码前对齐”。如果 proposal 和 spec 没看就直接 apply,本质上仍然是在让 AI 自由发挥,只是多绕了一层目录结构。

2. 不要把 syncarchive 混为一谈

更稳妥的理解是:archive 本身就是默认收口动作;如果你希望在归档前先单独核查或单独合并 specs,再引入 verifysync。这样既能适配默认工作流,也能覆盖扩展工作流。

3. 不要混用不同工件的时态

  • specs/ 讲的是当前事实;
  • proposal.md 讲的是本次准备做什么;
  • tasks.md 讲的是实现步骤。

一旦把“未来工作项”写进主 spec,或者把“系统现状”写进 tasks,AI 和人都会很难读懂上下文。

4. config.yaml 很重要,但不是绝对前提

它常用于补充项目上下文、默认 schema 和工件规则,在复杂项目中尤其有价值;但 OpenSpec 的工作流不应被误解为“没有 config.yaml 就无法启动”。

5. 用渐进式方式接入 OpenSpec

没有必要一夜之间把整个项目重写成 Spec。更现实的方式,是从新功能、关键接口、经常变动的模块开始,让 specs/ 随着一次次 sync 慢慢沉淀起来。

6. 警惕 spec drift

只修代码、不修规范,短期看起来省事,长期会不断制造重复劳动。每当 AI 下一次再读取过时的 spec,旧问题就会重新出现。

7. 把 archive 当成历史资产,而不是回收站

归档目录里保存的是一次变更完整的上下文:为什么改、怎么设计、怎么拆任务、最后沉淀了哪些规范。它对回溯设计决策非常有价值。

验收标准与 ATDD:让 Spec 真正可测试

OpenSpec 的 spec 非常适合描述行为,但要把规范真正变成可执行验收标准,仍然需要进一步思考一个问题:

“可读的场景”如何变成“可验证的标准”?

Gherkin 场景擅长行为,不天然擅长量化指标

例如,下面这种写法表达了目标,但仍然不够可测:

1
2
3
#### Scenario: 整体响应时间不超过 5 秒
- **WHEN** 客户端请求 `/res/v1/health/custom`
- **THEN** API 响应时间不超过 5 秒

问题在于:

  1. 没说明是单次请求、平均值,还是 P95 / P99;
  2. 没说明并发与负载条件;
  3. AI 也很难直接据此生成精确断言。

更实用的做法:分层表达验收标准

比较稳妥的实践是把验收标准拆成三层:

  1. spec.md:描述功能行为和关键场景;
  2. spec.md 的 Acceptance Criteria 章节:补充结构化、量化的验收指标;
  3. design.md:说明这些指标应如何被测试框架验证。

例如:

1
2
3
4
5
6
7
## Acceptance Criteria

| ID | 指标 | 条件 | 阈值 | 测试方法 |
|----|------|------|------|----------|
| NFR-001 | 响应时延 P95 | 并发 ≤ 100 | ≤ 200ms | Gatling |
| NFR-002 | 响应时延 P99 | 并发 ≤ 100 | ≤ 500ms | Gatling |
| NFR-003 | 错误率 | 并发 ≤ 100 | < 0.1% | Gatling |

然后在 design.md 中说明测试策略,例如使用 Gatling 做压测、用 JUnit 覆盖功能场景。这样做的好处是:

  • 行为契约仍然留在 spec;
  • 量化目标有明确归属;
  • AI 在 apply 阶段更容易同时生成功能代码与测试代码。

ATDD 在 OpenSpec 里的落点

如果把验收测试驱动开发(ATDD)放进 OpenSpec 语境里,可以把它理解成:

1
2
3
4
5
6
7
先在 spec 中定义行为

再用 Acceptance Criteria 定义可测标准

再在 design / tasks 中说明如何验证

最后让 AI 按这些标准实现与测试

也就是说,OpenSpec 最适合作为 ATDD 的上游规范层。它不替代测试框架,但它能让测试目标在编码前就变得清晰。

OpenSpec Commands / Skills:如何理解这套生态

在支持 AI 命令和技能机制的工具里,OpenSpec 常常会以命令、skills 或两者结合的方式交付:

  • 显式命令:例如 /opsx:propose/opsx:apply/opsx:archive
  • 自动激活的 skills:当你用自然语言描述意图时,AI 工具可按其集成方式匹配对应能力。

更稳妥的理解是:它们常常服务于同一套工作流,但具体呈现方式取决于工具集成与交付配置:

  • 命令更适合显式控制、重复执行和标准化步骤;
  • skills 更适合自然语言驱动和低摩擦交互。

一个很实用的理解方式是:

  • 新手阶段优先用显式命令,因为边界更清楚;
  • 熟练阶段可以更多使用自然语言,让 AI 自动匹配;
  • 复杂场景常常是两者混用:先用自然语言澄清问题,再用显式命令推进关键步骤。

三个最容易讲错的点

/opsx:new 不是“一次生成所有工件”

它更像“建立 change 容器并开始第一步”,适合想自己掌控生成节奏的人。

/opsx:explore 不是“提前开始实现”

它的价值在于思考、调研、比较、澄清,以及在必要时把结论回写到工件,而不是直接产出业务代码。

/opsx:archive 不是“只做历史归档”的别名

archive 的意义是收口与保留历史,同时在默认流程里也会更新主 Specs;sync 更适合在需要时把 spec 合并动作单独拿出来执行。

项目配置搭配建议

把 OpenSpec 放进真实项目时,最容易困惑的一点是:AGENTS.mdCLAUDE.md、规则文件、openspec/ 到底是什么关系。

最简单的理解方式是把它们分成三层:

1. AI 工具层

例如 AGENTS.mdCLAUDE.md 或某些工具自己的 rules 文件。它们主要负责:

  • 项目级提示;
  • Agent 行为约束;
  • 编码规范、协作规则、调用策略。

它们回答的问题更像是:

AI 在这个仓库里应当如何工作?

2. OpenSpec 上下文层

主要是 openspec/config.yaml。它常用于:

  • 提供项目背景;
  • 提供规则与约束;
  • 提供默认 schema 或工件级规则,并改善 proposal 等工件草案的质量。

它回答的问题是:

这个项目是什么样的?

3. OpenSpec 规范层

主要是 openspec/specs/openspec/changes/。它们负责:

  • 描述当前事实;
  • 表达本次变更;
  • 沉淀历史与知识。

它回答的问题是:

系统现在是什么,以及这次准备把它改成什么?

推荐搭配方式

如果你只想用一句话记住:

  • AI 工具层负责“怎么协作”;
  • OpenSpec 上下文层负责“项目是什么”;
  • OpenSpec 规范层负责“系统行为与变更是什么”。

三者并不冲突,反而是互补关系。对复杂项目来说,这种分层往往比把所有信息都塞进同一个 Markdown 文件里更稳定。

参考资料