--- name: writing-plans description: 当你有规格说明或需求用于多步骤任务时使用,在动手写代码之前 version: "1.0.0" license: MIT metadata: hermes: tags: [planning, documentation] --- # 编写计划 ## 概述 为一位没见过这个代码库、也没见过这份规格的工程师编写实现计划。假设他们一旦知道确切的接口和确切的测试,就能用项目所用的语言写出地道的代码;也假设在计划留出选择余地的地方,他们会做出合理的选择。他们无从知道的,是你已经决定了什么:哪些文件、哪些名字和签名、规格里的哪些取值、哪些测试证明每个任务。把这些记下来。将整个计划拆成小步骤任务。DRY。YAGNI。TDD。频繁 commit。 **开始时宣布:** "我正在使用 writing-plans 技能创建实现计划。" **上下文:** 此技能应在专用 worktree 中运行(由 brainstorming 技能创建)。 **计划保存位置:** `docs/superpowers/plans/YYYY-MM-DD-.md` - (用户对计划位置的偏好优先于此默认值) ## 范围检查 如果规格涵盖了多个独立子系统,它应该在头脑风暴阶段就被拆分为子项目规格。如果没有,建议将其拆分为独立的计划——每个子系统一个。每个计划应该能独立产出可工作、可测试的软件。 ## 文件结构 在定义任务之前,先列出将要创建或修改的文件以及每个文件的职责。这是锁定分解决策的地方。 - 设计边界清晰、接口定义良好的单元。每个文件应有一个明确的职责。 - 你对能一次放入上下文的代码推理得最好,文件越专注你的编辑越可靠。优先选择小而专注的文件,而非承担过多功能的大文件。 - 一起变更的文件应放在一起。按职责拆分,而非按技术层级拆分。 - 在现有代码库中,遵循已有模式。如果代码库使用大文件,不要单方面重构——但如果你正在修改的文件已经变得难以管理,在计划中包含拆分是合理的。 此结构决定了任务分解。每个任务应产出独立的、有意义的变更。 ## 任务粒度定界 一个任务是**能独立承载自己那一轮测试循环、且值得一个全新审查者把关**的最小单元。划任务边界时:把搭建、配置、脚手架和文档这些步骤,折进那个真正需要它们的交付物所在的任务里;只在「审查者有可能否掉这个任务、同时批准它旁边那个」的地方才拆开。每个任务都以一个**可独立测试的交付物**结束。 ## 步骤粒度 **每步是一个操作,并有一个可检查的结果:** - "编写失败的测试" - 一步 - "运行它确认失败" - 一步 - "实现最少代码让测试通过" - 一步 - "运行测试确认通过" - 一步 - "Commit" - 一步 ## 计划文档头部 **每个计划必须以此头部开始:** ```markdown # [功能名称] 实现计划 > **面向 AI 代理的工作者:** 必需子技能:使用 subagent-driven-development(推荐)或 executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。 **目标:** [一句话描述要构建什么] **架构:** [2-3 句话描述方案] **技术栈:** [关键技术/库] **规格:** [本计划所实现的规格 / 设计文档路径 —— 计划的论证依据来自规格,所以规格要跟着计划一起走;执行者两份都读] ## 全局约束 [来自规格的项目级要求 —— 版本下限、依赖限制、命名与文案规则、平台要求 —— 每条一行,数值从规格里逐字照抄。每个任务的要求都隐含包含本节。] ## 审查重点(Review Focus) [规格隐含了、但没有任何任务的测试覆盖到的输入类别或失败模式中,最可能伤到这个软件使用者的五个 —— 每条一行,写明那个输入或条件,以及一个正常人会期待的行为,最可能的排最前。规格是一份愿景文档:它说软件必须做什么,而不是软件会遇到的一切;规格对某种输入没有提及,不等于允许这种输入把程序弄坏。趁规格就在眼前,在这里一次写完这份清单。然后针对每一行,把钉住它的那个测试,按那个任务自己的步骤写法,加进负责那段代码的任务里。] --- ``` ## 任务结构 ````markdown ### 任务 N:[组件名称] **文件:** - 创建:`exact/path/to/file.py` - 修改:`exact/path/to/existing.py:123-145` - 测试:`tests/exact/path/to/test.py` - [ ] **步骤 1:编写失败的测试** ```python def test_specific_behavior(): result = function(input) assert result == expected ``` - [ ] **步骤 2:运行测试验证失败** 运行:`pytest tests/path/test.py::test_name -v` 预期:FAIL,报错 "function not defined" - [ ] **步骤 3:在 `exact/path/to/file.py` 中实现 `function(input: InputType) -> ResultType`** 当签名和测试还留有选择余地时(用哪个库调用、哪种数据结构),用一行说明做法;只有签名和测试决定不了的算法,才给代码块。 - [ ] **步骤 4:运行测试验证通过** 运行:`pytest tests/path/test.py::test_name -v` 预期:PASS - [ ] **步骤 5:Commit** ```bash git add tests/path/test.py src/path/file.py git commit -m "feat: add specific feature" ``` ```` ## 一个步骤包含什么 当实现者能根据一个步骤**恰好写出一种合理的东西**时,这个步骤就写完了。要求就这一条:无歧义,而不是完整。每一类步骤只带上让它无歧义的内容,不多带: - **测试步骤:** 测试的名字和它的断言,以代码形式给出,里面是规格的确切取值。 - **代码步骤:** 确切的签名(名字、参数、返回类型)、它所在的文件,以及规格钉死的具体取值。函数体由实现者来写。只有签名和测试决定不了的算法,或规格定死的确切文案,才写出函数体。 - **验证步骤:** 要运行的命令,以及代表通过的输出。 - **引用另一个任务:** 那个任务的 Interfaces 块说明要用什么;计划不重复那个任务的代码。 计划是实现者无法独自作出的那些决定的集合。一份比它所描述的代码还长的计划,其实是把代码写了一遍。什么都没决定的行("待定"、"处理边界情况"、"添加适当的验证"、"为上述代码编写测试"、没有任何任务定义的类型或函数)是反方向的失败,自检会把这两种都抓出来。 ## 自检 编写完整计划后,以全新视角审视规格并对照检查计划。这是你自己执行的检查清单——不是子代理调度。 **1. 规格覆盖度:** 浏览规格中的每个章节/需求。你能指出实现它的任务吗?列出所有遗漏。 **2. 步骤扫描:** 每个步骤都必须让实现者恰好写出一种合理的东西,而且任何步骤都不能多带:什么都没决定的行是缺口,签名和测试已经决定了的函数体是照抄。两种都要修。 **3. 类型一致性:** 后续任务中使用的类型、方法签名和属性名是否与前面任务中定义的一致?任务 3 中叫 `clearLayers()` 但任务 7 中叫 `clearFullLayers()` 就是 bug。 **4. 审查重点:** 对规格隐含的每个输入类别或失败模式,有没有哪个任务的测试覆盖到它?没被覆盖、最可能伤到使用者的五个写进「审查重点」一节,那里的每一行都要把对应的测试加进负责的任务里。这一节为空,意味着你检查过、没发现,而不是你跳过了检查。 **5. 比例:** 拿计划的长度和规格比一比。一份比它所实现的规格长好几倍的计划,是程序的抄本,不是计划。如果代码块占了文档的大半,就把函数体换成签名、测试名和断言,然后检查每个步骤是否仍然无歧义。 如果发现问题,直接内联修复。无需重新审查——修好继续推进。如果发现规格中的需求没有对应任务,就添加任务。 ## 执行交接 保存并自检完计划之后,把计划链接给你的人类伙伴阅读。如果他们已经明确给出了执行方式,就请他们审阅计划、确认它抓住了他们想要的东西;等他们审阅完再开始实现,然后沿用那个既定的方式。否则,请他们审阅计划,并在实现之前选定一种执行方式。 **尚未给出执行方式时:** **"计划已完成并保存到 `docs/superpowers/plans/.md`。请审阅这份计划。你希望用哪种执行方式?** - **子代理驱动** - 每个任务由一个全新子代理实现,并在下一个任务开始前由一个全新审查者检查,最后再做一次覆盖整个分支的审查。最彻底;代价是每个任务、每次审查各一个全新上下文。 - **原生执行** - 我在当前会话里按这个运行环境的方式亲自实现每个任务,最后由一个跑在最强模型上的全新审查者检查整个分支。最便宜、最快;直到最后才有独立审查。会话用中档模型就能跑好,因为计划已经承载了设计。 **针对这份计划,我推荐<两者之一>,因为<从计划里得出的一句话:任务之间对彼此接口的依赖有多深、有多少个任务、一个错误上线的代价是什么>。这份计划抓住你想要的东西了吗?我们用哪种方式?"** **已经给出执行方式时:** **"计划已完成并保存到 `docs/superpowers/plans/.md`。请审阅这份计划。它抓住你想要的东西了吗?"** **如果选择子代理驱动:** - **必需子技能:** 使用 subagent-driven-development **如果选择原生执行:** - **必需子技能:** 使用 executing-plans