Files
git-learn/skill-workbench/docs/dev-flow/background-and-evolution.md
T

573 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dev Flow:背景与技术演进
**状态**:设计中
**日期**:2026-07-05
**范围**:说明 `dev-flow` skill 为什么存在、从哪来、为什么是现在这个形态
---
## 1. 这份文档解决什么
`dev-flow` 的核心只有一条规则:**每个非平凡变更必须留下 `decision.md`**。
这个规则看起来很轻,但它是从五次设计迭代、一次完整协议重写、以及和三个外部实现的对比里收敛出来的。**如果只看结论,很容易觉得它"太简单了,不够"**——所以这份文档把推理链完整留下来。
配套文档:
- `.agents/skills/dev-flow/SKILL.md` — 运行协议
- `.agents/skills/dev-flow/references/decision-note.md` — 格式规范与校准案例
---
## 2. 背景:为什么需要一层"决策档案"
### 2.1 原始动机(2026-05-18 前后)
第一次完整跑通一个 AI 驱动的开发流程后,暴露的问题是**产物散落**:
```text
CONTEXT.md ← 根目录
docs/adr/ ← 架构决策
docs/agents/ ← PRD
openspec/changes/ ← 提案与规格
knowledge/entries/ ← 知识条目
```
三个月后想回溯"这个项目到底做了什么决策",需要同时翻 4 个目录。**产物按"工具来源"组织,而不是按"项目"组织。**
这是 `devflow/` 聚合层的直接起因,设计上借鉴了 CodeStable 的**单一聚合根**。
### 2.2 当时确立的两层分工
| | `openspec/changes/` | `devflow/projects/` |
|---|---|---|
| 角色 | 工具工作区(WAL) | **人类可读档案层(Tables)** |
| 谁读 | 机器 | **人** |
| 生命周期 | 活跃变更期 | 永久保留 |
| 组织方式 | 按变更名 | 按日期 + 项目 |
**"给人看"是明确的设计目标**,不是副产品。这一点在后来的讨论中被反复确认,也是最终形态的关键约束。
### 2.3 但有一个前提,后来被证伪
原始设计文档里写着:
> `openspec/changes/` 是工具工作区(WAL),**archive 后清空**;`devflow/projects/` 永久保留。
**在真实仓库里这个前提不成立。** 实测:
```text
openspec/archive/2026-05-19-add-clear-filters/
openspec/archive/2026-05-21-sm-flow-v3-1-upgrade/
openspec/archive/2026-07-05-validate-sm-flow-explicit-trigger/
openspec/changes/archive/2026-05-25-knowledge-index-sort/
```
归档内容**没有被清空**,而且历史上出现过两个归档位置(`openspec/archive/` 与 `openspec/changes/archive/`)。
后来设计文档自己把真正丢失的东西说清楚了:
> 旧:`openspec archive` 只归档需求,项目的**术语、业务规则、架构决策**散落在对话中,下次新对话全丢
关键点:**丢的不是变更产物,是 `why`。** 术语有 `devflow/glossary/CONTEXT.md` 承载;而"架构决策"就是决策档案本身。
**结论:"需要一层新目录"这个判断,建立在错误的前提上。**
---
## 3. 技术演进
### 3.1 v1 — `dev-flow` 骨架
`.claude/skills/dev-flow/` 的最早形态:Phase 1-4 流水线(手动 PRD → openspec propose → to-prd → grill → apply),外加一个 `devflow/` 目录约定。
### 3.2 v2.0.0 — 产物聚合层(现存 213 行)
现行 `.claude/skills/dev-flow/SKILL.md` 的版本。核心贡献:
```text
devflow/
├── projects/YYYY-MM-DD-{slug}/
│ ├── {slug}-prd.md {slug}-research.md
│ ├── {slug}-design.md {slug}-tasks.md
│ ├── {slug}-acceptance.md
│ └── adr/
├── glossary/CONTEXT.md
├── compound/
└── reference/
```
确立了两层架构与"Phase 4 从 OpenSpec 提取到 devflow"的回填模式。
**它已经埋下了后来的两个问题**:
1. **数量不封顶**——一个项目 5 个文件起,`adr/` 与 `compound/` 还可再长
2. **提取 = 有损转换**——`{slug}-tasks.md` 是从 openspec `tasks.md` **提炼**出来的,同一事实两个权威
### 3.3 sm-flow v2 — 产品化
`dev-flow` 被重构为 `.agents/skills/sm-flow/`,把设计说明书拆成代理可执行的 skill 结构(`SKILL.md` + `references/`)。
### 3.4 sm-flow v3 / v3.1 — 真理源分层与产物瘦身
v3 解决的问题:**devflow 产物越来越完整,agent 开始直接拿 devflow 写代码,OpenSpec 被架空。**
确立分层:
```text
devflow = 上下文真理源 OpenSpec = 执行真理源 代码 = 结果
```
v3.1 进一步明确 **devflow 不复制 OpenSpec**,只保存它不擅长表达的人类上下文、证据、决策和验收归档。默认产物收敛为 `brief` / `evidence` / `decisions` / `acceptance`。
**这一步方向是对的,但执行没有一致性保障**——见第 5 节的实测。
### 3.5 sm-flow v4.0 — 协议层 harness 重设计
一次设计评审后做了一次**结构性重写**(不是加规则):
| 改动 | 性质 |
|---|---|
| 19 条规则 → 6 条硬约束 | 删减 |
| `SKILL.md` ~85 行 → ~50 行 | 删减 |
| **grill 提到 specify 之前** | 消除"细化 → 澄清 → 回写"的必然返工 |
| devflow 延迟写入:过程只维护 `decisions.md` | 消除双重记录 |
| 阶段改英文动词命名,4 个用户命令 | 接口收窄 |
设计文档自己写下了这次重写最重要的判断:
> **这不是 agent 执行问题,是流程顺序决定了返工必然存在。**
**v4.0 是这条演进线上唯一一次"减法生效"的版本。**
### 3.6 sm-flow v4.1 / v4.2 — 两次应激加码
| 版本 | 触发原因 | 成本 |
|---|---|---|
| v4.1 | 山东商客意向单同步接口返工 4-5 次 | +55 行,门控 4→6 |
| v4.2 | `lookup-knowledge-integration` 跳过 7 个阶段 | +80 行,门控 6→8,新增 `.committed` / `.archive-ready` |
v4.2 的核心主张是**把软性约束变成硬性检查**——但形式上仍然是提示词里的检查清单。
两个值得记录的问题:
1. **v4.0 的验证复盘已经观察到同类跳过行为,并明确决定"暂不改协议"**(原文:"问题 1 和 2 暂不改协议,如果多次验证中反复出现再考虑加约束")。**v4.1/v4.2 违反了自己定下的门槛。**
2. **v4.2 的动机案例按 v4.3 的触发规则根本不算 sm-flow 运行**——优化建议文档自己标注"执行模式:手动跳阶段(用户直接要求修复问题)"。
### 3.7 sm-flow v4.3 — 回到减法
| 改动 | 性质 |
|---|---|
| **只显式触发**(不再按需求类型自动触发) | 砍掉常驻税与假阳性违规 |
| `.agents/skills/sm-flow/references/scales.md` 成为分档**唯一**来源 | 消除重复定义 |
| 新增 `.agents/skills/sm-flow/references/glossary.md` | 统一术语 |
| **拒绝**引入 `.sm-flow-state` 状态文件 | 主动不做 |
| 删掉固定投入指标 | 去掉会腐化的经验值 |
**v4.3 是自 v4.0 以来第一个走简化方向的版本。** 它证明这条演进线是活的。
### 3.8 现状实测(截至本文)
`sm-flow` skill 本体:
| 指标 | 实测 |
|---|---|
| 文件数 / 体积 | 8 个 / 58,597 B |
| 总行数 | **1,253 行** |
| "必须 \| 不得" | **52 处** |
| 列表项 | **454 个** |
| 首次加载(SKILL.md + phase-contracts) | ≈ 8.5k tokens(**占全量一半**) |
| 单次 standard 变更产物 | OpenSpec 7 文件 14.8 KB + devflow 5 文件 20.3 KB = **35 KB** |
设计文档自己给出的成本表:
| 规模 | 代码工作量 | 流程开销 | 占比 | 自评 |
|---|---|---|---|---|
| micro | 30 min | 40–60 min | **~60%** | **太重** |
| standard | 2 h | 1–1.5 h | ~40% | 还行 |
| complex | 1–2 d | 2–3 h | ~20% | 值得 |
---
## 4. 对照外部实现
为了让判断不建立在自我推演上,对三个外部实现做了一手对照。
### 4.1 三个外部实现的做法
| | 状态持有者 | 强制力在哪 | 约束对象 |
|---|---|---|---|
| **OpenSpec**(v1.13) | 产物依赖图,**CLI 计算** readiness | `status --json` 与 schema | 一次变更的产物 |
| **Trellis**(0.7) | `task.json.status` + **hook 每轮注入** | 平台 hook | 一轮该做什么 |
| **DSH Agent Notes** | **文件路径** | **CI 脚本 + PR 原子性** | 一个决策必须留下理由 |
| *sm-flow* | *agent 的 checkpoint 陈述* | *提示词自查* | *阶段顺序* |
**三家都把强制力移出了提示词,sm-flow 是唯一没移的。**
### 4.2 关键发现一:DSH 并不强制"必须写"
DSH 有 19 个 CI workflow、110+ 个 `verify-*` 脚本,其中与 Agent Notes 相关的只有三个:
| 脚本 | 管什么 |
|---|---|
| `verify-agent-note-classification.ts` | 路径是否在关闭集内 |
| `verify-agent-note-format.ts` | 三行头 + 骨架 + **`## Alternatives considered` 必须存在** |
| `verify-archived-agent-notes.ts` | 冻结三元组 + 哈希 manifest |
检索确认:**没有任何脚本强制"改了代码就必须有笔记"。** 那条规则靠约定与人工 review。
**一个 1,026 篇笔记、110 个校验脚本的项目,把所有机器能查的东西都做成了脚本,却单独把这一条留给了判断。**
原因是"非平凡"本身是判断,机器强制会产生假阳性——正是 pre-commit hook 拦琐碎提交的那个问题。
**结论:任何"三条时机不变量 + hook"的方案都比业界最成熟的实现更重。这个方向的复杂度应当被否决。**
### 4.3 关键发现二:devflow 层的前提被证伪
见第 2.3 节。归档不丢数据,丢的是 `why`。
### 4.4 收敛出的三条原则
**① 不新增检查点,把义务挂在已有的动作上。**
- DSH:写新笔记 → 顺手检查旧笔记是否被取代
- DSH:改代码 → 同一次变更里更新笔记的事实
- `triage`:决定拒绝 → 同时写 `.out-of-scope/`
- *反面*:sm-flow 现在新增 4 个 checkpoint + 8 个门控文件 + 各类自检清单
**② 检查只有一个,放在收尾边界。**
- OpenSpec:archive 时检查完整性
- Trellis:finish 时拒绝在不一致状态下完成
两家独立收敛——不是"全过程多条不变量",而是"收尾时一次检查"。
**③ "是否该写"是判断,不强行机器强制。** 见 4.2。
---
## 5. devflow 现状实测:问题不是"文件太多"
`devflow/` 现状:
| 指标 | 实测 |
|---|---|
| 文件数 / 体积 | **37 个 / 85.0 KB** |
| 项目数 | 9 |
| 每项目文件数 | **2 – 7,无规律** |
| 命名约定 | **4 套并存** |
| **含"替代方案"的文件** | **1 / 37** |
### 5.1 四套命名约定并存
| 世代 | 形态 |
|---|---|
| 早期 | `{slug}-{prd,design,research,tasks,acceptance}.md` |
| 早期 | 自由式(`dev-flow-skill-evaluation.md` + `todo.md`) |
| 早期 | `adr/` 子目录 |
| v3.1 后 | `{brief,evidence,decisions,acceptance}.md` |
**没有任何机制阻止格式漂移**,因为格式从未被校验。
### 5.2 真正的病:同一个"5 个文件",两种质量
| 项目 | 文件数 | 与 OpenSpec 的关系 |
|---|---|---|
| `add-year-filter` | 5 | **严重重复**——`-design.md` 与 `design.md` 同一件事写两遍;`-tasks.md` 与 `tasks.md` 任务核对两遍 |
| `sm-flow-execution-hardening` | 5 | **做对了**——`brief.md` 写的是"OpenSpec 对齐:已覆盖",用引用代替复制 |
**两种质量共存,而文件名看不出区别。** 只能靠打开和回忆。
### 5.3 最稀缺的内容几乎为零
在整个 `devflow/` 里检索 `备选|替代方案|放弃了|代价|alternatives|trade-off|权衡|否决`:
```text
Found 1 match
devflow/projects/2026-05-18-knowledge-index-panel/adr/0001-directory-name-as-truth-source.md
Line 21: ## 替代方案
```
**37 个文件、85 KB,只有一篇文章记录了"我们放弃了什么"。** 而它的骨架恰好就是决策档案的骨架:
```markdown
## 背景 ← Problem
## 决策 ← Decision
## 替代方案 ← Alternatives considered
## 后果 ← Consequences(正面 / 负面分开)
```
**正确答案在 2026-05-18 就被写出过一次,此后 9 个项目再没出现第二次——因为没有机制要求它。**
---
## 6. 决策:三个方案
| | A:devflow 分层 + 同步机械 | B:devflow 只给 agent 读 | **C:取消 devflow 层** |
|---|---|---|---|
| 决策笔记落点 | `devflow/decisions/<lifecycle>/<class>/` | 同 A,但格式机器优先 | **`openspec/changes/<slug>/decision.md`** |
| 受众 | 人 + agent | 只 agent | **人 + agent** |
| 同步问题 | 有,需 3 条不变量 + hook 解 | 有,且对 agent 更严重 | **不存在** |
| 新增机械 | ~200 行 + hook | ~60 行 | **~0 行** |
| 写入时机 | 需要保证 | 需要保证 | **天然正确**(写变更产物的同一时刻) |
| 人的可读性 | 是 | 否 | **是** |
### 为什么否决 A
A 要新增三条时机不变量 + pre-commit hook。**4.2 的发现表明这比 DSH 更重**,而 DSH 是靠约定解决的。
更根本的是:A 是在**解决同步问题**;而 4.3 表明这个同步问题**本来不需要存在**。
### 为什么否决 B
B 的直觉是"只给 agent 读就不用管可读性和索引,复杂度就降了"。但:
- 省掉的只是 `index.md` 和文笔(**便宜**)
- 省不掉时机同步(**昂贵**),而且对 agent 阅读**更关键**——agent 会照着过时的记忆干活
而且 A/B 是超集关系:一份写好的人类可读决策档案**同时**是 agent 能读的;反过来不成立。
**唯一支持 B 的论证是"没人读的档案会腐烂"——而这一点已被 5.3 实测证明:覆盖率 1/37,无人校对。**
### 为什么选 C
1. **它消除问题而不是解决问题**——同步复杂度归零
2. **它纠正了事实错误**:devflow 层的主要理由(archive 后清空)不成立
3. **它符合设计原则 ①**:写入义务挂在"写变更产物"这个已有动作上
4. **它满足原始设计目标**:`decision.md` 是给人看的,而且和它描述的那次变更放在一起,人类读起来上下文最完整
保留下来的是三样最有价值的东西:
- **强制 `## Alternatives considered`**(ADR-001 就是范例)
- **`glossary/CONTEXT.md`**(真正的跨项目知识)
- **`rejected/`**(未实施提案的归宿,DSH 与 `triage` 独立收敛的机制)
放弃的是:devflow 作为独立"人类档案层"的存在感,以及 A 的那套同步机械。
---
## 7. 新 dev-flow 的设计
### 7.1 定位:替代 sm-flow
**dev-flow 是一套轻量开发流程,并且替代 sm-flow。** 这不是"两套正交工具",而是一次替换:
| | sm-flow | dev-flow |
|---|---|---|
| 身份 | 协议层 harness,编排 9 阶段 | **轻量开发流程,3 阶段** |
| 人类确认点 | 4 | **2** |
| 分档 | micro / standard / complex | **无** |
| 协议行数 | 1,253 | **~150** |
| 单次变更产物 | OpenSpec 7 文件 + devflow 5 文件 = 35 KB | **OpenSpec 4 件 + `decision.md`** |
| 强制内容 | "按顺序做 9 件事并逐项自查" | **"放弃了什么"** |
| 强制力 | 提示词 + 8 个自查清单 | **1 个收尾检查** |
**sm-flow 的归档由用户手动执行**,在 dev-flow 实现完成之后。
### 7.2 三阶段流程
```text
Think 想清楚 → Build 做出来 → Close 收好尾
⏸ 确认点 1 ⏸ 确认点 2
授权进入实现 是否归档
```
| 阶段 | 做什么 | 用 OpenSpec 的 |
|---|---|---|
| **Think** | 澄清 → 读上下文 → 写 proposal/design/specs/tasks → **写下 `decision.md` 的 Problem 与 Alternatives** | `openspec-propose` |
| **Build** | 按 tasks 分步实现 + 验证;冲突三分法 | `openspec-apply-change` |
| **Close** | 补齐 Decision / Consequences / Verification → 收尾检查 → 归档确认 | `openspec-archive-change` |
**为什么是 3 个而不是 9 个**:sm-flow 的 9 阶段里有一半是"为了防止 agent 偷懒"而设的编排层。当强制力改由文件与收尾检查承担之后,这些中间阶段失去了存在理由。**Trellis 用 Plan / Execute / Finish 三段跑通,是同一判断的独立佐证。**
**为什么不分档**:sm-flow 的分档设计者自己评分 micro 开销约 60%、"太重"。**流程本身就轻的时候,分档是多余的**——它只是在给一个重流程寻找减负的借口。
### 7.3 产物布局
```text
openspec/changes/<slug>/
├── proposal.md
├── design.md ← 实现设计(执行向)
├── specs/
├── tasks.md
└── decision.md ← why + 备选 + 代价 + 验证(人向)
devflow/
├── glossary/CONTEXT.md ← 跨项目术语
├── rejected/<class>/ ← 未实施的提案
└── index.md ← 脚本扫描归档生成,不手写
```
### 7.4 保留了 sm-flow 的什么
dev-flow 不是全盘重写。三样东西被完整继承:
- **Draft / Committed 思想** → 变成**确认点 1**("未获授权不得进入 Build")。这是三家外部实现独立收敛到同一处的设计。
- **冲突三分法** → 完整保留,是四家对比里唯一没找到对应物的原创资产。
- **显式触发** → 保留,v4.3 唯一一次生效的减法。
放弃的是:9 阶段编排、micro / standard / complex 分档、8 个自查清单、`.committed` / `.archive-ready` 门控文件、每阶段声明 capability 来源的仪式、以及 devflow 的 4–5 件套档案。
### 7.5 三条设计原则
见 `SKILL.md`。**① 义务挂在已有动作上 ② 确认点只有两个 ③ "是否该写"是判断。**
### 7.6 能力来源:编排,而不是重写
dev-flow 和 sm-flow 一样是**元技能**——由多个子 skill 实现。但组合的务实程度不同:
| | sm-flow | dev-flow |
|---|---|---|
| 组合的子 skill | 8 个(3 个 OpenSpec + grill / to-prd / zoom-out / diagnose / tdd) | **5 个**(3 个 OpenSpec + grill-with-docs + diagnose / tdd) |
| 能力声明 | 每个阶段显式声明 capability 来源 | **一张表,不逐阶段声明** |
| 不可用时 | `references/fallbacks.md`,8 个内置协议(49 行) | **一行规则**:按能力绑定;不可用时读本地 `SKILL.md`;仍不可用时按最小等价协议直接产出文件并注明 |
| 不使用 | — | `to-prd`(发布到 issue tracker,且与 `proposal.md` + `specs/` 重复) |
| 不设阶段 | — | `audit`。`zoom-out` 降为按需能力,不再是关卡 |
**设计期间发生的三次修正,值得记录:**
1. **澄清环节一度被重写。** 初版 `phases.md` 把"一次一问、能查代码的自己查、术语沉淀进 `CONTEXT.md`"又写了一遍——而这三条 `grill-with-docs` 已经实现了。
这正是 sm-flow v2 自己修过的错(元技能复述子技能逻辑)。现已改为**引用 + 只保留 dev-flow 特有的部分**。
2. **`grill-with-docs` 的取向与本流程有张力。** 它要求 "interview me **relentlessly** … until we reach a shared understanding",而 dev-flow 要轻。
解法是分层:**relentless 是对问题质量的,不是对数量的**——方法借用,范围收窄。
3. **ADR 与 `decision.md` 原本会重复。** `grill-with-docs` 会提议创建 ADR(需同时满足难以逆转 / 缺少上下文会困惑 / 源自真实权衡),而 dev-flow 要求每个变更写 `decision.md`,两者形态几乎相同。
处置:**`decision.md` 吸收 ADR**,不再另建 `adr/` 目录。ADR 三条件从"要不要写"降级为"值不值得写深"。
(DSH Agent Notes 用 `architecture` class 承担 ADR 角色,是同一思路。)
4. **冲突面比预想的宽。** 逐一实测后,`grill-with-docs` 有**三处**默认与本仓库不符:
| 它的默认 | 本仓库实际 |
|---|---|
| ADR 写入 `docs/adr/` | `docs/adr/` **不存在**;实际在 `devflow/projects/*/adr/`(3 个目录) |
| 用 `ADR-FORMAT.md` | dev-flow 用 `decision-note.md` |
| 假设根目录 `CONTEXT.md` | 用 `devflow/glossary/CONTEXT.md`(根目录是 235 B 重定向 stub) |
#### 裁决:都不内置
理由是结构性的,不是偏好:
> **内置只在被组合 skill "消失"时才消除冲突。** 只要它还装在目录里(用户可以直接调用),
> 内置就只是让 dev-flow 多出一份**会漂移的私有副本**——重复实现加上版本漂移,两个问题。
改成一条**可判定的归属规则**,它能覆盖未来还没组合的 skill:
> **子 skill 管"这一步怎么做";dev-flow 管"做多少、结果放哪"。冲突时,流程赢。**
> 归属规则解决不了的 → **不组合它**(`to-prd` 就是这么处理的)。
| 内容 | 归属 |
|---|---|
| 一次一问、先查代码、场景压测 | `grill-with-docs`(方法,引用不内联) |
| 问多少、何时停、要不要写 ADR | **dev-flow** |
| `CONTEXT.md` 在哪、ADR 落哪、用什么格式 | **dev-flow** |
#### 为什么 `openspec-*` 尤其不能内置
它们由 `openspec update` 生成,同时存在于 `.claude/skills/` 和 `.codex/skills/`,是**外部管理的多副本产物**。只能按能力名引用。
**顺带发现**:`.agents/skills/` 下**没有** `openspec-*`,所以在本环境里"优先用平台原生 skill"会失败,必须降级到"读本地 `SKILL.md`"(`.claude/` 或 `.codex/` 下有)。这正好验证了"能力绑定而非路径绑定"这条规则在**兜住一个真实的不一致**——而不是一条装饰性的设计原则。
---
## 8. 验证记录
### 8.1 `openspec validate` 是否容忍 change 目录内的 `decision.md` —— ✅ 容忍
这是方案 C 的硬前提。**已实测确认。**
| 步骤 | 结果 |
|---|---|
| 基线:`validate knowledge-index-panel` | `is valid` / exit=0 |
| **探针:加入 `decision.md`** | `is valid` / **exit=0** ✅ |
| **反向对照:抽掉 `proposal.md`** | **exit=1** ✅ 证明探针可信 |
| 恢复后复验 | exit=0 |
| 工作区洁净度 | 干净,无残留 |
**反向对照是必须的。** 第一次探测只跑出 exit=0 就下结论,那是假阳性——CLI 当时根本没执行。
### 8.2 环境约束:受限沙箱下无法捕获外部程序输出
本次踩到的坑,记录下来避免重复:
```powershell
$x = & openspec validate foo # ❌ 捕获 -> 命令静默不执行,仍返回 0
& openspec validate foo | Select-Object -First 5 # ❌ 管道 -> 同样失效
& openspec validate foo # ✅ 直连控制台 -> 正常
```
**后果**:所有 CLI 验证必须让输出直连控制台。否则会得到"永远通过"的假象——**而且比没有验证更危险**,因为它会让人相信一个不存在的结论。
### 8.3 顺带发现:`add-year-filter` 通不过校验——根因是 UTF-8 BOM
```text
Change 'add-year-filter' has issues
✗ [ERROR] knowledge-filtering/spec.md: No delta sections found.
✗ [ERROR] file: Change must have at least one delta.
```
**这个报错会误导。** `specs/knowledge-filtering/spec.md` 第 1 行**就是** `## ADDED Requirements`,完全按要求写的。
真正的原因是文件带 **UTF-8 BOM**——解析器把行首标题读成了 `\ufeff## ADDED Requirements`,匹配不上。
字节级实测,相关性完美:
| change | 首 4 字节 | BOM | validate |
|---|---|---|---|
| `add-year-filter`(4 个文件全部) | `EF BB BF 23` | **有** | ❌ 失败 |
| `knowledge-index-panel` | `23 23 20 57`(`## W`) | 无 | ✅ 通过 |
| `sm-flow-execution-hardening` | `23 23 20 57` | 无 | ✅ 通过 |
**这个结论差点写错。** 第一版是从报错**推断**出"它的 spec 用的是普通 `##` 小节",而没有实际读那个文件——
读进去才发现推断是错的。**报错信息描述的是解析器看到的东西,不是文件里实际写的东西。**
本地 CLI 版本 **1.3.1**(官网最新 1.13.0),偏旧,但 BOM 不兼容与版本关系不大——这是一个**可机械修复**的问题(去掉 BOM 即可)。
### 8.4 dev-flow 首次验证(retrofit)
用 `add-year-filter` 做了一次 **retrofit** 验证:Think 与 Build 在历史上已发生,所以本次验证的是 **Close 阶段与产物格式**。
产物:`openspec/changes/add-year-filter/decision.md`(4,198 B)
| 验证项 | 结果 |
|---|---|
| 五节骨架齐备 | ✅ |
| 新文件无 BOM | ✅ |
| **收尾检查 3 项** | ✅ **全部机械通过** |
| 加入 `decision.md` 后 validate 报错集合未变 | ✅ 未引入新问题 |
| **补出被丢弃的备选** | ✅ 第 3 条「只改 HTML 不改生成脚本」原本藏在 `design.md` 的"架构风险"里 |
**未被验证的部分**(本次覆盖不到):Think 的澄清、确认点 1、Build 的冲突三分法,以及**冲突裁决**——retrofit 没有调用 `grill-with-docs`,所以三项覆盖一次都没触发。
**本次暴露的一个格式缺口**:`## Verification` 要求"可重跑的命令",但**最有说服力的那条验证会修改文件**(重建 `knowledge-index.html`),导致收尾时不敢跑,只好把它拆成"已实测"和"需执行"两段。
→ 应补充约定:**区分只读命令与有副作用的命令**。这是格式需要补的第一处。
---
## 9. 尚未解决
| # | 问题 | 处置 |
|---|---|---|
| 1 | 旧的 `.claude/skills/dev-flow/`(2.0.0,213 行)同名但设计相反 | **用户后续处理** |
| 2 | `sm-flow` 归档 | **用户后续处理**(dev-flow 实现完成之后) |
| 3 | 现有 3 个活跃 change(`knowledge-index-panel` / `add-year-filter` / `sm-flow-execution-hardening`) | **本次忽略** |
| 4 | 现有 9 个项目 37 个 devflow 文件的迁移 | 未定;建议先做单样本再定规则 |
| 5 | `rejected/` 是否照搬 DSH 的 6 类关闭集 | 沿用,未验证 |
| 6 | "计划写在 `decision.md` 却从未更新为事实"会不会出现 | 观察到再引入 DSH 的两套骨架 |
| 7 | `devflow/index.md` 与 `scripts/check-dev-flow.sh` 尚未编写 | 脚本待写;index 暂手工 |
---
## 10. 附录:对照结论一览
| 收敛的设计 | 出现处 |
|---|---|
| 状态必须外化到文件 | 全部四家 |
| **强制力不该放在提示词** | OpenSpec(CLI 计算)/ Trellis(hook 注入)/ DSH(CI 脚本) |
| 保留被否决的方案以防重新争论 | DSH `rejected/` / `triage` `.out-of-scope/` / OpenSpec archive |
| 单一来源,不建中央索引 | DSH(明令禁 INDEX)/ Trellis(hook parser-only,零文案副本) |
| 角色/上下文隔离靠进程 | Trellis(research/implement/check 子代理)/ OpenSpec(规划 skill "Never code") |
| 按需加载 | OpenSpec(optional skills)/ Trellis(JSONL manifest) |
| 不新增检查点,义务挂在已有动作上 | DSH(supersession)/ `triage`(决定即写) |
| 检查只在收尾一次 | OpenSpec(archive)/ Trellis(finish) |
| 用真实案例校准,不用阈值 | DSH("word count is never the test") |