OpenSpec迭代开发:基于历史档案的增量变更工作流

以"油价监控历史趋势图支持多油号"需求为例,记录在项目已有历史文档的情况下,OpenSpec 如何利用现有档案完成迭代开发。


背景

项目已通过 OpenSpec 完成过一次完整开发:

bash 复制代码
归档记录:
  openspec/changes/archive/2026-08-24-oil-price-monitor/
  ├── proposal.md
  ├── specs/oil-price-monitor/spec.md     ← 主规格文档(已存在)
  ├── design.md
  └── tasks.md

现有系统已上线运行,用户提出新需求:历史趋势图从单选油号改为多选油号

这是一个增量变更(迭代开发),而非从零开始的新项目。


1. Explore 阶段:借助历史代码理解现状

做了什么

用户提出需求后,进入 Explore 阶段纯粹讨论方案。

没有创建任何新文件。

关键动作是阅读现有代码,找到需要修改的位置:

scss 复制代码
┌─────────────────────────────────────────────┐
│  Explore 阶段                                │
│  - 读取 web/templates/index.html            │
│  - 找到 updateChart() 函数                  │
│  - 确认当前是单选油号下拉框实现              │
│  - 确定多选改造方案                          │
│  - 不生成任何文件                            │
└─────────────────────────────────────────────┘

历史档案利用方式:

本次 Explore 阶段没有显式读取 OpenSpec 档案(因为是在已有代码库上做变更,代码本身就是档案),但 AI 通过阅读代码理解了:

  • 当前的图表实现方式
  • 城市筛选已有复选框交互可参考
  • 油号选择器需要改造的位置

关键输出

确认了改造方案:

  • 自定义 checkbox 下拉展开式(参考城市筛选的交互)
  • 默认全选所有油号
  • 城市用颜色区分,油号用线型区分

2. Propose 阶段:声明变更范围,创建增量规格

创建了新变更目录

arduino 复制代码
openspec new change "oil-price-multi-oiltype-chart"
bash 复制代码
openspec/changes/oil-price-multi-oiltype-chart/     ← 新建
├── .openspec.yaml
├── proposal.md              ← 新建
├── specs/
│   └── oil-price-monitor/
│       └── spec.md         ← 新建(delta spec)
├── design.md               ← 新建
└── tasks.md               ← 新建

关键:读取历史主规格文档

在创建 delta spec 之前,AI 读取了归档中的历史主规格文档:

bash 复制代码
归档路径:
  openspec/changes/archive/2026-08-24-oil-price-monitor/
  └── specs/oil-price-monitor/spec.md     ← 被读取

历史规格中,"历史油价趋势图表"需求原文:

shell 复制代码
### Requirement: 历史油价趋势图表

系统 SHALL 提供 ECharts 折线图展示历史油价趋势。

图表 SHALL 支持多城市叠加显示,用户可勾选显示哪些城市。

图表 X 轴为日期,Y 轴为价格。

用户 SHALL 可选择切换不同油号查看。

这是创建 delta spec 的依据------AI 看到了"单选油号"的原始描述,才能写出"改为多选"的新描述。

proposal.md 中声明变更类型

markdown 复制代码
## Capabilities

### New Capabilities
- (无)

### Modified Capabilities
- `oil-price-monitor`: 修改"历史油价趋势图表"规格,
  将油号选择从单选改为多选,支持同时展示多个油号趋势

这里的关键是:oil-price-monitor已存在的 capability ,不是新创建的。OpenSpec 据此在 specs/oil-price-monitor/ 下创建了 delta spec。

delta spec 只写变化的部分

specs/oil-price-monitor/spec.md(delta)的内容:

erlang 复制代码
## MODIFIED Requirements

### Requirement: 历史油价趋势图表

**原内容:**
系统 SHALL 提供 ECharts 折线图展示历史油价趋势。图表 SHALL 支持多城市叠加显示...
用户 SHALL 可选择切换不同油号查看。

**新内容:**
系统 SHALL 提供 ECharts 折线图展示历史油价趋势,支持多城市与多油号同时展示。

油号选择器 SHALL 为多选下拉控件,初始默认全选所有6个油号...

图表每条线 SHALL 以"城市-油号"格式命名(如"北京-92#")。

城市 SHALL 用颜色区分,油号 SHALL 用线型区分...

没有重复历史文档中未变化的内容,只写了:

  1. 引用原内容(便于对比)
  2. 新内容(描述变更后的行为)

design.md 比首次开发更轻

因为架构已经确定(Flask + Bootstrap + ECharts),design.md 只需聚焦增量部分:

shell 复制代码
## Context
当前历史趋势图使用单选油号下拉框... 需求变更为多选油号。

## Decisions
### 1. 油号多选 UI 实现
自定义 checkbox 下拉展开式(参考城市筛选交互)

### 2. series 生成逻辑
从单油号遍历改为 (城市 × 油号) 双遍历

### 3. 线型映射
p92→solid, p95→dashed, ...

对比首次开发的 design.md:省去了技术栈选型、目录结构设计、API 设计等已在历史文档中确定的内容。

tasks.md 只列出增量任务

ini 复制代码
## 1. 前端 UI 改造
- [ ] 1.1 将油号下拉框替换为自定义 checkbox 下拉控件
- [ ] 1.2 实现下拉展开时显示6个油号checkbox,默认全部勾选

## 2. 图表数据逻辑
- [ ] 2.1 修改 selectedOilTypes 从单值改为数组
- [ ] 2.2 修改 updateChart() 的 series 生成逻辑

...

对比首次开发的 29 个任务,这次只有 11 个任务------因为后端代码完全不需要改动。


3. Apply 阶段:仅改动一行文件

实现前的状态

历史归档中已有完整实现代码:

arduino 复制代码
oil-price-monitor/
├── config/
├── data/
├── scripts/
│   └── fetch_price.py
├── web/
│   ├── app.py
│   └── templates/
│       └── index.html     ← 只需修改这个文件
├── requirements.txt
└── README.md

实现后的改动

只修改了一个文件:

bash 复制代码
web/templates/index.html     ← 唯一被修改的文件

具体改动包括:

  • CSS:新增 .oil-multiselect 等样式(约60行)

  • HTML:将 <select> 替换为自定义 checkbox 下拉控件

  • JS:

    • 新增 selectedOilTypes Set(替代原单值)
    • 新增 initOilTypes/toggleOilMultiselect/toggleOilType/updateOilTagsDisplay
    • 修改 updateChart() 的 series 生成逻辑
    • 修改 ECharts 配置

未改动:

  • web/app.py --- API 无变化
  • scripts/fetch_price.py --- 数据获取逻辑无变化
  • config/ --- 配置无变化

tasks.md 逐项标记完成

每完成一个任务就把 - [ ] 改为 - [x]

scss 复制代码
## 2. 图表数据逻辑
- [x] 2.1 修改 selectedOilTypes 状态管理,从单值改为数组
- [x] 2.2 修改 updateChart() 的 series 生成逻辑
- [x] 2.3 修改 series.name 格式为"城市-油号"

4. Archive 阶段:归档变更档案

执行归档

bash 复制代码
mv openspec/changes/oil-price-multi-oiltype-chart \
   openspec/changes/archive/2026-08-24-oil-price-multi-oiltype-chart

归档后目录结构

bash 复制代码
openspec/changes/archive/
├── 2026-08-24-oil-price-monitor/              ← 历史首次开发档案
│   ├── proposal.md
│   ├── specs/oil-price-monitor/spec.md
│   ├── design.md
│   └── tasks.md
│
└── 2026-08-24-oil-price-multi-oiltype-chart/   ← 新迭代档案
    ├── proposal.md
    ├── specs/oil-price-monitor/spec.md    ← delta spec
    ├── design.md
    └── tasks.md

两个档案并列存在,各自记录一次独立的变更。


5. 全流程文件变动总览

阶段 新建文件 修改文件 读取的历史档案
Explore --- --- web/templates/index.html(代码本身)
Propose proposal.md specs/oil-price-monitor/spec.md design.md tasks.md .openspec.yaml --- archive/.../specs/oil-price-monitor/spec.md
Apply --- web/templates/index.html tasks.md(逐项完成)
Archive --- --- ---

总新建文件数:5个(.openspec.yaml + 4个artifacts)

总修改代码文件:1个


6. 关键心得:OpenSpec 增量变更的设计

1. 增量变更的核心:delta spec

markdown 复制代码
历史主规格(不可修改)
    │
    │  ← 读取后创建 delta
    ▼
delta spec(记录变化)
    │
    │  ← 归档时可选同步到主规格
    ▼
主规格更新(可选)

Delta spec 的价值:不修改历史档案,只记录增量变化。即使归档后,也能看到每次变更的内容和原因。

2. proposal 是变更的入口

proposal.md 中的 Modified Capabilities 声明了哪个已存在的 capability 被修改。这决定了:

  • delta spec 放在哪个路径下(specs/oil-price-monitor/
  • OpenSpec 知道这不是新能力,而是对 oil-price-monitor 的修改

3. 任务量随存量递减

复制代码
首次开发:29个任务(全部新建)
第二次迭代:11个任务(只改 UI + 图表逻辑)
第三次迭代:(如果还有)可能只有3-4个任务

存量越大,增量越小。OpenSpec 的任务清单只记录"这一次需要做什么",而非重复历史已做的工作。

4. Archive 不合并,只归档

两次迭代的变更档案并列保存,不合并:

  • 第一次:archive/2026-08-24-oil-price-monitor/
  • 第二次:archive/2026-08-24-oil-price-multi-oiltype-chart/

这样做的好处是:可以追溯每次决策的背景和上下文。即使未来主规格被更新,历史 delta 仍然记录了当时的思考过程。

5. 实现代码和规划文档分离

bash 复制代码
规划文档 → openspec/changes/archive/yyyymmdd-xxx/
实现代码 → oil-price-monitor/

Archive 移动的是 openspec 下的规划文档,不是项目实现代码。两者独立存在,实现代码不受 OpenSpec 工作流的影响。

相关推荐
程序员柒叔1 小时前
luna 的内心独白:我把一个本该暂停的任务,跑成了几十轮空转
agent·ai编程·vibecoding
飞哥数智坊1 小时前
难道 AI 真要让程序员三班倒了?
人工智能·ai编程
leeyi1 小时前
Deep Agent 文件系统工具链:ls/read/write/edit/glob/grep/shell 七个工具怎么设计(第94篇-E80)
aigc·agent·ai编程
必须会一定会1 小时前
Agent Handoff M5 发布验收:`CHANGES.md`、`npm pack`、`release:check` 与干净环境安装验证
前端·人工智能·npm·node.js·ai编程
程序员-李俞2 小时前
Coze 工作流调用异步 HTTP API 完整教程:任务 ID、循环轮询、状态判断与结果 URL 提取
网络·人工智能·网络协议·http·aigc·ai编程·ai写作
Setsuna_F_Seiei10 小时前
前端的 AI 学习之路 02 之 Provider 与 Structured Output - 规范化模型输入输出
人工智能·agent·ai编程
Setsuna_F_Seiei11 小时前
前端的 AI 学习之路 01 之 Agent API 调用 - 和 Agent 的基础对话
前端·人工智能·ai编程
Jooolin14 小时前
AI项目实战日记ep1:从零做一个 AI 日志分析助手
ai编程
小虎AI生活15 小时前
用 WorkBuddy 三个 Skill 串起一条流水线:8 张随手拍照片 30 分钟变口播视频
ai编程