💡阅读指南

11.4 讲了怎么生成初版 SPEC,再用二次澄清补齐关键歧义。你审批通过后,SPEC 才会冻结。 但冻结不等于永远不变。系统会迭代、需求会演变、团队会换人——SPEC 本身需要一套管理机制。 本节讲 OpenSpec:怎么用"主 SPEC + 增量变更"的模式,让 SPEC 像代码一样可追溯、可回滚、可验证。

6.1 SPEC 不是写一次就完的

你花了一下午,把胶囊系统的 SPEC 写完了。数据模型、业务规则、验收场景,一条条写得清清楚楚。Agent 按 SPEC 开发,系统上线,一切正常。

两周后,你发现一个问题:有些文章是重复的,你收藏了同一个链接两次,系统里存了两份一模一样的正文。你心想,加个去重功能吧。

于是你打开 SPEC 文件,找到数据模型那一节,在文章表里加了一行:

code
- url_hash: string(URL 的哈希值,用于去重)

又找到业务规则,加了一条:

code
- 新增文章时,先检查 url_hash 是否已存在,存在则跳过

改完保存,告诉 Agent 按新的 SPEC 开发。Agent 照做了,去重功能上线。

看起来没问题对吧?但三个月后,新来的同事接手这个系统,打开 SPEC 文件,看到 url_hash 字段,不知道它是什么时候加的、为什么加、之前有没有其他方案被否决过。他想改数据结构,又不敢动,因为不知道这个字段还有哪些地方依赖它。

问题出在哪里?出在你直接改了 SPEC 文件,而没有留下任何变更记录。

这就是 SPEC 管理最典型的痛点:直接改文件,没有上下文,没有记录,没有回滚能力。一次两次还好,迭代十次之后,SPEC 文件里全是"历史遗迹"——你不知道哪些字段是核心设计、哪些是临时补丁、哪些已经废弃了。你不敢删,不敢改,只能往上堆。最后 SPEC 比代码还乱。

你可能说,我用 Git 管理不就行了?Git 能记录谁在什么时候改了哪一行,但它回答不了几个更关键的问题:这次变更的动机是什么?审批通过了吗?验收通过了吗?当前生效的到底是哪个版本?

所以你需要一套专门的管理机制。

6.2 OpenSpec 是什么

OpenSpec 是一套 SPEC 管理规范,核心理念就一句话:

所有变更,都先写成 SPEC,验收通过了再执行。

这不是什么新发明。你想想软件开发里怎么做需求变更的——先写变更申请,评审通过,排期开发,测试验证,上线。OpenSpec 把同样的流程搬到 SPEC 管理上。

核心区别在于:传统开发里,变更流程是人的事,开会、写邮件、签审批单。OpenSpec 把整个流程做成了一套文件结构 + 工作流,Agent 可以自动执行大部分步骤,你只需要做你最擅长的事——审批。

具体来说,这套机制长这样:

  • 你提出一个变更想法(比如"加去重")
  • Agent 自动生成变更 SPEC
  • 你审批,通过或打回
  • 通过后 Agent 按变更 SPEC 执行
  • 执行完自动验证,验证通过后合入主 SPEC
  • 锁定当前版本

每一步都有记录,每一步都可追溯。

6.3 三层结构

OpenSpec 把 SPEC 分为三层,每层解决一个问题。

第一层:变更层

目录结构:changes/<change-name>/SPEC.md

每次变更单独一个目录,里面放这份变更的 SPEC。变更 SPEC 只描述"这次要改什么",不涉及整个系统的完整规范。

比如你要加去重功能,就新建一个 changes/add-dedup/SPEC.md,里面只写去重相关的数据模型、业务规则、验收场景。简单、聚焦、独立。

第二层:主规范层

目录结构:specs/<domain>/SPEC.md

这是当前系统行为的完整规范。所有已验收的变更,最终都要合入这里。主 SPEC 就是系统的"宪法"——任何时候你想知道系统应该怎么做,看这里。

第三层:锁定层

文件:SPEC.lock.md

当前生效版本的快照。锁定的意思是:Agent 在执行任务时,只认这个版本的 SPEC。主 SPEC 可能还在编辑中,但 lock 文件指向的是上一个已验收的稳定版本。

这三层的关系,你可以类比前端开发里的依赖管理:

  • 变更层 = 你每次 npm install <package> 时加的那条记录
  • 主规范层 = package.json,记录了所有依赖的声明
  • 锁定层 = package-lock.json,锁定了当前所有依赖的确切版本

你每次装一个新包,package.json 里多一条记录,package-lock.json 更新锁定版本。哪天出问题了,回滚 lock 文件就能回到上一个稳定状态。OpenSpec 的三层结构也是同样的逻辑。

6.4 实际操作:给胶囊系统加"内容去重"

拿胶囊系统来走一遍完整的变更流程。

第一步:你提出变更

你在 Hermes 里说:"胶囊系统现在有重复文章的问题,同一个链接收藏两次,系统存了两份。我想加个去重功能。"

第二步:Agent 生成变更 SPEC

Agent 收到你的需求,去看了当前的主 SPEC,然后生成了一份变更 SPEC:

code
changes/add-dedup/SPEC.md
---
变更概要:新增内容去重功能
影响范围:数据模型(article 表)、业务规则(新增流程)
数据模型变更:
  - 新增 article.url_hash: string(URL 的 SHA256 哈希值)
  - 新增 article.dedup_key: string(URL 哈希的首次出现标识)
业务规则变更:
  - 新增文章流程:收到新链接 → 计算 url_hash → 检查 dedup_key 是否存在
  - 存在则返回已有文章 ID,不创建新记录
  - 不存在则创建新文章,写入 dedup_key
验收场景:
  - 输入相同 URL 两次,系统只存一份
  - 输入不同 URL,即使内容相似,也不误判为重复
  - 存量数据不受影响,新增数据才去重

第三步:你审批

你看了一遍,发现一个问题:Agent 写的验收场景里没有提到"用户再次收藏同一个链接时,应该返回什么"。你加了一条:

code
- 用户收藏已存在的 URL 时,系统提示"已收藏过",并跳转到原有文章

然后点了通过。

第四步:Agent 执行变更

Agent 按变更 SPEC 修改代码。加字段、改新增流程、写去重逻辑、改前端提示。改完之后自动跑验收场景——三个场景全部通过。

第五步:合入主 SPEC

Agent 把变更 SPEC 的内容合并到主 SPEC 里。specs/domain/SPEC.md 里多了 url_hashdedup_key 字段的说明,多了去重相关的业务规则,多了对应的验收场景。

第六步:更新锁定

Agent 更新 SPEC.lock.md,锁定当前版本。现在所有 Agent 在执行任务时,都认这个新版本。

整个流程走完,你去重功能上线了,而且留下了完整的变更记录。三个月后新同事来了,打开 changes/add-dedup/ 目录,看到这份变更 SPEC,就知道当时为什么加去重、怎么加的、验收了什么。他想改,也知道从哪里入手。

对比一下直接改文件的方式——没有对比就没有伤害。

6.5 OpenSpec 和 Shell Engineering 的关系

说到这里,你可能已经感觉到了:OpenSpec 不只是一个"管理 SPEC 的工具",它就是 Shell Engineering 的工程化实现。

回顾一下 11.2 的五阶段链路:想法 → 需求 → SPEC → Agent 开发 → 部署。OpenSpec 把第三阶段(SPEC)和第四阶段(Agent 开发)串起来了——所有变更都先写成 SPEC,验收通过再执行。Agent 不是自由发挥,而是严格按照 SPEC 的变更流程来工作。

换句话说,Shell Engineering 的"严格"体现在哪里?就体现在这套流程里。没有 OpenSpec,你说"让 Agent 按规范开发",但规范本身在变、没人记录、没人审批,Agent 拿到的规范和昨天拿到的可能不一样。有了 OpenSpec,规范本身被管理起来了,Agent 永远只认锁定层的版本,变更必须走完整的 propose → 审批 → apply → 验收 → 合入 → 锁定流程。

这就是形态四的 Shell + OpenSpec 工作流:一个完整的、可落地的工程化方案。

6.6 全章总结

回到 11.2 的五阶段图:想法 → 需求 → SPEC → Agent 开发 → 部署。

现在你已经走通了每一步:

  • 11.3:模糊想法 → grill-me 拷问 → 需求简报
  • 11.4:需求简报 → 初版 SPEC → 二次澄清 → 审批后的 SPEC
  • 11.5:SPEC 冻结 → plan(技术方案)→ converge(验证)→ 交付部署
  • 11.6:SPEC 工程化管理 → OpenSpec 三层结构 → 变更可追溯

整条链路由三件事贯穿:

SDD(规范先行开发)——先写 SPEC,再写代码。顺序不能乱。先写 SPEC,Agent 不会跑偏;先写代码,Agent 跑偏了你还不知道。

Shell Engineering(交付形态)——最终交付的不是代码,是一个可用的 Shell 项目。用户不需要关心你用了什么技术、数据存在哪里,他们只需要在对话里说一句话,系统就能完成一件事。

OpenSpec(规范管理)——SPEC 不是一次性的。系统会演进、需求会变更、团队会换人,SPEC 需要工程化管理。OpenSpec 的三层结构(变更层 + 主规范层 + 锁定层)让 SPEC 可追溯、可回滚、可协同。

这三件事是一条链,缺一环都不行。没有 SPEC,Agent 不知道做什么;没有 OpenSpec,SPEC 会腐烂;没有 Shell Engineering,项目交付不了。

走通一次,以后做任何 Shell 项目,你都有章法了。

6.7 ■ 学点英语

中文 English 音标 说明
规范变更管理 OpenSpec /ˈoʊpən spek/ 用主规范和增量变更记录管理需求演进的工作方式
分布式版本控制 Git /ɡɪt/ 记录文件变化并支持比较、协作和回退的版本控制系统
锁定 Lock /lɑːk/ 把当前已经验收的规范版本固定下来
哈希 Hash /hæʃ/ 把输入转换为固定长度摘要,用于比较内容是否一致
软件包 Package /ˈpækɪdʒ/ 可以被项目安装和复用的软件单元
版本 Version /ˈvɜːrʒən/ 用来区分规范或依赖在不同时间状态的编号
提议 Propose /prəˈpoʊz/ 在变更流程中正式提出一项修改
应用变更 Apply /əˈplaɪ/ 将已经批准的变更落实到项目中