write-prd:把需求写成能逐条对账的合同
这是逐份拆 skill 系列的一篇,拆的是 write-prd——澄清著述链的第二份,排在 clarify-requirements 之后。母篇《从能跑到可交付:一套前端 AI 协作系统的设计笔记
》第三节拿这份 skill 当标本解剖过整体设计;这个系列逐份拆开,每篇只讲一份:它为什么存在,具体怎么干。我用的系统是开源的 frontend-harness-cli
,文中规则都能在仓库的 templates/skills/write-prd/SKILL.md 里找到原文。
一份挑不出毛病的 PRD,下游全线卡住
先看一个具体的场景。
AI 交来一份需求文档:背景讲得清楚,目标写得恳切,功能描述一段一段很流畅,还配了用户故事。你从头读到尾,挑不出毛病,点头,开工。
第三天,开发问:导出功能要导筛选后的结果,还是全部数据?文档里写的是"支持数据导出"——导什么,没说。再过几天到评测环节,要逐条核对"需求实现了没有",这时发现文档里没有"条",只有"段":一整段话里混着三个意思,做到了两个半,算过还是不过,没人判得了。最后验收,用户说"我要的不是这个",开发把文档翻出来自证清白——文档确实站在他那边,因为这份文档从头到尾什么都没承诺过。
什么都没承诺的文档,谁也驳不倒,也谁也保护不了。
这份文档的问题不在文笔,在形态:散文不能对账。 需求的下游不是读者,是机器和一道道门禁:评分表要拿它当分母,评测报告要逐条引它,覆盖检查要数它。数不了、引不了的东西,写得再顺也只是读物。write-prd 这份 skill 存在的理由,就是把需求从读物变成合同。
这份 skill 干什么:把确认稿编成合同
著述链的第一份 clarify-requirements 负责访谈:把用户问到开放问题清零,产出一份用户确认过的澄清摘要(clarification/summary.md)。那份摘要是对话的结论,是给人看的。
write-prd 接手的是下一步:把确认稿编成 .harness/tasks/<feature>/prd.md——skill 原文的说法是"产品契约"(product contract):问题是什么、谁有这个问题、功能必须做什么、明确不做什么,全部"用测试者能核对的话写"。
为什么这一步要单独成一份 skill,不能让访谈顺手写了?因为两种文本的消费者不同。确认稿的消费者是用户本人,标准是"你看对不对";prd.md 的消费者是后面的每一份产物和每一个角色:质量愿景 vision.md 引用它、技术方案对着它设计、评分表 rubric.json 直接把它的需求编号抄走当检查项。一个给人确认,一个给机器消费,写错的代价完全不同,所以分工:访谈归访谈,立约归立约。
先过门,再动笔
这份 skill 的第一部分不是"怎么写",是 Gate——动笔之前先核查三个事实:
clarification/summary.md存在;- 里面有
confirmed-by-user: yes; open-questions: 0。
差任何一样,停下,退回 clarify-requirements。不许"先写着,确认回头补"。道理很直白:如果用户还没确认,或者还有没问清的问题,你写下去的每一个字都是替用户做的决定,而替用户做的决定写得越完整,返工越贵。
过了这道门,还要读项目概览(.harness/overview/product.md 和 technical.md),核对这个功能的范围和已有的能力、边界冲不冲突。一个已经有导出能力的系统,新 PRD 又从零规划一套导出,就是在概览这一步该被发现的事。
这就是这类 skill 的通用设计:前置条件是能逐项核查的文件事实,不是模型"觉得差不多了"。 母篇把这份 skill 的五块结构(描述即接口、Gate、产物契约、模式检查、Rules 与 Done Criteria)解剖过,这里不重复,只接着往下讲它具体怎么干。
九节:prd.md 的固定骨架
prd.md 的结构是写死的九节,顺序都不能换:
- 问题——谁的问题,用他们自己的说法,为什么是现在。
- 用户与场景——有哪些角色,他们在什么情境下用。
- 用户故事——“作为……我想要……以便……"。
- 需求——编号
REQ-1、REQ-2……每条一句可测的话。 - 非功能需求——必须可度量:响应时间、体量、浏览器范围、无障碍等级。
- 成功画像(what good looks like)——每个关键页面、每个核心交互,用 2–3 句写清用户认可的目标状态。
- 范围外——明确不做的事。
- 成功指标——凭什么说它成功了。
- 概览增量——这个功能带来的新能力域或边界变化,一条一行,先列在这里,不许直接去改项目概览,后面由
sync-overview统一吸收。
九节里有三节值得单独说。
需求节是整份文件的枢纽。 编号、一句一条、可测——这三点下一节细讲。这里只强调 skill 写死的一句话:“These ids become rubric.json requirements verbatim."(这些 id 原样成为评分表里的 requirements。)也就是说需求编号不是写给读者看的序号,它写下去的那一刻,就已经是评测的考题清单了。
非功能需求必须可度量。 “性能要好"在这一节里等于没写:多快算好?在什么体量下?支持到哪个浏览器?写不出数字的那一栏,就是留给上线后扯皮的。
第六节"成功画像"是全篇最容易写废的一节,skill 专门给了一组对照:
- 差:“列表页要简洁现代。”
- 好:“列表一行一条通知,最新在上;未读行标题加粗并带圆点;行间距 12–16px;空列表显示一句话和一个『刷新』按钮。”
好例的标准是原文那句:具体到"开发者和评估者脑中会浮现同一个画面”。感觉词的问题不是不好听,是两个人脑中的画面不一样——开发觉得做到了,评估觉得没做到,验收现场吵的其实是这一节当初没写清。注意这一节只是摘要,完整的画面锚点在下一篇 write-quality-vision 产出的 vision.md 里,两边必须一致。
编号不是排版,是对账的键
为什么非要编号?把一条需求的一生看完就懂了。
REQ-3 在 prd.md 里出生;在澄清阶段末尾,它被抄进 rubric.json,变成一条带检查方式的评测项;到了评测,报告里有一行证据挂在它名下——通过或不通过,证据是什么;覆盖门禁数分母时,它是分母里的一个。整条链能对上账,前提是链的每一环都用同一个键引用同一条需求。
散文段落没有键。“第三节第二段那句"不是引用,是猜谜:改一次文档,段落位置变了,引用全灭。编号的意义就是给每条需求一个稳定地址,让"这条验了没"从一个需要通读全文的问题,变成一次查表。
也正因为是地址,编号一旦发布就不能动——这直接决定了下一个话题:导入模式为什么那么克制。
导入的 PRD:只补缺号,不动一个字
v0.9 起,这套系统支持用户自带写好的 PRD(harness new <feature> --prd <file> 导入)。这时 state.json 里标着 prd.source: "imported",write-prd 先做模式检查(Mode check),切到仅规范化模式:不另写一份,用户的文档就是契约。它只做三件事:
- 核对九节在不在,或者是不是有意缺失。缺的节不许自己补写——变成一个缺口问题,经
clarify-requirements问回用户。 - 补编号:导入报告说编号有缺口的,就在原文里原地补上
REQ-n,保留原句措辞。已有的编号永不重编,需求句子永不改词。 - 核对成功画像的素材:品质目标写得具体的,原样不动;只有"干净现代"这类感觉词的,记成缺口问题——原文说得很清楚,那"是缺口问题,不是动笔改写的邀请”。
完成判据:编号补全到 REQ-1..n 无缺号,并且每一个缺口要么由用户补上、要么挂在开放问题清单上。然后照常进下一篇 write-quality-vision。
为什么克制到近乎洁癖?两个原因。一是上一节说的:编号是全链的对账地址,你在源头重编一遍号,下游所有引用连环错位,而且错得无声无息。二是更根本的:这份文档是用户签过字的合同。你润色一句、合并一条,语义漂移的那部分就不再是用户的决定,而是你的——但它还顶着用户的名义往下走。机器代笔最危险的不是写错,是把自己的意思写成别人的决定。仅规范化模式卡的就是这条线:结构可以整理,意思只能来自用户;拿不准的,变成问题递回去。
为什么禁止顺手补一条需求
这份 skill 的 Rules 一节,核心是一组禁令,条条冲着同一个行为:作者在立约时夹带私货。
- 每条需求必须能追溯到确认摘要(Done Criteria 原话:Every requirement traces to the summary)。摘要里没有的,不许出现在 PRD 里。
- 沿用用户在
answers.md里的用词,不许改名。 用户管它叫"工单”,你觉得"任务单"更专业,改了——下游所有产物跟着用新名,用户验收时对不上号。改名看着是小事,本质是换了一个概念,还换得没人察觉。 - 不许写 TODO / TBD。 原文的说法是:“还有未决问题,说明你还不该进入本 skill。“含糊不是留待补充,是 Gate 没过硬闯进来。
- 每条需求必须能被没写它的人核对。 只有作者自己懂的句子,评测时必然变成自由裁量。
- 一个 PRD 只写一个功能;不写实现细节。 框架、文件名是技术方案(
write-tech-design)的地盘,写进 PRD,等于在用户确认需求时偷偷把技术决策也签了。 - 不许直接写
.harness/overview/。 变化先列在第九节当增量,交付后统一吸收,避免每个功能随手改全局概览。
为什么发明一条需求这么严重?因为编号会给它平权。一条凭空补出的需求拿到 REQ-n 之后,和用户亲口要的需求长得一模一样:会被排进计划、被实现、被评测、被交付,用户最终要为自己从没要过的东西付出开发时间和验收精力,事后还很难说清是哪一步多出来的。反过来,真实需求漏了,评测报告至少会在覆盖门禁那里暴露缺口。发明比遗漏更坏,所以这份 skill 给缺口只留了一个合法去向:变成问题,回到 clarify-requirements,绝不许变成内容。
它管不了什么
边界说清。
第一,prd.md 只回答"做什么、不做什么”,不回答"好看到什么程度"和"怎么做”。第六节的成功画像是摘要级的,完整的质量锚点是下一篇的事:write-quality-vision
把用户认可的画面写成 vision.md,供实现和评测逐项比对;实现路径则归再后面的 write-tech-design。
第二,它救不了澄清问错。用户在一个方向就偏了的摘要上签了字,write-prd 只会把这个错误编得结构齐整、编号连续——合同写得越规范,错误反而越难被质疑。所以这份 skill 的 Gate 只认 confirmed-by-user: yes 这个事实,不认模型对内容的信心;真正的防线在上一份 skill 有没有把问题问透。那是另一篇的事了。