
规格写完了。按系列第一篇的路子——真跑 superpowers 拿到的是一份 RBAC 全栈框架规格examples/prd2spec/02-spec.md已经把「AI 会猜错」的岔路口清掉了大半。可接下来这步是整套流程里最爽、也最坑的一步让它写代码。爽在从文档到能跑的程序就差一句「开始吧」坑在 Claude 的默认动作是先写实现、再回头补测试。它补的测试不是来验证需求的是来给已经写完的代码盖橡皮章的——测试全绿覆盖全是假的。所以我的判断是反着来的让 Claude 写代码之前得先逼它写「会失败的测试」。系列第一篇把整条路画给你看过需求 → 规格 → 拆计划 → TDD 实现 → 验收 → 迭代。本篇走第二格和第三格——spec 到手之后的两步先把规格拆成有依赖顺序的任务清单再逐任务用 TDDRed→Green→Refactor实现。全流程地图不重复贴往上翻第一篇就有。全程我用第一篇真跑出来的那个项目当实例拆计划用真实落盘的examples/prd2spec/04-plan.mdTDD 循环用后端真实实现里的requirePermission中间件代码全从仓库里截的能照着抄。读这篇前你手里得先有一份 spec。没有就先回去读第一篇把需求和验收标准补齐再回来。这一篇的假设是——你要写的是一个模块互相依赖的复杂系统不是一个一次性脚本。拆计划先治「全局失序」系列地图里spec 的下一格是拆计划。一句话把一份 spec 拆成有依赖顺序、每个任务都能单独验收的小任务。别小看这步它治的是 vibe coding 最大的死因——全局失序。不拆计划直接让 Claude 按 spec 一把做完会怎么样它每块代码单独看都合理但中间没有任何验收点一个假设在第三行错了后面两百行全建在错假设上。你验收时看到的不是「一个 bug」是「一个系统性的错位」从哪改起都不知道。拆计划就是把「从哪改起都不知道」变成「最多错一个任务」。打个比方。不拆计划让 Claude 一把梭等于让一个不看路标的人开长途。它每段路都开得稳稳的你坐在后座等发现方向不对时已经下了三个高速口。拆计划是在每个出口提前立好路标——走错最多错一个出口就能发现。代价是每个出口你都得多看一眼收益是你永远不会在五十公里外才发现偏航。三个原则拆计划我压成三条原则照着拆不会散一个任务一个可验证的产出。任务不是「写授权模块」这种大词而是「实现 requirePermission 中间件让无权限调用返回 403」这种能独立验收的句子。判断标准就一条这个任务做完了你能不能单独验证「做完了」。不能单独验证的拆。依赖顺序 能测的先做地基先行。权限系统按「建表 → 认证 → 授权 → 角色 → API → 前端」排前一个任务是后一个任务的验收前提。建表没落地认证没法测认证没落地授权没法测。顺序不是你喜欢什么先做什么是「什么先能被测出来」。每任务绑定验收测试。从 spec 的验收标准用户故事 US如 US6 无权限点访问被拦和测试清单里把对应条目摘到任务头上。任务完成的标准不是「代码写完了」是「它绑定的那条验收从红到绿」。这一个绑定是拆计划和 TDD 之间的接缝——拆的时候就把「怎么算做完」钉死实现的时候才有得验。为什么这个接缝重要第一篇讲过「一个脊柱、两道闸」test 是第一道闸。可闸门得有东西可闸——拆计划就是在 spec 和 test 之间铺轨道让每个任务在开工前就知道自己的验收标准是什么。验收标准拖到实现完再补就是让 AI 自己给自己判分判出来的分你不敢信。RBAC 全栈框架拆计划实例回到第一篇那份规格examples/prd2spec/02-spec.md。它的验收标准是六条用户故事——US1 管理员能管理用户/角色/权限点、US2 运营只能管理商品、US3 运营只看自己创建的商品、US4 管理员看全部商品、US5 未登录访问被拦、US6 无权限点访问被拦——外加一张 29 个用例的测试清单。我按上面的原则把它拆成十一个任务真实计划在examples/prd2spec/04-plan.md验证绑定直接抄 spec 的条目任务内容验证T1 后端骨架package.json、config、db 连接 建表 种子数据启动建表种子存在T2 认证登录 API JWT 签发 auth 中间件登录返回 JWT无 token 401T3 权限permission 中间件 dataScope 中间件 权限点 API无权限点 403SELF 过滤生效T4 用户管理CRUD 分配角色集成测试通过T5 角色管理CRUD 绑定权限点 data_scope集成测试通过T6 商品模块CRUD 数据范围过滤集成测试通过T7 后端测试单测 集成supertestnpm test 全绿T8 前端骨架Vite Vue3 UnoCSS 路由 pinia axiosnpm run dev 可起T9 登录页登录页 导航守卫 token 持久化登录跳转、401 拦截T10 用户/角色管理页列表/增删改/绑定页面功能可操作T11 商品管理页按钮按权限显隐权限按钮显隐正确依赖关系是这张图箭头指「必须先于」注意 T3 内部会拆成两个 TDD 周期先做permission中间件拒掉无权限调用US6再做dataScope中间件SELF 只放本人数据US3——一个周期一个行为第四章你会看到第一个周期的完整过程。真实团队也这么拆过。得物技术发过一篇 Spec Coding 实战tech.dewu.com/article?id212记录一次前端项目从 0 到 110 天 2.5 万行代码、提效 36%核心决策链是 proposal → design → specs → tasks——先规格、再拆任务tasks 分组让 AI 聚焦当前步骤先明确验收标准再实现。规模你不一定复刻得了但「先拆任务、任务绑验收、再实现」这个顺序跟我这套是同一条。用 Claude 拆人审拆计划能交给 Claude但别全交。我的做法把 spec 丢给它让它输出「任务清单 依赖顺序 每任务验收绑定」。它习惯在 plan mode 里只读探索、不动代码——plan mode 是干嘛的我在别处讲过不展开。任务拆好之后想让 Claude 按依赖顺序自动往下执行可以用 /workflow 编排一轮任务机制同样不展开。它拆完你人审三件事依赖顺序对不对、每个任务是不是真能独立验收、验收绑定有没有把 spec 的验收标准摘漏。为什么最后一道是人审因为拆计划的质量直接决定后面所有 TDD 的质量——拆错了后面每个任务都在错的地基上盖房。Claude 拆出来的计划可以很顺眼但「这个任务到底怎么算做完」只有你还有你的 spec说了算。对照 superpowerswriting-plans 就是「拆计划」的产品化这套拆法我自己攒了好一阵回头看 superpowers 插件发现它把同样的动作做成了技能名字就叫 writing-plans。它就是我说的「拆计划」的产品化实现只是粒度更狠。writing-plans 的玩法跟我的三个原则逐条对上每个 step 拆到 2-5 分钟级是「一个动作」而不是「一个任务」写失败测试是一步、跑起来确认失败是一步、写最小实现是一步、再跑确认绿是一步、提交是一步。每个任务写全精确文件路径、完整代码块、精确命令 预期输出还有 Interfaces 块这个任务消费什么、产出什么让相邻任务知道彼此的精确签名。TDD 测试步骤内置它把「写失败测试 → 确认失败 → 最小实现 → 确认通过 → 提交」直接写进每个任务的步骤里你拆计划的时候顺便把测试循环也拆好了。禁占位符计划里不许出现 TBD、TODO、「加合适的错误处理」这类话——每个 step 必须给全真实内容和完整代码。为什么这么狠因为执行计划的子代理对项目零上下文占位符等于把决策留给了一个读不懂上下文的代理它只能猜。存盘计划落到 docs/superpowers/plans/YYYY-MM-DD-功能名.md进 git。它和我手拆最大的区别在粒度。我拆到「一个任务一个可验证产出」就停——一个任务可能是一下午的量它拆到 2-5 分钟一个 step。为什么它敢拆这么碎因为执行者不同。人是执行者时任务级够用拆太碎反而烦代理是执行者时step 级才不跑偏——代理没耐心也没上下文跨越大步每个 step 必须自带验证点。所以粒度不是越细越好是跟执行者匹配。这条写给你是提醒别照搬它的粒度——先想清楚执行计划的是人还是代理。TDD 实现为什么 TDD 治「假覆盖」拆完计划进第三格TDD 实现。为什么非 TDD 不可我先把话撂这儿TDD 对 AI 不是锦上添花是刹车。AI 写代码天然是下坡——它倾向一次写很多、写完整、写着写着觉得「再顺手多加点」。刹车不让你更快但让你在每个弯道都能停下来重新看清方向。没有刹车也能到终点只是你不敢问自己是怎么到的。Claude 的默认写代码姿势恰好是 TDD 的反面。社区对这个有共识claude-code-ultimate-guide 把话说得很白Claude Code 对实现有先天偏好implementation-first bias——你让它建功能它自然先写能跑的代码再写一套对着这堆代码必然全过的测试。这不算 bug是训练本能。所以光说「写个测试」没用你得显式命令它「先写 FAILING 测试」「先给失败断言再动生产代码」。这是本篇最硬的一条纪律。假覆盖是什么测试套件绿成一片但断言全是迁就实现写出来的——测的是实现长什么样不是需求要什么。像镜头盖没摘的摄像头一直开着一直在录录的全是黑屏。TDD 把顺序倒过来先写一个会失败的测试亲眼看到它失败再写最小实现让它变绿。失败断言是「这个行为还不存在」的硬证据。有了这个证据绿才有意义。requirePermission 中间件完整 TDD 循环实例来了从真实仓库examples/prd2spec/backend/里截。拆计划把「权限」挂在 T3对应 spec 的 US6有 token 但缺权限点 → 403。现在实现 T3 的第一个周期——requirePermission拒掉无权限调用。Red先写 FAILING 测试。注意措辞不是「写测试」是「写 FAILING 测试」。给 Claude 的命令原文是先写测试别动实现这个测试必须是因为功能还不存在而失败。测试长这样backend/test/unit/permission.test.js// backend/test/unit/permission.test.js —— 周期一的失败测试 const { requirePermission } require(../../src/middleware/permission); test(requirePermission 拦截缺少权限点的用户返回 403, () { const req { user: { id: operatorId } }; let status null; let body null; const res { status(s) { status s; return this; }, json(b) { body b; return this; }, }; requirePermission(user:list)(req, res, () { throw new Error(不应进入 next); }); assert.strictEqual(status, 403); assert.strictEqual(body.code, 403); });operatorId是测试 setup 里造的一个绑定operator角色的用户——种子角色只有product:*4 个权限点没有user:list。这段测试引用了../../src/middleware/permission——这个文件还不存在。这就是「红」的正确打开方式测试针对一个还不存在的功能。确认失败断言。跑测试命令node --test backend/test/unit/permission.test.js。预期输出Error: Cannot find module ../../src/middleware/permission红得对。因为失败原因是「功能缺失」不是拼写错、不是依赖装错、不是测试自己写错。这一步是整个循环里我最不让你省的确认失败断言就是确认这个测试真的在测「功能不存在」这件事。Steve Kinney 的课程把这步叫 watch the first test绝不让代理写一个一跑就过的测试在动生产代码之前先让它把失败断言亮给你看。他还有一句更狠的「没亲眼看着测试失败你就不知道它测的是不是对的东西。」superpowers 的 TDD 技能里这句话几乎是原文——这不是一个人的偏好是 TDD 对 AI 的核心。我的结论也放这儿不确认失败断言就放行等于没写测试。你想想——测试跑完是绿的你分不清是「功能真的对了」还是「测试压根没测到点子上」。失败断言是你唯一能确认「测试在测对的东西」的时刻。这个时刻放过了后面所有绿都不可信。Green写最小实现。现在允许 Claude 动生产代码了命令是「只写让这个测试变绿的最少代码不要顺手加别的」。最小实现backend/src/middleware/permission.js 配套的权限点查询// backend/src/middleware/permission.js最小实现 const { getPermissionCodesByUserId } require(../db/queries); function requirePermission(code) { return function permissionMiddleware(req, res, next) { const codes getPermissionCodesByUserId(req.user.id); if (!codes.includes(code)) { return res.status(403).json({ code: 403, message: 无权限访问缺少权限点${code} }); } return next(); }; } module.exports { requirePermission };配套的getPermissionCodesByUserId同周期写进backend/src/db/queries.js——它把「用户 → 角色 → 权限点」的三表 JOIN 合并成一组权限点 code中间件才能判断。写它的原因只有一个测试要按 operator 的真实权限点判断得先有这个查询。只写了让失败断言变绿的最少东西没有参数校验、没有错误处理、没有「user 不存在怎么办」。不是这些不该想是这一轮不想——现在加的任何一行都是没被测试逼出来的代码属于 YAGNI。superpowers 的 TDD 技能甚至允许 Green 阶段「作弊」——硬编码、复制粘贴都行反正重构会收拾。你看到的是收敛版作弊版就不贴了。再跑node --test backend/test/unit/permission.test.js绿。确认绿和确认红一样重要绿了且只有当前这个测试从红变绿别的测试没被碰坏。Refactor重构测试保持绿。真实实现里这一周期还做了一次「不改行为地变结构」把查到的权限点集合挂到请求上——req.permissions codes——后面 dataScope 中间件和商品 handler 不必再查一遍库。收敛后长这样// backend/src/middleware/permission.js重构后 function requirePermission(code) { return function permissionMiddleware(req, res, next) { const codes getPermissionCodesByUserId(req.user.id); req.permissions codes; if (!codes.includes(code)) { return res.status(403).json({ code: 403, message: 无权限访问缺少权限点${code} }); } return next(); }; }重构完重跑同一命令绿。注意这里的重构没加任何行为数据权限的 SELF 过滤是另一个行为属于下一个周期。重构只允许「不改行为地变结构」。下一个周期。T3 还绑着 US3运营只看自己创建的商品。这是数据权限独立行为走第二个完整周期。核心是一个纯函数buildDataScopeFilter测试如下backend/test/unit/dataScope.test.js// backend/test/unit/dataScope.test.js —— 周期二的失败测试 const { buildDataScopeFilter } require(../../src/middleware/dataScope); test(buildDataScopeFilter(SELF) 生成 creator_id 过滤条件, () { assert.deepStrictEqual(buildDataScopeFilter(SELF, 7, creator_id), { where: creator_id ?, params: [7], }); });先写测试此时dataScope.js不存在 → 红→ 最小实现SELF 返回{ where: creator_id ?, params: [userId] }ALL/DEPT 返回 null 不过滤 → 跑绿 → 商品列表路由接上router.get(/, requirePermission(product:list), dataScope(), handler)handler 里buildDataScopeFilter(req.dataScope, req.user.id)拼进 SQL 的 WHERE。几处小 diff行为加一条全部测试保持绿。我没有把两个行为塞进一个周期因为一次一个周期失败时你才知道是哪个行为错了。四条纪律为什么不能省上面这个循环里有四条纪律每条都有「为什么」不是仪式先写 FAILING 测试。Claude 默认先实现后补测补的测试是橡皮图章。「让 Claude 写测试」和「让 Claude 写 FAILING 测试」是两件事。前者它写的是辩护词后者它写的是起诉书。你要的是起诉书——先证明现有代码有罪再让生产代码改。先确认失败断言。唯一能证明「测试测的是对的东西」的时刻。绿测试能说明的东西太多可能压根没测、可能测错了地方只有失败断言是确定的。这条和上一条是一对写 FAILING 测试保证测试在测新行为确认失败保证它测的是「功能缺失」而不是「测试写错」。一次一个 TDD 周期。一次让 Claude 从红到绿全通过是最大的偷懒——绿得太容易你连它到底做了几件事都数不清。多个行为混在一个周期里测试一红你定位不到是哪个行为错了只能整块返工。一个周期一个行为最坏情况是某个行为错了你只返工那一个。保留 Refactor。绿只证明行为对不证明结构对。跳过重构技术债在 AI 手上积累得比人快——它每次改这块代码都会在原来的乱结构上再叠一层。thoughtbot 的文章里那个翻车现场就是这么来的它第一次让 Claude 干Claude 把 model、controller、route、view、migration、seeds 一次全写了9 个文件从红到绿一大步没有任何重构。作者觉得这违背了 TDD 精神把改动全部 stash 掉重来。重构不是可选项是循环的一格。另外注意绿测试不证明重构安全重构完必须重跑同一验证命令——Steve Kinney 专门提醒过这条。反模式清单五个「假覆盖」的坑上面四条纪律反过来说就是五个最常见的坑。每个我都见过人踩包括我自己。每条给你「错在哪」和「正确做法」。写 tests不是 failing tests。命令是「给这个功能写测试」Claude 写了测试全绿——因为它照着实现写的。错在哪测试迁就代码测的是代码长什么样不是需求要什么。正确做法命令改成「先写 FAILING 测试别动实现」并让它把失败断言亮给你。一次做多个功能。一个任务里塞三个行为让 Claude 一口气完成。错在哪红了定位不到是哪个行为错绿了不知道它是不是把不该做的也做了。正确做法一个周期一个行为最小实现只解决当前断言别的行为下个周期见。跳过 Refactor。测试绿了就走下一轮再来。错在哪结构越来越乱AI 下次改这处会叠加更多乱测试是绿灯不保证房子结构安全。正确做法绿之后显式进重构重构完重跑测试。mock 当测试。把真实依赖全替换成 mock测试断言 mock 的返回值。错在哪mock 永远返回你设定好的值测试不可能失败——绿了只证明 mock 写得对不证明代码对。superpowers 的 TDD 技能原话除非实在躲不开不要用 mock。正确做法能测真实行为就测真实mock 只用来隔离真正的外部边界网络、时间、文件系统。让测试立刻通过。测试红了让 Claude「修一下测试」删断言、加 .skip、放宽断言。错在哪这是 reward hacking把闸门本身拆了。测试红了该改的是生产代码不是测试。正确做法测试一旦被确认过就冻结红只能由生产代码变绿来解决。claude-code-ultimate-guide 专门给过对策确认过测试文件后冻结它、审查 diff、用 Stop hook 拦住红套件不让收工。对照 superpowers实现的产品化subagent-driven-development每任务独立子代理 两阶段审查我在第四章是手动守纪律自己命令、自己看失败断言、自己跑测试。superpowers 把「实现」这步做成了另一个技能subagent-driven-development。它把我在第四章做的事全部搬给子代理还多加了一道我手动做起来很累的环节——独立审查。它的玩法每个任务派一个全新的子代理去实现fresh subagent per task实现完不直接进下一个任务先过两道独立的审查闸第一道spec 符合性审查spec-compliance审查子代理读实际代码核对「实现的是不是任务要的东西」——该做的做全了吗有没有加没要求的东西YAGNI 违规第二道代码质量审查code-quality只有 spec 符合性过了才启动看结构、可维护性、测试覆盖。两道审查的顺序是硬性的不能反。为什么因为这是两个维度——一个写得漂漂亮亮但做错了需求的函数照样是废的。先审「做对了」再审「做得好」顺序反了就是浪费精力在注定要重写的代码上挑结构。这跟我「拆计划先绑验收」是同一个道理先钉死「怎么算做完」再看「做得怎么样」。审查不合格打回给同一个实现子代理修修完再审循环到过才进下一个任务。它把我的「每任务验收」自动化了我手动对照 spec 逐条过它派一个专职审查子代理干这事而且审查读的是代码不是实现者的汇报——这点比我手动做还稳我手动容易听 Claude 说完「好了」就信。但我不夸大它。superpowers 是 Jesse VincentGitHub 上叫 obra写的开源 Claude Code 插件MIT 协议/plugin install superpowersclaude-plugins-official就能装一套 14 个技能。它的口碑是真实积累出来的——2026 年 6 月官方插件目录里安装量约 75 万排在 Anthropic 自家插件后面。它也有已知的失效模式GitHub issue #463 里就有人报告控制器会跳过派审查子代理这一步理由是「这任务太简单不用审」——文字规则被模型合理化掉了。我的判断两阶段审查是对的但它跟所有靠自觉的纪律一样有被绕过的空间。手动做的好处是审查人是你自己你没法骗自己。test-driven-developmentTDD 纪律的硬约束subagent 管「每任务怎么过」test-driven-development 技能管「每个任务里怎么实现」。它是 TDD 纪律的产品化比我在第四章的手动约束更狠。核心是一条铁律原文大写NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST——没有失败的测试就不许写生产代码。违反的后果不是提醒是删除先于测试写的代码要删掉重来不能留着当参考、不能边写测试边改它。「删除就是删除。」它为什么这么绝因为它认准一件事先写的代码会污染测试的公正性——你会不自觉地写一个对已存在代码友好的测试而不是写一个测需求的测试。它还有一张「合理化借口表」把跳过 TDD 的常见借口逐条堵死「太简单不用测」「先测后再测结果一样」「我已经手动测过了」「删掉几小时的工作太浪费」——每条都给了反驳。这张表的价值不在反驳本身在于它承认了一个事实跳过 TDD 从来不是技术问题是借口问题。和我第四章的做法对着看我手动用命令约束自己它用铁律 反借口表约束代理。方向完全一致它只是把「每次都要自己想起」变成了「技能的默认行为」。这就是 superpowers 那句口号的意义——Process over Prompt流程比提示词重要。你手动走、我手动走、它插件走最后到的是同一个地方让 AI 在写代码之前先证明它知道要写的是什么。结论拆计划治「全局失序」TDD 治「假覆盖」走完这两格把主线的两个词收一下。拆计划治「全局失序」把一份 spec 拆成有依赖顺序、每任务绑定验收测试的任务清单让 Claude 的每一步都有路标最坏情况错一个任务而不是错一个系统。TDD 治「假覆盖」用「先写 FAILING 测试、先确认失败断言、一次一个周期、保留重构」把每个任务的完成标准钉成可执行的断言让绿灯从「它说好了」变成「我看到它绿了」。一句话拆计划管「往哪走」TDD 管「走到没」。前者让 AI 不跑偏后者让 AI 不糊弄。两件事都不难难的是每次都想起来做——这也是下一篇的主题。「复杂软件系统的Vibe Coding实践」还有两格没走怎么把这些纪律固化进工具hooks、CLAUDE.md、技能以及怎么验收、怎么迭代收尾。下一篇讲这个关注不迷路。你用 TDD 让 Claude 写代码时踩过什么「假覆盖」的坑是它把测试写成辩护词还是你让它一口气干完、没看失败断言评论区说说我想看看大家各自栽在哪一条。想让下一篇展开哪块的也评论区告诉我——hooks 怎么拦 RED、任务拆多细才不碎、验收清单怎么定你问的我都会记下来可能就成了下一篇的选题。收个尾。AI 写代码像下坡TDD 是刹车。别嫌刹车麻烦——下坡不装刹车也能到终点只是你不敢问自己是怎么到的。系列文章1.复杂软件系统的Vibe Coding实践一从需求到规格superpowers 实操-CSDN博客