write-dev-plan:计划不在脑子里,在文件里
这是技能深挖系列的一篇。母篇《从能跑到可交付:一套前端 AI 协作系统的设计笔记
》讲了整套系统的形状,前面两个系列拆了五层约束和五个阶段,这个系列逐份拆技能——每份技能为什么存在、具体怎么干。write-dev-plan 是澄清链上的第五步,排在 write-tech-design 之后:需求和技术方案都已经定了,这一步把方案拆成一份能执行的任务清单。我用的系统是开源的 frontend-harness-cli
,文中规则都能在仓库的技能文件里找到原文。
第三天,没人说得清做到哪了
先看一个具体的场景。
你让 AI 做一个功能,它说"没问题",然后开始干。第一天它说在搭数据层,第二天它说在写页面,第三天你问它:做到哪一步了?还剩什么?
它给你一段很像样的回答:数据层基本完成,页面完成大半,还剩联调和几个边界情况。你接着问:什么叫"基本完成"?它列了三点。你问:你昨天说一共五步,第四步做完的标准是什么?它又给你一段解释——和你前天听到的版本,不太一样。
问题不在它记性差。问题在这份计划从来没有离开过它的上下文。计划活在聊天里,就意味着:没有一份东西能被拿出来对账,“做完了"没有一个固定的判定标准,每次问它,它都是在当场重新生成一个说法。生成出来的说法每次都通顺,但通顺不等于一致。
write-dev-plan 这份技能管的就是这一件事:在写第一行代码之前,把计划落成一份文件,拆到每一步都小得能单独验证。
这份技能是什么:把设计拆成任务清单
它的位置在澄清链的中间。前面几步的产出是:一份带编号的需求(prd.md)、一份质量愿景(vision.md)、一份技术方案(tech-design.md)。这三份文件回答的是"做什么"和"用什么办法做”。
write-dev-plan 回答剩下的问题:按什么顺序、切成多细去做。
它的产出有两份:tasks.json 和 plan.md。前者给内核读,后者给人读——也给范围守卫读。下面细说。
它自己开篇的第一句把标准定死了,值得原样引用:
把技术方案拆成开发者能逐个完成、测试、提交的小任务,按依赖排序。需要开发者「自己理解」的任务,就是评估者会判失败的任务。
注意最后一句。它把"任务写得含糊"的代价提前说清了:含糊不会消失,只会往后传——传到实现阶段变成开发者自由发挥,传到评测阶段变成判失败。拆计划这一步,就是把所有"需要理解"的地方提前消灭掉。
它具体怎么干:两份文件,两种读者
动手之前有门禁:prd.md、vision.md、tech-design.md 三份文件必须都存在,且摘要已经过确认。缺一份,这一步不许开始——计划不能建在还没定的方案上。
tasks.json:给内核读的清单
[
{
"id": "TASK-1",
"title": "Notice list model: sort newest first, unread count",
"requirements": ["REQ-1"],
"checks": ["node --test test/notice-model.test.js"]
}
]
每条任务只有四个字段,但每个字段都有讲究:
id唯一且稳定。之后重新规划(replan)只许往后追加新 id,不许重新编号——因为别的文件已经在引用这些 id 了,改号等于把引用全部打断。requirements列出这条任务推进了哪些需求,写的是 PRD 里的REQ-n编号。这条线是后面能对账的根,下一节细说。checks是 shell 命令。规则是:这些命令必须全部以退出码 0 结束,harness task-done才接受这条任务算完成。还有一条细节:用这条任务自己的测试,不要跑全量测试套件——除非这条任务本身就是集成步骤。跑全量又慢又说不清是哪条任务的账。
plan.md:给人读,也给范围守卫读
同一个任务,在 plan.md 里要写全四样东西:
### TASK-1: Notice list model
**目标** —— 本任务交付什么,一句话。
**文件**
- Create: `src/notices/model.ts`
- Modify: `src/notices/index.ts`
- Tests: `src/notices/model.test.ts`
**契约**
- `sortNotices(list: Notice[]): Notice[]` —— 按 `createdAt` 最新在前;相同时保持稳定。
- `unreadCount(list: Notice[]): number`
**验证**
- 单元:`npx vitest run src/notices/model.test.ts` —— 预期 6 个通过
- API / 集成:N/A —— 纯模块
目标一句话、文件给精确路径、契约给精确签名连排序的平局规则都写明、验证给精确命令和预期结果(“预期 6 个通过”,不是"跑一下测试")。
plan.md 的末尾还有一节,是写给机器看的:
## Editable scope
- `src/notices/**`
- `test/notices/**`
这一节由范围守卫(harness scope)读取:实现阶段只许改这些路径之内的文件。计划写到哪,权限就给到哪——计划不只是说明书,还是授权书。
写法的总规则只有一条,技能里写得很硬:精确路径、精确签名、精确命令和预期结果。不写 TODO、TBD、「按需」、「等等」。 这几个词在人的计划里是偷懒,在给 AI 的计划里是入口——它会在这些词下面自由发挥,而且发挥的方向没人能预先知道。
设计一:任务小到一次提交
任务的粒度标准,技能给了一个具体数字:每个任务小到一次提交能装下,大致不超过 300 行改动,并且能单独验证。
为什么卡这个尺寸?往小了拆有三个实际收益。
第一,验证能跟着任务走。一条任务自带 checks,实现阶段做完就跑,绿了才许提交——这是实现阶段 那篇讲的任务循环。任务要是大到横跨五六个文件、两种性质的改动,它的 checks 就没法只测"它自己的事",验证又退回"最后一起跑一遍"。
第二,失败时重做的范围小。评测打回来一条任务,推翻重做的就是这一次提交的量;打回来一个两千行的大任务,等于整个功能重来。
第三,顺序可以由依赖决定,而不是由谁先想到决定。技能要求按依赖排序:数据和契约在前,UI 在后,集成最后;一条任务绝不依赖排在它后面的任务。任务足够小,依赖关系才画得出来;任务一大,内部就藏着没写出来的子顺序,执行到一半才发现"得先有那个东西",计划就在执行现场被改写了。
还有一条容易漏的:加载、空、错误、无权限这几种状态,要写成任务(或任务的一部分),不许当事后补的东西。 这几个状态是 AI 最容易跳过的——它默认写主线畅通的那条路。写进任务清单,它们就有了 id、有了 checks,跳过它们等于没做完一条任务,会被对账查出来;不写进清单,它们就永远排在"有空再补"里。
设计二:计划是文件,不是聊天
为什么非要落成两份文件,不能让规划者在对话里说一遍计划、开发者接着干?
因为这套系统的每个阶段都是全新上下文。实现阶段的开发者没有参加过澄清阶段的对话,它能拿到的只有文件。计划留在聊天里,等于计划在进入实现阶段的那一刻就丢了——开发者只能凭任务标题猜,或者干脆自己重新规划一遍,那就不是同一份计划了。
落成文件之后,计划还多了一个身份:它是内核能拿着对账的凭据。
- 任务算不算完成,内核拿
tasks.json里的 checks 去验,不听执行者自述; - 改动有没有越界,范围守卫拿
plan.md末尾的 Editable scope 去比对; - 需求有没有被漏掉,拿 PRD 的
REQ-n列表和所有任务的requirements字段做覆盖检查。
这三样检查,每一样都需要计划以结构化、稳定的形态存在。聊天记录做不了这三件事:它没有字段,没有稳定的 id,也没法被一个命令读进来逐项核对。
技能里还有一条配套的纪律:任务细节只写在计划里,绝不写进 .harness/overview/。 overview 是项目的长期总览,放的是稳定的产品与技术事实;任务清单是这一个功能的临时账本,功能做完就归档。把临时细节塞进长期文件,总览会越积越脏,后面每个功能读到的"项目现状"都掺着上一个功能的施工记录。
设计三:粒度决定评测能不能对上账
最后说这份技能和评测的关系——这也是"为什么要拆这么细"最硬的理由。
评测阶段的产出是按需求编号说话的:REQ-3 没做对,证据在这。修复的时候问题来了:REQ-3 对应的是哪次改动?
任务粒度对得上时,这笔账是现成的。每条任务在 tasks.json 里写明了自己推进哪些 REQ-n,每次提交只对应一条任务。评测说 REQ-3 有问题,顺着 requirements 字段就能找到是哪几条任务、哪几次提交,修复的范围当场圈定。任务粒度一大——一条任务挂六条需求、一次提交两千行——评测的发现就落不到具体的改动上,修复退化成大海捞针。这一点实现阶段那篇从提交的角度讲过:逐任务提交是可对账的证据单元。而证据单元的大小,就是在这一步定下来的。
这份技能的完成标准也是围着对账定的,一共两条硬线:
- PRD 里每个
REQ-n,至少出现在一条任务的requirements里。 有一条需求没任务接,它就不会被实现,也不会在任何地方报错——只会在验收时以"缺功能"的形态出现。这条检查在计划写完时就能跑,不用等到最后。 tasks.json和plan.md的 id 与标题必须一致。 两份文件是同一份计划的两种写法,一份给内核、一份给人;对不上,就说明其中一份已经过期了,后面所有对账都建立在错位上。
它管不了什么
边界照例说清。
write-dev-plan 保证的是计划可执行、可对账:任务够小、顺序合法、每条需求有人接、每条任务有明确的完成判定。它不保证计划是对的——技术方案本身选错了路线,拆得再细,也只是把错误的路线执行得很整齐。那是上一步 write-tech-design 和澄清阶段需求质量的问题,不归这份技能管。
它也不负责给任务定验收标准。每条任务的 checks 回答的是"这条任务做完了没",回答不了"这个功能做得对不对、够不够好"。后者是下一步的事:write-acceptance-criteria 把 PRD 和质量愿景变成评测阶段用的验收标准与评分表——下一篇拆它。