直接回答
**OpenSpec 是一套把 AI 辅助编码变更放进仓库、用纯文本工件规划和追踪的工作流。**正确用法是:安装 CLI,在仓库初始化,通过当前 AI 工具生成的工作流创建变更,依次审查 proposal.md、delta spec、按需生成的 design.md 和 tasks.md,再实现、验证并归档。
最重要的边界是:OpenSpec 能让意图、范围和变更历史更容易审查,但不会让生成代码自动正确。openspec validate 能发现结构不合规的工件,可选 verify 工作流能发现规格与实现之间的疑似偏差;真正的正确性仍需由编译、测试、安全检查、人工审查和发布控制证明。
目录
- 先理解模型:当前规格与活动变更
- 安装并初始化 OpenSpec
- 终端命令与 AI 工作流不是一回事
- 如何选择 Core 与 Expanded 工作流
- 实战:为 Go API 增加请求体大小限制
- 写代码前如何审查工件
- 实现变更并建立证据链
- Update、Sync、Verify 与 Archive 的边界
- 团队中如何落地 OpenSpec
- 常见失败模式与排查
- 什么场景值得使用 OpenSpec
- 常见问题
- 官方资料
先理解模型:当前规格与活动变更
OpenSpec 把团队当前接受的系统行为与待实施的修改分开保存:
openspec/
├── config.yaml
├── specs/
│ └── <domain>/spec.md
└── changes/
├── <active-change>/
│ ├── proposal.md
│ ├── specs/<domain>/spec.md
│ ├── design.md
│ └── tasks.md
└── archive/
openspec/specs/描述维护中的系统行为。它是经过审查的文档基线,不是自动执行约束的引擎。openspec/changes/<name>/保存一个活动变更的意图和增量。proposal.md说明为什么要改、改什么、明确不改什么。specs/保存 delta spec(增量规格),标识新增、修改或删除的需求。design.md记录有实质影响的技术取舍。小变更或非技术变更可以按当前 Schema 规则不生成它。tasks.md排列实施与验证任务。勾选框只是一项状态声明,不是完成证据。changes/archive/保存已经关闭的变更记录,其增量已经与主规格完成协调。
这些工件构成依赖关系,而不是四份互不相关的文档:
实施中发现遗漏约束时,应修订上游工件和对应任务。只改代码、不改约束,会制造规格漂移。
如果希望先理解与具体工具无关的需求工程、可追踪性和证据模型,可阅读 Spec Coding 核心概念解析。本文专门解决当前 OpenSpec 的安装、命令和执行问题。
安装并初始化 OpenSpec
通过 npm 安装时,OpenSpec 当前要求 Node.js 20.19.0 或更高版本。修改仓库前先检查运行时与 CLI:
node --version
npm install -g @fission-ai/openspec@latest
openspec --version
然后在仓库根目录初始化。老项目建议先创建分支,以便完整审查生成文件:
cd existing-go-service
git switch -c feature/request-body-limit
openspec init
git status --short
openspec init 会询问正在使用的 AI 工具,并写入两类文件:
openspec/项目目录;- 目标工具对应的 Skill 和/或命令,例如
.agents/、.claude/、.cursor/或.trae/。
提交前应审查所有生成文件。初始化程序找到代码仓库,不代表它已经理解系统架构。可在 openspec/config.yaml 中维护长期有效的项目上下文和约束,再对照真实代码验证生成工作流。
重复运行 openspec init 可以新增或刷新工具集成。升级 CLI、切换 Profile 或增加 AI 工具后,应刷新项目生成文件:
openspec update
生成的 Skill 和命令不会自动更新。如果新命令没有出现在 AI IDE 中,先检查生成路径,再重启工具。
终端命令与 AI 工作流不是一回事
这是初次使用最容易混淆的地方:
| 操作界面 | 示例 | 作用 |
|---|---|---|
| 终端 | openspec init |
创建或刷新项目集成 |
| 终端 | openspec list |
列出活动变更 |
| 终端 | openspec show add-request-body-limit |
查看指定变更 |
| 终端 | openspec validate add-request-body-limit |
检查工件结构和 Schema 规则 |
| 终端 | openspec view |
打开交互式项目视图 |
| AI 对话框 | propose 工作流 | 让助手创建变更工件 |
| AI 对话框 | apply 工作流 | 让助手执行任务 |
| AI 对话框 | archive 工作流 | 让助手协调规格并关闭变更 |
不要在终端里输入 /opsx:propose,也不要把 openspec validate 发给 AI 后就认为执行了确定性的 CLI 校验。
不同工具执行的是同一套工作流,但调用语法不同:
| 宿主 | Propose | Apply | Archive |
|---|---|---|---|
| Claude Code | /opsx:propose |
/opsx:apply |
/opsx:archive |
| Cursor 或 Trae | /opsx-propose |
/opsx-apply |
/opsx-archive |
| Codex | $openspec-propose |
$openspec-apply-change |
$openspec-archive-change |
以上是当前生成名称。对本地项目而言,openspec init 的完成提示和实际生成文件才是权威答案。OpenSpec 可以交付 Skill、命令或两者;部分宿主只支持 Skill。
如何选择 Core 与 Expanded 工作流
默认 core Profile 包含六个工作流:
| 工作流 | 适用场景 |
|---|---|
explore |
问题或方案仍有歧义,暂时不需要创建变更工件 |
propose |
一次生成 proposal、spec、按需生成的 design 和 tasks |
apply |
工件已审查,可以开始实施 |
update |
实施中的新发现要求修订已有计划或需求 |
sync |
需要把增量合入主规格,但仍保留活动变更 |
archive |
实现已经验收,可以关闭变更 |
Expanded 集合还提供 new、continue、ff、verify、bulk-archive 和 onboard。需要逐份审批工件时,使用 new 加 continue;希望一次生成全部规划工件时可用 ff;归档前需要规格与实现对照检查时,可启用 verify。
先修改全局工作流选择,再更新当前项目:
openspec config profile
openspec update
命令更多不等于流程更成熟。如果团队无法稳定审查四份核心工件,再增加六个入口只会提高认知负担。
实战:为 Go API 增加请求体大小限制
假设一个成熟 Go 服务通过 POST /v1/import 接收 JSON 导入数据,大请求可能在校验前耗尽内存。目标变更如下:
- 请求体超过 1 MiB 时返回 HTTP
413; - 正好等于上限的请求必须通过;
- 没有
Content-Length的请求也必须受限; - 不改变其他路由和鉴权行为;
- 增加测试和拒绝计数指标;
- 不修改公开 JSON Schema。
这个例子同时涉及安全、HTTP 语义、内存边界和既有中间件顺序,比“增加一个按钮”更能体现规格驱动流程的价值。
步骤 1:先调查当前行为
如果还不知道限制应插在哪一层,先运行 explore 工作流,并明确要求只调查、不修改:
调查 POST /v1/import 及其中间件链。确认请求体第一次被读取的位置、
现有限制、错误响应格式、监控指标、测试以及反向代理限制。不要修改文件,
结论必须附带文件路径、配置键或测试名称。
审查结果至少要回答:
- 代理层是否已经拒绝大请求,应用层是否仍需独立约束?
- 请求体是流式读取、整体缓冲、解压后读取,还是被重复解析?
- 限制应放在鉴权之前还是之后?
- API 是否使用统一错误信封,而不是
http.Error? - 缺失或伪造
Content-Length时会发生什么? - 哪些测试能证明其他路由没有变化?
不要把“助手已经看过”当作证据。缺少文件路径、配置键和测试名称的结论,应回到仓库核验。
步骤 2:提出一个边界清晰的变更
使用当前宿主生成的 propose 语法。例如:
/opsx:propose add-request-body-limit
把已确认事实、目标行为、非目标和验证门禁一起提供给助手:
为 POST /v1/import 创建 add-request-body-limit 变更。
应用层上限为 1 MiB,超过后使用现有 API 错误信封返回 HTTP 413;
必须覆盖无 Content-Length 请求,拒绝时增加现有指标一次;
保持中间件顺序和其他路由不变。加入 Go 单元测试、仓库 Lint 门禁
以及正好等于上限的回归测试。不要修改鉴权、代理配置或 JSON Schema。
一份合格的计划应足够具体,让评审者在任何代码生成之前就能否决错误方案。
步骤 3:编写可验证的 Delta Spec
新增行为应放入 ## ADDED Requirements:
# HTTP Import 变更增量
## ADDED Requirements
### Requirement: 导入请求体上限
服务 MUST 在 JSON 解码前拒绝大于 1 MiB 的 `POST /v1/import`
请求体,并且 MUST 使用现有 API 错误信封返回 HTTP `413`。
#### Scenario: 已声明长度的请求超过上限
- **GIVEN** 一个 `Content-Length` 大于 1 MiB 的已鉴权导入请求
- **WHEN** 请求到达导入请求体限制中间件
- **THEN** 服务返回 HTTP `413`
- **AND** 导入 Handler 不得执行
- **AND** 拒绝计数器只增加一次
#### Scenario: 未知长度的请求超过上限
- **GIVEN** 一个没有 `Content-Length` 的分块导入请求
- **WHEN** 读取量超过 1 MiB
- **THEN** 服务返回 HTTP `413`
- **AND** 不得分配无界缓冲区
#### Scenario: 请求体正好达到上限
- **GIVEN** 一个请求体正好为 1 MiB 的已鉴权导入请求
- **WHEN** 服务读取请求体
- **THEN** 导入 Handler 收到完整请求体
只有替换既有需求时才使用 ## MODIFIED Requirements,而且必须放入修改后的完整需求及全部场景,不能只写一句补丁说明。归档时,新版本会替换原需求。只有维护中的行为确实要消失时才使用 ## REMOVED Requirements,并在 proposal 中交代迁移和兼容影响。
Delta spec 是行为契约。包名、辅助函数签名和具体缓冲策略通常属于 design.md;只有成为外部可观察约束时,才应写进需求。
步骤 4:显式记录设计取舍
这次变更的设计审查至少应比较:
| 决策 | 方案 | 影响 |
|---|---|---|
| 限制层 | 只在反向代理限制 | 拒绝成本低,但各部署环境可能不一致,本地测试无法覆盖 |
| 限制层 | 只在应用限制 | 行为契约可移植,但请求会先消耗应用资源 |
| 请求体处理 | 最多读取 limit + 1 字节 |
适合小型 JSON,内存有界但仍会整体缓冲 |
| 请求体处理 | 使用 http.MaxBytesReader 流式限制 |
复制更少,但下游解码错误必须可靠映射为 413 |
| 中间件顺序 | 鉴权前限制 | 能先保护鉴权服务,但可能改变错误优先级 |
| 中间件顺序 | 鉴权后限制 | 保留鉴权语义,但被拒请求仍会消耗鉴权资源 |
正确答案取决于现有架构。应记录最终选择、被否决方案、内存上限、状态码映射、指标和回滚方式,不能把这些决定隐藏在生成代码里。
步骤 5:把需求映射成任务
有用的 tasks.md 应同时覆盖实现与证据:
## 1. 请求限制
- [ ] 1.1 为 POST /v1/import 增加有界读取
- [ ] 1.2 保留当前 API 错误信封与中间件顺序
- [ ] 1.3 拒绝时将现有指标准确增加一次
## 2. 验证
- [ ] 2.1 测试已声明长度且超过上限的请求
- [ ] 2.2 测试未知长度且超过上限的请求
- [ ] 2.3 测试正好为 1 MiB 的请求
- [ ] 2.4 测试其他路由不受影响
- [ ] 2.5 运行 Go 测试、Lint 和仓库安全门禁
任务不应凭空引入 proposal 与需求之外的工作;每个高风险场景也必须有对应的实现或验证任务。
写代码前如何审查工件
按依赖顺序阅读生成工件,一旦上游假设错误就先停止:
| 工件 | 核心问题 | 应退回修改的情况 |
|---|---|---|
proposal.md |
问题、范围、非目标和影响领域是否正确? | 擅自加入无关重构,或遗漏兼容性影响 |
| delta spec | 每项目标行为是否可观察、可测试? | “大”“快”“安全”等词没有阈值 |
design.md |
风险、备选方案、边界和回滚是否明确? | 未评估中间件顺序或内存行为就直接选型 |
tasks.md |
每项任务是否能追溯到已批准行为? | 任务没有需求来源,或需求没有证据任务 |
高风险变更可以在 proposal 或 PR 中维护一张短小的追踪表:
| 需求 | 实现位置 | 证据 |
|---|---|---|
| 拒绝已声明超限请求 | import 中间件 | Content-Length > limit 单元测试 |
| 拒绝未知长度请求 | 有界读取器 | 分块请求单元测试 |
| 接受正好达到上限的请求 | 边界判断 | 精确边界单元测试 |
| 其他路由不变 | 路由级注册 | Router 回归测试 |
| 指标只增加一次 | 拒绝分支 | 指标断言 |
OpenSpec 不提供审批权、所有权或访问控制。真正的强制机制应由 Code Owner、受保护分支、CI 和发布策略承担。
实现变更并建立证据链
工件审查通过后再运行 apply。助手可以在执行时更新任务勾选状态,但每完成一组有实质影响的修改,都应审查代码差异。
下面的 Go 示例展示小型 JSON 接口的有界读取逻辑。生产服务应复用已有错误信封和指标,不应直接复制示例文案:
package bodylimit
import (
"bytes"
"io"
"net/http"
)
func Middleware(maxBytes int64, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if maxBytes < 1 {
http.Error(w, "invalid body limit", http.StatusInternalServerError)
return
}
if r.ContentLength > maxBytes {
http.Error(w, "request body too large", http.StatusRequestEntityTooLarge)
return
}
body, err := io.ReadAll(io.LimitReader(r.Body, maxBytes+1))
if err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
if int64(len(body)) > maxBytes {
http.Error(w, "request body too large", http.StatusRequestEntityTooLarge)
return
}
r.Body = io.NopCloser(bytes.NewReader(body))
next.ServeHTTP(w, r)
})
}
配套测试同时覆盖两个拒绝路径,并证明被接受的请求体能原样交给下游:
package bodylimit
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestMiddleware(t *testing.T) {
const limit = int64(8)
tests := []struct {
name string
body string
contentLength int64
wantStatus int
wantBody string
}{
{
name: "declared body exceeds limit",
body: "123456789",
contentLength: 9,
wantStatus: http.StatusRequestEntityTooLarge,
},
{
name: "unknown length exceeds limit",
body: "123456789",
contentLength: -1,
wantStatus: http.StatusRequestEntityTooLarge,
},
{
name: "body at exact limit",
body: "12345678",
contentLength: 8,
wantStatus: http.StatusNoContent,
wantBody: "12345678",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("read accepted body: %v", err)
}
w.Header().Set("X-Accepted-Body", string(body))
w.WriteHeader(http.StatusNoContent)
})
request := httptest.NewRequest(
http.MethodPost,
"/v1/import",
strings.NewReader(tc.body),
)
request.ContentLength = tc.contentLength
response := httptest.NewRecorder()
Middleware(limit, next).ServeHTTP(response, request)
if response.Code != tc.wantStatus {
t.Fatalf("status = %d, want %d", response.Code, tc.wantStatus)
}
if got := response.Header().Get("X-Accepted-Body"); got != tc.wantBody {
t.Fatalf("accepted body = %q, want %q", got, tc.wantBody)
}
})
}
}
独立于助手运行验证:
openspec validate add-request-body-limit
go test ./...
go vet ./...
再根据风险执行仓库自己的 Lint、Race、安全、契约和集成检查。真实项目还应补充错误信封、指标、路由范围、压缩请求、代理行为以及并发负载下内存上限的测试。
可以按五层证据链检查:
- 工件有效性:
openspec validate证明变更符合当前 Schema。 - 构建有效性:编译和静态检查发现类型、API 与常见正确性错误。
- 行为证据:每个场景都有覆盖边界和失败分支的测试。
- 差异审查:人工确认范围、设计、生成文件、依赖和运维影响。
- 运行证据:必要时用指标、负载测试、灰度检查和回滚演练证明生产假设。
任务勾选框或模型声称“测试通过”都不是充分证据。必须能看到实际命令结果,并确认测试确实覆盖对应需求。
Update、Sync、Verify 与 Archive 的边界
这四个工作流解决不同的生命周期问题。
Update
实施中的新发现改变活动工件时使用 update。例如,调查发现代理层已经限制为 512 KiB,此时应先决定应用契约是与其一致还是有意不同,再修订 proposal、spec、design 和 tasks,之后重新执行工件校验及受影响测试。
Sync
Sync 把当前变更的 delta spec 合并进 openspec/specs/,但不关闭活动变更。它适用于一个变更横跨多个 PR,而后续开发已经需要引用新行为基线的情况。执行前必须检查冲突,尤其是两个活动变更同时修改同一需求时。
Verify
可选 verify 工作流会从完整性、正确性和一致性三个维度比较工件与实现,可发现未完成任务、缺失场景测试和设计漂移。但它仍是 AI 辅助审查:
- 除非明确要求且具备权限,否则它不会自动执行所有相关测试;
- 它无法证明安全或性能属性;
- 它不能独立审批自己刚生成的实现;
- 它不会自动阻止 archive。
应把输出当成待核验的问题清单,而不是发布证书。
Archive
Archive 用于关闭变更。OpenSpec 会按增量语义处理:
ADDED需求加入维护中的主规格;MODIFIED需求以完整新版本替换旧版本;REMOVED需求从主规格删除;- 变更目录移动到
openspec/changes/archive/。
归档前确认:
- 已批准需求与实际交付行为一致;
- 最终代码差异运行过全部必需检查;
- 已知偏差已经解决或显式接受;
- 重叠活动变更已经协调;
- 发布和回滚说明存放在团队规定的位置;
- 合并后的主规格仍准确描述当前行为。
归档是审计记录,不会自动成为未来每个模型都能调用的“长期记忆”。能否发挥作用,取决于检索方式、当前规格、仓库上下文以及评审者是否真正使用它。
团队中如何落地 OpenSpec
渐进式接入
第一次接入前,不要逆向整理整个遗留系统。先选择一个范围受控、行为可观察、风险可验证的变更,让已经验收的增量逐步构建主规格。
把工件放进版本控制
在正常 PR 中审查 openspec/、生成工作流文件和代码。可采用以下顺序:
- 起草并审查工件;
- 按批准版本实施;
- 附上场景到测试的证据;
- 一起审查最终代码与工件差异;
- 验收后在同一 PR 或受控后续 PR 中归档。
如果规划和实现拆成两个 PR,应记录实施所依据的准确工件 Commit。
区分全局规则与单次变更
AGENTS.md、CLAUDE.md 或工具规则用于描述长期编码约定;OpenSpec 工件用于描述一次行为变更。两者都不是安全边界。关键约束必须通过代码、测试、策略、权限和 CI 强制执行。
衡量结果,而不是工件数量
选择相似变更,比较:
- 编码前评审发现的问题数;
- 开始编码后发生的需求变更;
- 交付周期与评审时间;
- 线上逃逸缺陷和回滚率;
- 计划外修改文件数;
- 过期或冲突规格数;
- 维护工件消耗的时间。
Markdown 更多不代表工程质量更高。只有可追踪性收益高于评审和维护成本时,才值得保留这套流程。
常见失败模式与排查
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
找不到 /opsx:propose |
当前宿主使用另一种语法,或未生成命令 | 查看 init 提示和宿主目录,运行 openspec update,再重启工具 |
| 只能看到六个工作流 | 正在使用默认 core Profile | 运行 openspec config profile 选择所需工作流,再更新项目 |
没有 design.md |
当前 Schema 判定无需设计,或工件图尚未完成 | 检查状态与 Schema,不要为了形式强行创建 |
openspec validate 通过但代码报错 |
校验只覆盖工件结构 | 运行编译、测试、静态分析和场景到代码审查 |
| Apply 修改了无关文件 | 范围或任务过弱,或 Agent 执行漂移 | 停止执行,审查差异,收紧非目标与任务,只撤销非预期修改后继续 |
| 代码与规格不一致 | 需求只在对话中改变,或实施自行偏离 | 使用 update,重新审查工件并运行受影响证据 |
| 归档后主规格异常 | MODIFIED 增量不完整,或活动变更冲突 |
补全完整需求,对比主规格与增量,解决后再归档 |
| 生成工作流看起来过时 | CLI 或 Profile 已变,但项目文件未刷新 | 运行 openspec update 并审查生成差异 |
不要直接复制旧教程中的命令。旧命令名和目录结构会长期留在搜索结果中,应以本机当前版本生成的集成为准。
什么场景值得使用 OpenSpec
| 变更类型 | 建议流程 |
|---|---|
| 错别字或明显的一行修复 | 使用普通 Issue 与代码审查,完整变更工件可能只是噪声 |
| 验收行为清晰的局部功能 | Propose、审查、Apply、测试、Archive |
| 充满歧义的老项目改动 | 先 Explore 获取代码证据,再 Propose |
| 鉴权、支付、隐私、迁移或不可逆数据变更 | 逐步生成工件,显式设计与威胁/故障审查,执行独立门禁 |
| 横跨多个 PR 的功能 | Propose,随证据 Update,必要时 Sync,最终验收后 Archive |
| 紧急事故修复 | 先按事故机制恢复服务,再补齐工件与证据,不能让形式阻碍止损 |
| 纯文档或纯工具修改 | 行为增量没有意义时,选择更轻的 Schema |
OpenSpec 最适合“理解错误代价高,而可审查意图能降低风险”的变更。当工件维护成本高于它消除的不确定性时,就不应强行套用。
常见问题
OpenSpec 是什么,应该如何使用?
OpenSpec 是一套面向 AI 编程助手、以文本工件管理变更的开源规格驱动开发工作流。安装 CLI,执行 openspec init,在 AI 对话中调用生成的 explore 或 propose 工作流,审查工件,再执行任务、收集独立证据,并且只在行为与维护中的规格一致后归档。
为什么同一条 OpenSpec 命令在不同 AI 工具中写法不同?
各宿主加载 Skill 和命令的机制不同。Claude Code 可使用 /opsx:propose,Cursor 与 Trae 使用 /opsx-propose,Codex 使用 $openspec-propose。本地实际生成文件和初始化完成提示才是权威来源。修改 Profile 或工具后,应运行 openspec update 并重启宿主。
OpenSpec 可以渐进式接入老项目吗?
可以。先在分支中初始化,从一个边界明确的变更开始,调查并记录受影响领域的现有行为。不要先为整个遗留系统制造一套未经验证的规格;应让后续已经验收的变更逐步扩展主规格。
OpenSpec 能验证 AI 生成的代码正确吗?
不能。CLI validate 检查工件,可选 verify 工作流进行 AI 辅助的规格与实现对照。二者都不能替代构建、测试、安全与性能检查、人工审查和生产控制。失败后果越严重,证据就必须越独立、越接近真实运行环境。
OpenSpec 的 sync 与 archive 有什么区别?
Sync 把 delta spec 合并进主 openspec/specs/,但让变更继续保持活动状态;Archive 协调增量、关闭变更并把目录移入归档。执行两者前,都应检查并发变更冲突,并确认主规格仍描述已验收行为。
总结
可靠的 OpenSpec 流程不只是 Propose、Apply、Archive 三条命令,而是:
调查当前行为
-> Explore 消除歧义
-> Propose 创建工件
-> 审查范围与场景
-> Apply 执行受控任务
-> 运行独立证据
-> 协调实施中的新发现
-> 必要时 Sync
-> Archive 已验收行为
使用当前 AI 宿主实际生成的命令语法,区分主规格与变更增量,并把每份工件都当成待验证的声明。只有当这些声明能清晰映射到代码、测试、运行证据和受控发布时,规格驱动工作流才真正产生价值。
官方资料
- OpenSpec 安装说明
- OpenSpec 项目初始化
- OpenSpec 快速入门
- 支持工具与调用语法
- 工作流 Profile
- 工作流 Skill 契约
- 计划审查与修订
- Spec-driven Schema 参考
- OpenSpec 源代码仓库