概念
本指南阐述了 OpenSpec 的核心理念及其相互关联。如需了解实际用法,请参阅快速入门和工作流程。
设计哲学
OpenSpec 围绕四大原则构建:
灵活而非僵化 — 不设阶段关卡,按需推进工作
迭代而非瀑布 — 边构建边学习,持续优化改进
简易而非复杂 — 轻量配置,最小化流程仪式
存量优先 — 兼容现有代码库,而非仅适用于全新项目为何这些原则至关重要
灵活而非僵化。 传统规范系统将您锁定在固定阶段:先规划,再实施,最后完成。OpenSpec 更具灵活性——您可以按照对工作最有利的顺序创建各类产出物。
迭代而非瀑布。 需求会变化,理解会深入。最初看似可行的方案,在接触实际代码库后可能不再适用。OpenSpec 拥抱这一现实。
简易而非复杂。 某些规范框架需要繁琐配置、严格格式或重量级流程。OpenSpec 不会干扰您的工作。数秒内即可初始化,立即开始工作,仅在必要时进行定制。
存量优先。 多数软件开发并非从零开始——而是改造现有系统。OpenSpec 基于增量的特性,使其能够轻松定义对现有行为的修改,而不仅仅是描述全新系统。
整体概览
OpenSpec 将您的工作组织为两个主要部分:
┌────────────────────────────────────────────────────────────────────┐
│ openspec/ │
│ │
│ ┌─────────────────────┐ ┌───────────────────────────────┐ │
│ │ specs/ │ │ changes/ │ │
│ │ │ │ │ │
│ │ 事实来源 │◄─────│ 提议的修改 │ │
│ │ 描述系统当前 │ 合并 │ 每个变更 = 一个文件夹 │ │
│ │ 如何运作 │ │ 包含工件和差异 │ │
│ │ │ │ │ │
│ └─────────────────────┘ └───────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘规格 是事实来源 —— 它们描述了您系统当前的行为。
变更 是提议的修改 —— 它们存放在单独的文件夹中,直到您准备好合并它们。
这种分离是关键。您可以并行处理多个变更而不会产生冲突。您可以在变更影响主要规格之前对其进行审查。当您归档一个变更时,其差异会干净地合并到事实来源中。
协调工作区
工作区支持正在积极开发中,尚未准备好使用。请勿基于工作区行为构建外部自动化、集成或长期运行的工作流;命令、状态文件和 JSON 输出可能随时更改。
下面的命令提供了跨链接仓库或文件夹进行规划的初始设置流程。
当一个仓库拥有规划、实现和归档流程时,仓库本地的 OpenSpec 项目是合适的默认选择。有些工作跨越多个仓库或文件夹。对于这种情况,OpenSpec 协调工作区是持久的规划中心。
工作区的心智模型是:
text
工作区 = 相关跨仓库变更的存放处
链接 = 工作区可以规划的仓库或文件夹的稳定名称
变更 = 一个功能、修复、项目或其他计划中的工作单元工作区与仓库本地项目的结构不同:
text
workspace-folder/
├── changes/ # 工作区级别的规划
└── .openspec-workspace/
├── workspace.yaml # 共享的工作区标识和链接名称
└── local.yaml # 此机器的本地路径仓库本地的 OpenSpec 状态保持现有结构:
text
repo-root/
└── openspec/
├── specs/
└── changes/这种区别很重要。工作区文件夹是跨链接仓库或文件夹进行规划的协调界面。每个仓库的 openspec/ 目录仍然是仓库自有规格、仓库本地变更和实现规划的中心。用户无需在工作区文件夹内运行仓库本地的 openspec init。
稳定的链接名称是工作区规划引用仓库和文件夹的方式。共享的工作区状态保存诸如 api、web 或 checkout 之类的名称;每台机器在 .openspec-workspace/local.yaml 中将这些名称映射到自己的本地路径。
yaml
# .openspec-workspace/workspace.yaml
version: 1
name: platform
links:
api: {}
web: {}yaml
# .openspec-workspace/local.yaml
version: 1
paths:
api: /repos/api
web: /repos/webOpenSpec 创建的工作区默认将 .openspec-workspace/local.yaml 排除在可移植协作状态之外。.openspec-workspace/workspace.yaml 保持可移植,因为它存储的是工作区名称和稳定的链接名称,而不是某个用户的绝对检出路径。
链接的路径可以是完整的仓库、大型单体仓库内的文件夹或其他现有文件夹。它们在参与工作区规划之前不需要仓库本地的 openspec/ 状态。后续的实现、验证或归档工作流可能需要更多的仓库准备,但规划可见性从链接开始。
text
多仓库:
api -> /repos/api
web -> /repos/web
大型单体仓库:
billing -> /repos/platform/services/billing
checkout -> /repos/platform/apps/checkout托管的工作区位于标准 OpenSpec 数据目录下:
text
getGlobalDataDir()/workspaces这意味着当设置了 XDG_DATA_HOME 时为 $XDG_DATA_HOME/openspec/workspaces,在 Unix 风格的回退路径下为 ~/.local/share/openspec/workspaces,在原生 Windows 回退路径下为 %LOCALAPPDATA%\openspec\workspaces。原生 Windows Shell、PowerShell 和 WSL2 各自为运行 OpenSpec 的运行时保留路径字符串。此基础不支持在 D:\repo、/mnt/d/repo 和 UNC WSL 路径之间进行转换。
OpenSpec 还在以下位置维护一个机器本地注册表:
text
getGlobalDataDir()/workspaces/registry.yaml注册表将工作区名称映射到工作区位置,以便后续的全局命令可以从任何地方列出或选择已知的工作区。它只是一个索引。每个工作区文件夹仍然是其自身 .openspec-workspace/workspace.yaml 和 .openspec-workspace/local.yaml 的权威来源,因此过时的注册表记录可以被报告和修复,而无需重新定义工作区本身。
工作区可见性不等于变更承诺。当 OpenSpec 需要知道哪些仓库或文件夹相关时,设置一个工作区;当您准备好规划一个功能、修复、项目或其他工作单元时,再创建一个变更。
常用命令:
bash
# 引导式设置
openspec workspace setup
# 自动化友好的设置
openspec workspace setup --no-interactive --name platform --link /repos/api --link web=/repos/web
openspec workspace setup --no-interactive --name platform --link /repos/api --opener codex
# 从本地注册表查看已知工作区
openspec workspace list
openspec workspace ls
# 为选定的工作区添加或修复链接
openspec workspace link /repos/api
openspec workspace link api-service /repos/api
openspec workspace relink api-service /new/path/to/api
# 检查此机器可以解析的内容
openspec workspace doctor
openspec workspace doctor --workspace platform
# 打开链接的工作集
openspec workspace open
openspec workspace open platform --agent github-copilot
openspec workspace open --editorworkspace setup 总是在标准工作区位置创建工作区,将其记录在本地注册表中,显示工作区位置,并且至少需要一个链接的仓库或文件夹。交互式设置会询问首选的打开器。非交互式设置仅在提供 --opener codex、--opener claude、--opener github-copilot 或 --opener editor 时存储一个打开器。
OpenSpec 还维护根工作区打开文件:AGENTS.md 中由 OpenSpec 管理的指导块、用于 VS Code 和 GitHub Copilot-in-VS-Code 打开的机器本地 <workspace-name>.code-workspace 文件,以及针对该维护的 .code-workspace 文件的特定忽略条目。用户编写的 *.code-workspace 文件仍然可跟踪,因为忽略规则仅针对维护的文件。
维护的 VS Code 工作区将协调根目录作为 .,并将有效的链接仓库或文件夹作为附加根目录。VS Code 将这些条目显示为多根工作区。
workspace open 使用存储的首选打开器打开链接的工作集,除非为该次会话传递了 --agent <tool> 或 --editor。同时传递两个打开器覆盖选项会报错。根工作区打开使链接的仓库和文件夹对探索和规划可见;实现工作在用户明确要求后开始。
workspace link 和 workspace relink 仅记录现有文件夹;它们不会创建、复制、移动、初始化或编辑链接的仓库或文件夹。链接或重新链接成功后,OpenSpec 会刷新管理的指导、VS Code 工作区文件和忽略规则。
需要一个工作区的工作区命令可以从任何地方使用 --workspace <name> 运行。如果您在工作区文件夹或子目录内运行它们,OpenSpec 将使用该当前工作区。如果有多个已知工作区可用,并且您没有传递 --workspace <name>,交互式命令会显示一个选择器;--json 和 --no-interactive 会以结构化状态错误失败,而不是提示。
直接工作区命令支持脚本的 JSON 输出。JSON 响应将主要数据保存在 workspace、workspaces 或 link 对象中,并在 status 数组中报告警告或错误。健康对象使用 status: []。
规格
规格使用结构化的需求和场景描述您系统的行为。
结构
openspec/specs/
├── auth/
│ └── spec.md # 认证行为
├── payments/
│ └── spec.md # 支付处理
├── notifications/
│ └── spec.md # 通知系统
└── ui/
└── spec.md # UI 行为和主题按领域组织规格 —— 对您的系统有意义的逻辑分组。常见模式:
- 按功能区域:
auth/、payments/、search/ - 按组件:
api/、frontend/、workers/ - 按限界上下文:
ordering/、fulfillment/、inventory/
规格格式
一个规格包含需求,每个需求都有场景:
markdown
# 认证规格用途
应用程序的身份验证与会话管理。
需求
需求:用户身份验证
系统 SHALL 在成功登录后颁发一个 JWT 令牌。
场景:有效凭据
- GIVEN 一个拥有有效凭据的用户
- WHEN 用户提交登录表单
- THEN 返回一个 JWT 令牌
- AND 用户被重定向到仪表板
场景:无效凭据
- GIVEN 无效凭据
- WHEN 用户提交登录表单
- THEN 显示一条错误消息
- AND 不颁发任何令牌
需求:会话过期
系统 MUST 在 30 分钟不活动后使会话过期。
场景:空闲超时
- GIVEN 一个已认证的会话
- WHEN 30 分钟过去且无任何活动
- THEN 该会话被失效
- AND 用户必须重新进行身份验证
**关键要素:**
| 要素 | 用途 |
|---------|---------|
| `## Purpose` | 本规范所属领域的高层描述 |
| `### Requirement:` | 系统必须具备的特定行为 |
| `#### Scenario:` | 需求在实际中的具体示例 |
| SHALL/MUST/SHOULD | RFC 2119 关键词,指示需求强度 |
### 为何以这种方式构建规范
**需求是“做什么”** —— 它们陈述系统应该做什么,而不指定实现方式。
**场景是“何时”** —— 它们提供可以验证的具体示例。好的场景:
- 是可测试的(你可以为它们编写自动化测试)
- 覆盖正常路径和边缘情况
- 使用 Given/When/Then 或类似的结构化格式
**RFC 2119 关键词**(SHALL, MUST, SHOULD, MAY)传达意图:
- **MUST/SHALL** —— 绝对要求
- **SHOULD** —— 推荐,但存在例外
- **MAY** —— 可选
### 规范是什么(以及不是什么)
规范是一份**行为契约**,而不是实现计划。
好的规范内容:
- 用户或下游系统依赖的可观察行为
- 输入、输出和错误条件
- 外部约束(安全、隐私、可靠性、兼容性)
- 可以测试或明确验证的场景
规范中应避免:
- 内部类/函数名
- 库或框架选择
- 逐步实现细节
- 详细的执行计划(这些属于 `design.md` 或 `tasks.md`)
快速检验:
- 如果实现可以在不改变外部可见行为的情况下更改,那么它很可能不属于规范。
### 保持轻量:渐进式严谨性
OpenSpec 旨在避免官僚主义。使用足以使变更可验证的最轻量级级别。
**轻量规范(默认):**
- 简短的、以行为优先的需求
- 清晰的范围和非目标
- 几个具体的验收检查
**完整规范(用于更高风险):**
- 跨团队或跨仓库变更
- API/契约变更、迁移、安全/隐私问题
- 歧义可能导致昂贵返工的变更
大多数变更应保持在轻量模式。
### 人机协作
在许多团队中,人类进行探索,智能体制品。预期的循环是:
1. 人类提供意图、上下文和约束。
2. 智能体将其转化为以行为优先的需求和场景。
3. 智能体将实现细节保留在 `design.md` 和 `tasks.md` 中,而不是 `spec.md`。
4. 验证在实现前确认结构和清晰度。
这使得规范对人类可读,对智能体保持一致。
## 变更
变更是对您系统提出的修改建议,以文件夹形式打包,包含理解与实施所需的一切内容。
### 变更结构openspec/changes/add-dark-mode/ ├── proposal.md # 原因与内容 ├── design.md # 如何实现(技术方案) ├── tasks.md # 实施检查清单 ├── .openspec.yaml # 变更元数据(可选) └── specs/ # 增量规格 └── ui/ └── spec.md # ui/spec.md 中正在变更的内容
每个变更都是自包含的。它包含:
- **工件** — 记录意图、设计和任务的文档
- **增量规格** — 关于新增、修改或删除内容的规范
- **元数据** — 此特定变更的可选配置
### 为何变更采用文件夹形式
将变更打包为文件夹有几个好处:
1. **内容集中。** 提案、设计、任务和规格都位于一处。无需在不同位置寻找。
2. **并行工作。** 多个变更可以同时存在而不会冲突。可以在处理 `fix-auth-bug` 的同时进行 `add-dark-mode` 的工作。
3. **历史清晰。** 归档时,变更会连同其完整上下文一起移至 `changes/archive/`。您不仅可以回顾发生了什么变更,还能理解其原因。
4. **便于审查。** 变更文件夹易于审查——打开它,阅读提案,检查设计,查看规格增量。
## 工件
工件是变更中指导工作的文档。
### 工件流程proposal ──────► specs ──────► design ──────► tasks ──────► implement │ │ │ │ 原因 内容 方法 步骤
- 范围 变更点 方案 操作
工件相互构建。每个工件为下一个提供上下文。
### 工件类型
#### 提案 (`proposal.md`)
提案在高层级上捕捉**意图**、**范围**和**方法**。
```markdown
# 提案:添加深色模式
## 意图
用户请求添加深色模式选项,以减少夜间使用时的视觉疲劳并匹配系统偏好。
## 范围
范围内:
- 设置中的主题切换
- 系统偏好检测
- 在 localStorage 中持久化偏好
范围外:
- 自定义颜色主题(未来工作)
- 每页主题覆盖
## 方法
使用 CSS 自定义属性进行主题设置,并通过 React Context 进行状态管理。首次加载时检测系统偏好,允许手动覆盖。何时更新提案:
- 范围变更(缩小或扩大)
- 意图澄清(对问题有了更好的理解)
- 方法发生根本性转变
规格(specs/ 中的增量规格)
增量规格描述相对于当前规格的变更内容。请参阅下方的增量规格。
设计 (design.md)
设计捕捉技术方案和架构决策。
markdown
# 设计:添加深色模式
## 技术方案
通过 React Context 管理主题状态,避免逐层传递 props。CSS 自定义属性支持运行时切换,无需切换类名。
## 架构决策
### 决策:使用 Context 而非 Redux
使用 React Context 管理主题状态,原因如下:
- 简单的二元状态(浅色/深色)
- 无复杂状态转换
- 避免添加 Redux 依赖
### 决策:使用 CSS 自定义属性
使用 CSS 变量而非 CSS-in-JS,原因如下:
- 与现有样式表兼容
- 无运行时开销
- 浏览器原生解决方案
## 数据流
```
ThemeProvider (context)
│
▼
ThemeToggle ◄──► localStorage
│
▼
CSS Variables (applied to :root)
```
## 文件变更
- `src/contexts/ThemeContext.tsx` (新建)
- `src/components/ThemeToggle.tsx` (新建)
- `src/styles/globals.css` (修改)何时更新设计:
- 实施过程中发现方案不可行
- 发现了更好的解决方案
- 依赖项或约束条件发生变化
任务 (tasks.md)
任务是实施检查清单 — 带有复选框的具体步骤。
markdown
# 任务
## 1. 主题基础设施
- [ ] 1.1 创建包含浅色/深色状态的 ThemeContext
- [ ] 1.2 为颜色添加 CSS 自定义属性
- [ ] 1.3 实现 localStorage 持久化
- [ ] 1.4 添加系统偏好检测
## 2. UI 组件
- [ ] 2.1 创建 ThemeToggle 组件
- [ ] 2.2 将切换按钮添加到设置页面
- [ ] 2.3 更新 Header 以包含快速切换
## 3. 样式
- [ ] 3.1 定义深色主题调色板
- [ ] 3.2 更新组件以使用 CSS 变量
- [ ] 3.3 测试对比度以确保可访问性任务最佳实践:
- 将相关任务分组在标题下
- 使用层级编号(1.1, 1.2 等)
- 保持任务足够小,可在一次会话中完成
- 完成任务后勾选复选框
增量规格
增量规格是使 OpenSpec 适用于棕地开发的关键概念。它们描述变更内容,而不是重述整个规格。
格式
markdown
# 认证增量规格
## 新增需求
### 需求:双因素认证
系统必须支持基于 TOTP 的双因素认证。
#### 场景:2FA 注册
- 假设一个未启用 2FA 的用户
- 当用户在设置中启用 2FA
- 则显示用于设置认证器应用的二维码
- 并且用户必须在激活前通过验证码验证
#### 场景:2FA 登录
- 假设一个已启用 2FA 的用户
- 当用户提交有效凭据
- 则显示 OTP 挑战
- 并且仅在输入有效 OTP 后完成登录
## 修改需求
### 需求:会话过期
系统必须在 15 分钟不活动后使会话过期。
(先前:30 分钟)
#### 场景:空闲超时
- 假设一个已认证的会话
- 当 15 分钟过去且无活动
- 则会话被失效
## 移除需求
### 需求:记住我
(已弃用,改用 2FA。用户应在每次会话中重新认证。)增量部分
| 部分 | 含义 | 归档时的操作 |
|---|---|---|
## 新增需求 | 新行为 | 追加到主规格 |
## 修改需求 | 变更行为 | 替换现有需求 |
## 移除需求 | 弃用行为 | 从主规格中删除 |
为何使用增量而非完整规格
清晰性。 增量明确显示了变更内容。阅读完整规格时,您需要在脑中与当前版本进行对比。
避免冲突。 两个变更可以修改同一个规格文件而不冲突,只要它们修改的是不同的需求。
审查效率。 审查者看到的是变更,而非未更改的上下文。专注于重要内容。
适合棕地开发。 大多数工作都是修改现有行为。增量使修改成为一等公民,而非事后考虑。
模式
模式定义了工作流中的工件类型及其依赖关系。
模式如何工作
yaml
# openspec/schemas/spec-driven/schema.yaml
name: spec-driven
artifacts:
- id: proposal
generates: proposal.md
requires: [] # 无依赖,可首先创建
- id: specs
generates: specs/**/*.md
requires: [proposal] # 需要先有提案才能创建
- id: design
generates: design.md
requires: [proposal] # 可与规范并行创建
- id: tasks
generates: tasks.md
requires: [specs, design] # 需要先有规范和设计工件构成一个依赖关系图:
提案
(根节点)
│
┌─────────────┴─────────────┐
│ │
▼ ▼
规范 设计
(依赖于: (依赖于:
提案) 提案)
│ │
└─────────────┬─────────────┘
│
▼
任务
(依赖于:
规范, 设计)依赖是促成因素,而非强制门控。 它们展示了可以创建什么,而不是你接下来必须创建什么。如果不需要,你可以跳过设计。你可以在设计之前或之后创建规范——两者都只依赖于提案。
内置模式
spec-driven (默认)
规范驱动开发的标准工作流:
提案 → 规范 → 设计 → 任务 → 实现最适合:大多数功能开发,你希望在实现前就规范达成一致。
自定义模式
为你的团队工作流创建自定义模式:
bash
# 从头创建
openspec schema init research-first
# 或者分叉一个现有模式
openspec schema fork spec-driven research-first自定义模式示例:
yaml
# openspec/schemas/research-first/schema.yaml
name: research-first
artifacts:
- id: research
generates: research.md
requires: [] # 首先进行研究
- id: proposal
generates: proposal.md
requires: [research] # 基于研究的提案
- id: tasks
generates: tasks.md
requires: [proposal] # 跳过规范/设计,直接进入任务有关创建和使用自定义模式的完整详情,请参阅自定义。
归档
归档通过将差异规范合并到主规范中并保留变更历史来完成一次变更。
归档时会发生什么
归档前:
openspec/
├── specs/
│ └── auth/
│ └── spec.md ◄────────────────┐
└── changes/ │
└── add-2fa/ │
├── proposal.md │
├── design.md │ 合并
├── tasks.md │
└── specs/ │
└── auth/ │
└── spec.md ─────────┘
归档后:
openspec/
├── specs/
│ └── auth/
│ └── spec.md # 现在包含了双因素认证要求
└── changes/
└── archive/
└── 2025-01-24-add-2fa/ # 为历史记录保留
├── proposal.md
├── design.md
├── tasks.md
└── specs/
└── auth/
└── spec.md归档流程
合并差异。 每个差异规范部分(新增/修改/移除)都会应用到相应的主规范上。
移至归档。 变更文件夹移至
changes/archive/,并添加日期前缀以便按时间顺序排列。保留上下文。 所有工件在归档中保持完整。你随时可以回顾以了解变更的原因。
为什么归档很重要
干净的状态。 活跃的变更 (changes/) 仅显示进行中的工作。已完成的工作会移出视线。
审计追踪。 归档保留了每次变更的完整上下文——不仅仅是变更内容,还有解释原因的提案、解释方法的设计以及展示已完成工作的任务。
规范演进。 随着变更被归档,规范有机地增长。每次归档都会合并其差异,随着时间的推移构建出全面的规范。
各部分如何协同工作
┌──────────────────────────────────────────────────────────────────────────────┐
│ OPENSPEC 流程 │
│ │
│ ┌────────────────┐ │
│ │ 1. 开始 │ /opsx:propose (核心) 或 /opsx:new (扩展) │
│ │ 变更 │ │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 2. 创建 │ /opsx:ff 或 /opsx:continue (扩展工作流) │
│ │ 工件 │ 创建 提案 → 规范 → 设计 → 任务 │
│ │ │ (基于模式依赖关系) │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 3. 实现 │ /opsx:apply │
│ │ 任务 │ 处理任务,逐项完成 │
│ │ │◄──── 随着了解深入更新工件 │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 4. 验证 │ /opsx:verify (可选) │
│ │ 工作 │ 检查实现是否符合规范 │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ ┌──────────────────────────────────────────────┐ │
│ │ 5. 归档 │────►│ 差异规范合并到主规范中 │ │
│ │ 变更 │ │ 变更文件夹移至 archive/ │ │
│ └────────────────┘ │ 规范现在成为更新后的事实来源 │ │
│ └──────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘良性循环:
- 规范描述当前行为
- 变更提出修改建议(作为差异)
- 实现使变更成为现实
- 归档将差异合并到规范中
- 规范现在描述新的行为
- 下一次变更基于更新后的规范进行
术语表
| 术语 | 定义 |
|---|---|
| 工件 | 变更中的文档(提案、设计、任务或差异规范) |
| 归档 | 完成变更并将其差异合并到主规范的过程 |
| 变更 | 对系统的拟议修改,打包为一个包含工件的文件夹 |
| 差异规范 | 描述相对于当前规范的变更(新增/修改/移除)的规范 |
| 领域 | 规范的逻辑分组(例如 auth/、payments/) |
| 需求 | 系统必须具备的特定行为 |
| 场景 | 需求的具体示例,通常采用 Given/When/Then 格式 |
| 模式 | 工件类型及其依赖关系的定义 |
| 规范 | 描述系统行为的规范,包含需求和场景 |
| 事实来源 | openspec/specs/ 目录,包含当前已达成一致的行为 |