- Record rejected answers in decision.md at the moment they are rejected - Allow explicit pre-authorized Build via a pre-authorized marker line - Land scripts/check-dev-flow.sh and derive devflow/index.md from archives - Add dev-flow process artifacts (decisions, prd) for open changes
9.7 KiB
name, description
| name | description |
|---|---|
| dev-flow | 轻量开发流程:想清楚 → 做出来 → 收好尾。编排 OpenSpec 的 propose / apply / archive 三个能力,并为每个非平凡变更留下 decision.md——记录为什么这么做、否决了哪些备选、代价是什么。在用户显式调用 /dev-flow,或明确要求走开发流程时使用。 |
Dev Flow
三个阶段,两个确认点,不分档。
Think 想清楚 → Build 做出来 → Close 收好尾
⏸ 确认点 1 ⏸ 确认点 2
授权进入实现 是否归档
每个非平凡变更留下一份 decision.md——记录代码和规格承载不了的东西:为什么这么做,以及放弃了什么。
能力来源
dev-flow 编排已有能力,不重复实现它们:
| 阶段 | 能力 | 用途 |
|---|---|---|
| Think | grill-with-docs |
澄清:一次一问、能查代码的自己查、术语沉淀进 CONTEXT.md |
| Think | openspec-propose |
生成 proposal.md / design.md / specs/ / tasks.md |
| Build | openspec-apply-change |
按规格实现 |
| Build | diagnose / tdd |
按需:bug 排查 / 测试驱动 |
| Close | openspec-archive-change |
归档 |
按能力绑定,不按路径绑定:优先用平台原生 skill;不可用时读本地 SKILL.md 并按其协议执行;仍不可用时按最小等价协议直接产出文件。不可用时在 decision.md 里注明。
不使用 to-prd:它把 PRD 发布到 issue tracker(本仓库没有),且内容与 proposal.md + specs/ 重复。
不设 audit 阶段:zoom-out 保留为按需能力(不熟悉代码区域时拉高视角),但它不是一个必须走的关卡。
冲突裁决
被组合的子 skill 与本流程会冲突——这是组合的固有代价。靠规则裁决,不靠内置。
子 skill 管"这一步怎么做";dev-flow 管"做多少、结果放哪"。 冲突时,流程赢。
当前组合里,由 dev-flow 覆盖子 skill 默认的有三处:
| 子 skill 的默认 | dev-flow 的覆盖 |
|---|---|
grill-with-docs 要求 relentlessly 追问 |
范围收窄:只问答案会改变产物的问题 |
ADR 写入 docs/adr/,用 ADR-FORMAT.md |
落点改为 decision.md,格式用 decision-note.md,不创建 docs/adr/ |
假设根目录 CONTEXT.md |
用 devflow/glossary/CONTEXT.md |
归属规则解决不了的冲突,不要靠内置解决,而是不组合它。
理由:内置只在被组合 skill 消失时才消除冲突。只要它还装在目录里(用户可以直接调用), 内置就只是让 dev-flow 多出一份会漂移的私有副本——重复实现加上版本漂移,两个问题。
to-prd就是这么处理的:直接排除,而不是改造成 dev-flow 的版本。
Think — 想清楚
进入:用户给出需求——粗略想法、issue、草稿、已有 PRD 都算。
动作:
- 澄清 —— 用
grill-with-docs的方法,但按上面的 「冲突裁决」 覆盖它的默认: 范围收窄(只问答案会改变产物的问题)、ADR 落点为decision.md、CONTEXT.md用devflow/glossary/下那份。 被否决的答案当场记进decision.md的## Alternatives considered(一行一条:问过什么、为什么输)——这是 Q&A 形态,见references/decision-note.md)。 - 读上下文:
devflow/glossary/CONTEXT.md,并搜索devflow/rejected/与已归档的decision.md—— 避免重新提出已经被否决过的方案。 - 写
openspec/changes/<slug>/proposal.md,并用 OpenSpec 补全design.md/specs//tasks.md。 - 写下
decision.md的## Problem和## Alternatives considered。 理由在这一刻最新鲜;留到事后补写会变成回忆录。
退出:proposal 能说清做什么、范围、非目标;decision.md 有 ## Problem 和至少一条备选(或用 <!-- alternatives-not-recorded --> 显式声明没有)。
⏸ 确认点 1 —— 向用户汇报方案方向、关键假设、主要风险,然后等待明确的实现授权。
未获授权不得进入 Build。 方案讨论中的任何一次确认,都不等于实现授权。 快节奏交付下可以预授权,但必须显式:Think 退出时在
decision.md写下<!-- pre-authorized: 日期 + 范围/原因 -->。静默跳过不允许——旁路是有意识的选择,不是遗忘。 授权状态以这行标记为准,恢复会话时读它,不靠对话记忆。
Build — 做出来
进入:确认点 1 已过。
动作:
- 按
tasks.md的纵向切片实现,按specs/验收。 - 分步验证:每完成一层再继续,不要一次写完再验。
- tasks/design 里提到"参考 XXX 实现"的,实现前先完整读它——设计文档点名参考而实现时没看,是返工最常见的来源。
- 冲突先分类再处理:
| 分类 | 处理 |
|---|---|
| 规格不准(遗漏、边界未覆盖、验收口径缺失) | 暂停,修正 OpenSpec,再继续 |
| 代码偏离(实现没按规格做) | 修正代码,不改 OpenSpec |
| 不确定、或涉及设计方向 | 暂停,问用户 |
- 遇到 bug 或行为不明 → 用
diagnose;需要测试驱动 → 用tdd。两者都不强制。
退出:tasks 完成或剩余已明确记录;已运行验证,或记录了未验证的原因;已知限制已列出。
Build 结束自动进入 Close,中间不设确认点。
Close — 收好尾
进入:实现达到可交接状态。
动作:
- 补齐
decision.md——把## Decision校准为实际发布的做法(现在时),补## Consequences和## Verification。 - 执行收尾检查(见下)。
- 检查澄清阶段的新术语已沉淀进
devflow/glossary/CONTEXT.md(写入时机在术语确认的当场,见「冲突裁决」;这里只查漏)。 - 有被否决的提案 → 确认已在否决发生时写入
devflow/rejected/<class>/(这里只兜底检查)。
退出:decision.md 五节齐全;收尾检查通过;用户已被询问是否归档。
核心规则
| 规则 | 说明 |
|---|---|
必须写 decision.md |
每个非平凡变更 |
## Alternatives considered 强制 |
没有记录"打败了什么"的决策会招来重新争论 |
| 备选是记录的,不是编造的 | 当时没记录就写 <!-- alternatives-not-recorded --> |
| 实现前必须有授权 | 确认点 1 |
| 冲突先分类再处理 | 三分法 |
| 一个事实只有一个权威 | 见下表 |
"非平凡"的判据:改变行为、架构、跨文件或跨包的契约、流程或工具、测试策略、磁盘/线缆/配置格式,或其他维护者可能重新考察的决策。
豁免:纯机械或局部编辑。小修不值得触发本流程——dev-flow 是显式触发的 opt-in 流程,轻的第一道防线是不进来;后来发现重要时,允许事后补录 decision.md(retrofit 已验证)。
事实的唯一权威
| 内容 | 权威 |
|---|---|
| 为什么这么做、放弃了什么、代价 | decision.md |
| 实现设计、接口影响 | design.md |
| 可观察行为、验收场景 | specs/ |
| 执行切片与完成状态 | tasks.md |
| 跨项目术语 | devflow/glossary/CONTEXT.md |
| 未实施的提案 | devflow/rejected/ |
任何一句话只能在一个地方是权威。出现第二处,就是错——两边会各自腐化,而且没人知道以哪边为准。
为什么这么轻
这三条是有意为之。不要"补全"它们。
- 义务挂在已有动作上。 写规划产物的时刻,就是写
decision.md的时刻。不设独立的归档阶段,不做事后提取。 - 确认点只有两个。 进入实现、是否归档。不新增。
- "是否该写"是判断。 规则 + 收尾看一眼。机器只校验已经写下的东西是否合规,不用脚本强制这件事本身。
收尾检查
openspec/changes/<slug>/decision.md存在- 含
## Alternatives considered,或显式的<!-- alternatives-not-recorded --> ## Verification里没有"已确认 X 存在"这类不可重跑的结论
纯机械改动可以豁免。豁免必须显式,但可以廉价——目的是让"跳过"变成有意识的选择,而不是静默遗忘。
已落地:
scripts/check-dev-flow.sh <slug>执行上述三项检查;--index扫描归档派生devflow/index.md(不手写);--all检查全部活跃 change。
目录
openspec/changes/<slug>/
├── proposal.md design.md specs/ tasks.md
└── decision.md ← 人向:为什么、放弃了什么、代价
↓ 随归档一起冻存
devflow/
├── glossary/CONTEXT.md ← 跨项目术语
├── rejected/<class>/ ← 未实施的提案(否决发生时写入,不是 Close 时)
└── index.md ← scripts/check-dev-flow.sh --index 派生,不手写
rejected/ 的保留条件:它仍能防止一个诱人的、有意义的错误。否则整体删除。
边界:decision.md 的 Alternatives 记"变更内"的方案备选;rejected/ 收"整个提案被否决"的方向。提案在确认点 1 被整个否决时,当场写入 rejected/——那时流程已终止,Close 不会执行。
触发规则
- 用户输入
/dev-flow。 - 用户明确要求走开发流程。
不要因为任务涉及 OpenSpec、跨模块、架构决策或 devflow 目录就自动触发。
未被这样要求之前,按普通工程任务处理。
decision.md 义务只覆盖经本流程的非平凡变更——不经 dev-flow 的普通工程任务没有这个义务。
相关文档
references/phases.md— 三阶段契约与判断细则references/decision-note.md—decision.md格式规范、校准案例、反模式skill-workbench/docs/dev-flow/background-and-evolution.md— 背景、技术演进、设计取舍