| name | product-feature-tech-implement |
| description | 基于功能设计文档与现有源码,端到端实现一个产品功能:开发、代码 review、测试用例编写与执行, 循环迭代直到测试通过,最终产出用户手册与实现总结文档(保存到当前项目 markdown/ 目录)。 触发条件:用户提供了功能设计文档(PRD/设计文档/需求描述)+ 源码目录(或代码库路径), 并希望"实现这个功能"、"开发这个功能"、"把这个设计文档变成代码"、"做这个功能的开发+测试"、 "完整实现并测试"、"端到端开发",或明确要求按"开发-review-测试"循环推进直到测试通过。 也适用于需要额外运行环境的开发任务,例如 PostgreSQL/MySQL 等数据库插件开发 (可能需要编译、搭建实例、配置参数、初始化、跑测试)、需要启动服务进程才能测试的后端功能、 需要特定运行时环境的库或框架功能。 即使用户只是说"帮我把这个功能做出来"、"按设计文档实现一下"、"这个功能开发完了麻烦测试一下", 但同时给出了设计文档和代码路径,也应使用本 skill。 本 skill 的核心是严格的"开发 ⇄ Review"循环 + "测试者不预先看代码"的盲测试原则, 因此当用户提到"独立测试"、"测试不能看代码"、"先写测试用例再看代码"、"black-box 测试"时, 也应优先使用本 skill。 不适用于:只是要求"写一个脚本"、"改一行代码"、"快速修一个 bug"等轻量级、 无需走完整开发-review-测试闭环的任务;也不适用于纯粹的代码评审(无需新开发功能) 或纯粹的测试用例编写(无设计文档、无需开发)这类单一环节请求—— 这些更适合直接处理而非套用本 skill 的完整闭环流程。
|
Product Feature Tech Implement
一个把"功能设计文档 + 源码"变成"经过 review、经过测试、可交付"代码的闭环流程。
核心思想很简单:开发者和审查者反复拉锯,直到审查者满意;然后测试者在完全不知道代码长什么样的情况下,
只根据设计文档独立写出测试用例,再去跑这些测试连同已有测试;测试不过就打回开发,测试过了才算完。
这个"测试者不能偷看代码"的约束是整个 skill 里最容易被悄悄破坏、也最值得认真保护的一条规则——
原因见下面"为什么要盲测试"。
为什么要盲测试
如果测试用例是照着代码写的,那测试只能验证"代码做了它自己以为该做的事",却验证不了
"代码做了设计文档真正要求的事"。这两者的差距正是 bug 藏身的地方:开发者对需求的理解偏差、
遗漏的边界条件、想当然的默认值——这些错误一旦被同一个脑子既写进代码又写进测试,测试就会
和代码一起错,谁也发现不了。让测试者只看设计文档独立写测试,本质上是用"两个互不通气的理解"
互相校验,偏差才会暴露出来。这就是为什么测试阶段要明确写着"千万不要先看代码"——不是走流程,
是这一步存在的全部意义。
整体状态机
┌─────────────────────────────┐
│ 读取设计文档 / 源码 / 上下文 │
│ (PG 插件等场景额外读取依赖) │
└───────────────┬───────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ 循环 A:开发 ⇄ Review(直到 review 通过) │
│ │
│ developer 子代理 ──写/改代码──▶ reviewer 子代理 │
│ ▲ │ │
│ └──────review 意见 / 修复要求────┘ │
└───────────────────────┬────────────────────────────┘
│ review 通过
▼
┌──────────────────────────────────────────────────┐
│ tester 子代理(独立、不看代码) │
│ - 若本任务此前已写过测试 → 跳过编写,直接复用 │
│ - 否则:只读设计文档,独立编写新测试用例 │
│ - 跑【新测试 + 已有测试】(含必要的环境搭建) │
│ - 输出测试报告 │
└───────────────────────┬────────────────────────────┘
│
┌─────────────┴─────────────┐
│ 测试失败 │ 测试通过
▼ ▼
回到循环 A 开头 任务完成,关闭三个角色
(带着测试报告去修 bug / 生成用户手册 + 总结文档
补全未覆盖的功能)
整个外层是一个"无限循环"——没有预先设定的最大轮数,因为一个功能到底要改几轮谁也说不准。
但"无限"不等于"放任不管":每一轮必须真的取得进展(修复了具体问题、补全了具体功能点),
如果连续多轮都在原地打转(同一个问题被反复打回、测试报告和上一轮几乎一样),就不再是
"继续迭代",而是"卡住了"——这时候停下来把情况摊开给用户看,比硬着头皮空转更负责任。
具体判断标准见下面"何时该向用户求助而不是继续循环"。
开始之前:收集必要输入
进入循环前,先确认手上有这些东西,缺什么就问用户要什么(用 ask_user_input_v0 或直接提问都行,
看哪个更顺手):
- 功能设计文档:路径或内容。这是 developer 和 tester 共同的"宪法"——两边都依据它工作,
但谁也不许看对方依据它做出的产物(开发者不看测试用例,测试者不看代码)。
- 源码目录:现有代码库的路径,开发者要在这个基础上改。
- 额外运行依赖(按需):如果功能涉及需要独立运行环境才能测试的场景——最典型的是数据库插件
开发(PostgreSQL/MySQL 等),需要额外确认:
- 数据库源码/安装路径,编译方式
- 是否已有现成实例,还是需要本次搭建一套测试集群
- 必要的连接信息、权限、配置参数
- 参考
references/postgres-extension.md 了解 PG 插件场景的具体环境搭建步骤
其他场景类推:任何"代码本身之外,测试还依赖外部服务/进程/环境"的情况,都在这一步问清楚,
不要等 tester 子代理跑到一半才发现少了张图纸。
- 现有测试套件位置(如果有):tester 子代理需要知道"已有测试"在哪里,才能在新测试之外
一并跑它们,防止改动破坏了原本好用的功能。
如果用户的描述已经包含了以上信息,直接提取使用,不要为了走流程而重复发问。
任务状态文件:让循环记得自己做到哪了
子代理之间不共享记忆,每一轮都是全新开始的对话;如果不显式记录状态,"测试用例是否已经写过"
"现在是第几轮"这类信息就会在每次循环里丢失,导致 tester 把测试重写一遍(违反用户的明确要求),
或者整个流程不知道何时该喊停。
在项目目录下维护一个状态文件 markdown/.feature_implement_state.json(隐藏文件,不算交付物,
不要出现在最终汇报里),结构大致如下:
{
"feature_name": "一句话功能描述,用于文件命名",
"design_doc_path": "...",
"source_dir": "...",
"iteration": 3,
"loop_a_rounds_this_iteration": 2,
"test_suite_written": true,
"test_suite_path": "...",
"history": [
{"iteration": 1, "review_verdict": "未通过", "test_verdict": "未运行", "summary": "首版实现,review 发现 3 处问题"},
{"iteration": 2, "review_verdict": "通过", "test_verdict": "失败", "summary": "review 通过,但测试发现并发场景未处理"},
{"iteration": 3, "review_verdict": "进行中", "test_verdict": "未运行", "summary": "修复并发问题"}
]
}
每次开始新一轮(无论是循环 A 内部还是测试失败后回到循环 A)之前先读这个文件,结束时更新它。
test_suite_written 是关键字段——只要它是 true,tester 子代理这次就不再重新编写测试用例,
而是直接复用 test_suite_path 里已有的测试,把新一轮的代码拿去跑;只有在测试本身也需要因为
设计文档变更而调整时才重写,并且要明确告诉用户"测试用例本身也改了,原因是……"。
角色说明
三个角色的详细职责、输入输出格式、产出要求都写在 agents/ 目录下,进入对应阶段时去读:
agents/developer.md —— 子代理 1:开发者
agents/reviewer.md —— 子代理 2:代码审查者
agents/tester.md —— 子代理 3:测试者(进入这个阶段前,先读它里面关于"如何保证盲测试"的部分)
根据运行环境调整执行方式
"子代理"这个词在不同环境里对应不同的实际能力,下面按环境分别说明该怎么落地这个流程。
不确定自己在哪个环境,看当前可用的工具列表里有没有任务派生/子代理类工具,没有就按
"无子代理环境"处理。
有子代理能力的环境(如 Claude Code、Cowork)
这是流程设计的原生形态,按字面执行即可:
- 循环 A 内,developer 和 reviewer 各自是独立的子代理调用,每次调用把当前所需上下文
(设计文档、上一轮 review 意见、上一轮测试报告——如果有)整理后传入,子代理各自独立工作,
互不偷看对方的"私货"(developer 看不到 tester 写的测试代码细节,tester 完全看不到 developer
写的实现代码)。
- tester 子代理在被派生时,prompt 里只能包含设计文档和已有测试套件的位置,绝对不能包含
代码目录路径或代码内容,这是物理隔离,比任何"请不要看代码"的口头约束都可靠——这件事的
安全性建立在你没有把代码位置写进 tester 的 prompt 这个事实上,而不是建立在它会不会自觉
遵守约束上。
- 三个子代理任务都结束、且最终测试通过后,不需要额外动作"关闭"它们——子代理本身是按任务
生命周期存在的,任务完成即结束。
无子代理能力的环境(如 Claude.ai 网页/客户端)
这种环境下,"派生子代理"实际上是你自己依次扮演三个角色,在同一个对话上下文里完成。
这时"测试者不看代码"的物理隔离没有了——你作为同一个 Claude,扮演 developer 时已经读过代码,
扮演 tester 时没法真的"忘记"读过的内容。诚实面对这个限制,用下面的办法尽量减少污染,
而不是假装隔离仍然存在:
- 先写测试,再细看代码:进入 tester 阶段时,即使你脑子里有代码的模糊印象,也要先只
重新读一遍设计文档,把测试用例写下来落盘,写完之后才去翻代码细节执行测试。这样测试用例
的设计依据是有记录可查的(设计文档本身),不是凭着刚才读代码时的印象反推的。
- 在测试用例文件里写明依据:每个测试用例标注一句"对应设计文档第几节/哪个需求点",
这样事后review的人也能看出测试是不是真的独立于实现的具体写法。
- 角色切换时显式声明:在回复里清楚说明"现在切换到 reviewer 角色,只依据设计文档和
增量代码","现在切换到 tester 角色,重新对照设计文档而非刚才看到的实现细节来设计用例"——
这不是给用户看的表演,是帮你自己在长对话里真正切换思维框架,效果是实打实的。
- 如果用户对"测试必须真正独立"这一点要求很高(比如这是个安全敏感场景),可以直接告诉用户
当前环境做不到物理隔离,建议的替代方案是:你先生成测试用例文件,用户开一个新对话/新会话
只把设计文档喂给一个全新的 Claude 实例去写测试,再拿回来给你跑——这是更可靠但更费事的做法,
由用户决定是否需要。
无论哪种环境,"循环 A 直到 review 通过""测试失败打回循环 A"这套状态机逻辑本身不变,
变的只是 developer/reviewer/tester 之间的隔离强度。
何时该向用户求助而不是继续循环
下面这些情况出现时,暂停循环,把情况清楚地告诉用户,而不是埋头继续转:
- 同一个问题被打回两次以上仍未解决:说明这可能不是实现细节问题,而是设计文档本身有歧义
或矛盾,需要用户澄清意图,开发者和审查者再怎么拉锯也解不开一个本身说不清楚的需求。
- 测试环境搭建本身失败(比如 PG 实例起不来、依赖装不上)且尝试了合理的排查后依然卡住:
这通常是环境/权限问题,不是代码问题,继续循环不会有帮助。
- 测试报告和上一轮几乎一样(同样的用例、同样的失败点),说明上一轮的"修复"没有真正起作用,
值得停下来看看是不是 developer 子代理误解了 review 意见或测试报告。
- 设计文档里的某个需求点在现有代码架构下几乎无法实现,或者实现代价(比如要推翻现有架构)
明显超出了"实现一个功能"的合理范围:这是架构级决策,应该让用户知道并做选择,不该由循环自己
decide。
求助时把当前状态文件里的 history 摘要给用户看一下,让用户能快速看懂"已经试了什么、卡在哪",
而不是从头解释一遍。
任务完成后:用户手册 + 总结文档
测试通过、循环结束后,再做两件事,都是 Markdown 格式,保存到当前项目的 markdown/ 目录:
用户手册(文件名建议 markdown/{功能名}_用户手册.md):写给"会用这个功能但不关心实现细节"
的人看——这个功能是什么、怎么用、有哪些参数/选项、典型使用场景举例、常见问题。不要把实现细节
或开发过程写进去,那是下一份文档的内容。
实现总结文档(文件名建议 markdown/{功能名}_实现总结.md):写给"想知道这个功能是怎么做出来的"
的人看——做了哪些代码改动(文件、模块层级即可,不需要逐行解释)、过程中 review 发现并修复了
哪些问题(这是有价值的信息,说明代码质量是怎么被把关的,不要省略)、测试覆盖了哪些场景、
测试过程中发现并解决了什么问题、最终经过了几轮迭代。可以直接基于状态文件里的 history 整理,
这正是它存在的目的之一。
写完这两份文档后,可以视情况删除或保留状态文件 .feature_implement_state.json——如果用户后续
可能基于同一个状态继续迭代这个功能(比如又来了新需求),保留它会让"循环记得做到哪了"这件事
对未来的请求依然有效;如果这是个一次性任务,清理掉也无妨。