复杂软件系统的Vibe Coding实践三:工具配置与迭代
前面两篇讲完了流程:需求怎么变成规格,规格怎么拆成计划、再用 TDD 逐格落地。流程本身不复杂,难的是让它不变形。我第一次带项目走完整套,第二次就跳步了——先写测试太麻烦,直接说「开始写吧」;第三次连我自己都忘了要先问边界。
我先把这篇的判断放这:把纪律写进人脑,不如写进工具。AI 不记得你说过什么,但 hook 和 CLAUDE.md 记得。
这是系列最后一篇,讲三件事:怎么把流程固化进工具(CLAUDE.md 当契约、hook 强制 TDD、skill 与子代理分工、MCP),怎么验收和迭代(/goal 可测终态、checkpoint、spec 演进、迭代节奏),以及这套东西的坑和边界在哪。前两篇给的是方法,这篇给的是能直接抄的配置——读完你能拿走一份 CLAUDE.md 契约实例、一组 hook 配置、一个 /goal 验收模板。
系列地图:五格之外,还要一个固化层
先回到系列开篇那张图。整套流程五格:需求→规格、规格→拆计划、拆计划→TDD 实现、验收、迭代。前两篇走完了前三格,剩下两格——验收、迭代——本篇会讲到,但那不是重点;重点是包在五格外面的那层东西:固化层。
| 流程节点 | 我的做法 | superpowers 对应技能 |
|---|---|---|
| 需求→规格 | brainstorming 九步(真实问答记录)→ 02-spec.md(US1-6 用户故事) | brainstorming |
| 规格→拆计划 | 任务清单 + 依赖顺序 + 验收绑定 | writing-plans |
| 拆计划→实现 | TDD:红灯-绿灯-重构 | subagent-driven-development / test-driven-development |
| 验收收尾 | /goal 可测终态 + 对照 spec 逐条过 | finishing-a-development-branch |
| 固化层 | CLAUDE.md 契约 + hook 强制 + skill/子代理分工 | using-superpowers(SessionStart 注入 + 惰性加载) |
这张表第一篇贴过,这里补一行「固化层」。前四行是流程本身,最后一行是让流程自动运转的脚手架。没有固化层,流程靠你每次开会时口头提醒,跑两次就散了。
本文要回答的问题就一个:怎么让流程成为项目的默认行为,而不是你的个人纪律。
CLAUDE.md 是给 AI 的可执行契约,不是项目介绍
很多人写 CLAUDE.md 是当 README 写:项目是什么、目录结构、技术栈。这些有用,但不够。我在腾讯云一篇讲 SDD 落地历史项目的文章里看到一个更准的定位——把 CLAUDE.md 当「可执行契约」,不是「项目介绍」。项目介绍回答「这个项目是什么」,契约回答「在这个项目里,AI 被允许做什么、必须做什么、不许做什么」。前者是背景,后者是规则。
背景不痛不痒,规则才救命。历史项目里 AI 最容易闯的祸,全在规则缺席的地方:忘记数据库约束、不复用已有策略类硬塞 if-else、重构时删掉并发竞态处理。契约就是把这些写进 AI 每次会话都看得见的地方。
我拿第一篇真跑出来的那个项目(examples/prd2spec/)写一份实例。八项,照抄骨架就行:
# rbac-framework CLAUDE.md(可执行契约)
## 1. 项目概况
单仓库全栈:`backend/`(Node + Express + better-sqlite3 + jsonwebtoken + bcryptjs)+ `frontend/`(Vue3 + Vite + UnoCSS + Pinia + Vue Router + Axios)。
## 2. 唯一可信命令
- 后端全量测试:cd backend && npm test
- 后端启动:cd backend && npm start(:3000)
- 前端构建:cd frontend && npm run build
- 前端开发:cd frontend && npm run dev(:5173,/api 代理到 :3000)
## 3. 架构边界(谁负责什么)
- 鉴权链路固定:auth(验 JWT)→ permission(验权限点 403)→ dataScope(注入数据范围)→ handler
- 权限点判定一律走 requirePermission,禁止在 handler 里手写权限判断
- 数据权限过滤一律走 buildDataScopeFilter,禁止在业务 SQL 里手写 creator_id 条件
- API body 用 camelCase:roleIds / permissionIds,禁止下划线
## 4. 不变量(任何变更不得破坏)
- 权限 = 功能权限(module:action 权限点)+ 数据权限(roles.data_scope)
- data_scope=SELF:列表查询只返回 creator_id = 当前用户的数据
- 多角色时 data_scope 取最宽:ALL > DEPT > SELF
## 5. 观测方式
- 统一响应 { code, message }:401 未登录、403 无权限/超数据范围
- 种子账号 admin/admin123(ALL + 全权限)、operator(SELF + product:* 4 条)
## 6. 已知陷阱(历史踩坑,别重踩)
- 删除接口返回 { code: 200, message },前端拦截器按 code >= 400 判错,别把 200 当失败
- 商品创建人字段是 creator_name(JOIN 出来),不是 creator_username
- 绑定权限用 permissionIds、分配角色用 roleIds,用下划线会静默失败
- operator 没有角色时 data_scope 默认 SELF,别假设恒为 ALL
## 7. 数据库变更约束
- 建表/种子走 backend/src/db/schema.js 与 seed.js,别手写 SQL 初始化
- 新查询函数放 backend/src/db/queries.js,别散在路由里
## 8. AI 工作约束(不要做什么)
- 没先写 FAILING 测试,不许碰生产代码(node --test)
- 禁止发明 data_scope 之外的新权限值
- 不引入新的外部依赖
八项里有三块是「项目介绍」里绝不会出现的:不变量、已知陷阱、AI 工作约束。这三块才是契约值钱的地方。
不变量是 AI 看不见的雷区——代码里没有一行写着「SELF 只返回本人数据」,AI 改商品列表的 SQL 时根本不知道这里是禁区,直到运营看到了别的运营创建的商品。已知陷阱是历史项目最值钱的资产,腾讯云那篇说得直白:项目超一万行、或在存量业务里做改动时,AI 遗忘约束的风险陡增。AI 工作约束则是把「不许越界」从提示词搬进常驻上下文——你不可能每次对话都重讲一遍。
「禁止事项」为什么要写进契约而不是留在脑里?因为 AI 的默认行为是顺着代码上下文发挥,不是顺着你的记忆发挥。你记得「这模块不能动」,代码里没有这个信号,AI 就不会知道。契约就是把你的隐性记忆,变成 AI 的显式上下文。
用 hook 把底线焊死,不靠模型自觉
CLAUDE.md 是建议层。建议层的意思是:模型大多数时候遵守,但它是「记得的时候遵守」。真不能妥协的底线,得靠 hook 焊死——hook 是脚本,挂上就一定执行,不由模型自觉。这句话我在工具文那篇说过,这里落到 TDD 上。
先给最划算的一个:写完代码自动跑测试。这是把「测试全绿」从口头约定变成机械默认的第一步。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/run-tests.sh \"$CLAUDE_PROJECT_DIR\"",
"timeout": 120
}
]
}
]
}
}
run-tests.sh 干一件事:跑后端测试,失败就把输出扔给模型看。注意一个机制细节——hook 的 stdout 在 PostToolUse 里默认不喂给模型,所以失败要走 stderr + 退出码 2,这个组合会把 stderr 原样塞给模型;成功就静默退出,别污染上下文。
#!/usr/bin/env bash
# .claude/hooks/run-tests.sh —— PostToolUse 自动跑后端测试
set -uo pipefail
cd "$1/backend" # CLAUDE_PROJECT_DIR/backend
if OUT="$(npm test --silent 2>&1 | tail -n 30)"; then
exit 0 # 全绿,静默
else
printf '测试失败,请修复:\n%s\n' "$OUT" >&2 # stderr 会被模型看到
exit 2
fi
效果是——每次 Edit 完自动跑一遍测试,红了就贴失败输出,模型自己看着去修。这一步挡的是「改完就忘测」,成本最低,收益最直接。
第二道闸更狠:拦 RED 阶段。TDD 的红灯是「先写测试,看到它失败」,但 Claude 的默认倾向是先实现后补测。怎么让「先写失败测试」从建议变成硬约束?hook 拦不住模型的想法,但能拦它的手——在 Write/Edit 生产代码之前,检查「是否已确认失败测试」这个状态。
#!/usr/bin/env bash
# .claude/hooks/pre-tool-use.sh —— RED 闸
set -euo pipefail
INPUT="$(cat)"
TOOL="$(printf '%s' "$INPUT" | jq -r '.tool_name // empty')"
[ "$TOOL" = "Write" ] || [ "$TOOL" = "Edit" ] || [ "$TOOL" = "MultiEdit" ] || exit 0
FILE_PATH="$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')"
case "$FILE_PATH" in
tests/*|docs/*|*.md) exit 0 ;; # 测试与文档随便写
esac
# 没有「已确认失败测试」标记,拦下生产代码写入
if [ ! -f "${CLAUDE_PROJECT_DIR}/.claude/.red-ok" ]; then
echo "RED 闸:还没有确认失败的测试(缺 .claude/.red-ok)。先写 FAILING 测试并看到它失败,再动生产代码。" >&2
exit 2
fi
exit 0
pre-tool-use.sh 返回码 2 = 拦下这次工具调用。注册方式和 PostToolUse 一个结构,事件换成 PreToolUse:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/pre-tool-use.sh",
"timeout": 10
}
]
}
]
}
}
配合 TDD skill:先写测试 → 运行确认失败 → 创建 .claude/.red-ok → 之后才放行生产代码。跑完一轮全绿,清掉标记,进入下一个周期。
这里得说句实话,别把 hook 神化:它拦的是状态,不是意图。模型完全可以先写个假测试骗过标记再写实现——hook 管不住恶意,管的是惯性。人用这套的收益,是把「记得先写测试」变成「必须过一道闸」,把纪律从自觉降级成机械动作。这正是「固化」的意义:不是让 AI 变聪明,是让 AI 不犯懒。
把这一层画成图,固化层的全貌是这样:

skill、子代理、MCP:把流程打包成资产
hook 管底线,流程本身靠 skill 和子代理打包。
skill 装多步流程。 TDD 的完整步骤——写失败测试、确认失败、最小实现、确认绿、重构——每一步都让模型记着很容易走样。打包成一个 skill,调用即展开:
# .claude/skills/tdd/SKILL.md
---
name: tdd
description: 用红-绿-重构实现一个功能。先写 FAILING 测试并确认它失败,再写最小实现。
---
1. 写一个失败的测试,断言你要的行为。
2. 运行它,确认它真的失败(不是报错、不是误写)。
3. 写最小实现让它通过。
4. 运行全量测试,确认没有回归。
5. 重构,保持全绿。
skill 的机制(name+description 常驻、正文调用才加载)我在工具文那篇讲过,这里不展开。用它的原因是:多步流程写成段落塞进每次提示词,模型读不全也记不住;做成 skill,一次说「用 tdd」就展开全套。
rules 挂路径约束。 想让某条规则只在碰某类文件时生效,用 rules + paths:backend/src/modules/** 下的路由必须过 requirePermission,backend/src/db/schema.js 禁止改动。路径约束比全局常驻省上下文,也比全凭模型判断更准。
子代理管分工。 最值得固化的一对:tester 和 reviewer。TDD 的验证环节最怕「实现者自己验自己」——Claude 写完代码跑测试,很容易把「测试失败」解释成「测试写得不对」然后顺手改测试。把验证丢给只读子代理,它不能改代码,只能如实报告:
# .claude/agents/tester.md
---
name: tester
description: 只读运行测试,报告失败与断言差异,不修改任何文件。
tools: Read, Grep, Glob, Bash
---
你是测试执行者。运行测试,把失败用例的断言差异原样报告,不修改任何文件,不判断对错。
# .claude/agents/reviewer.md
---
name: reviewer
description: 只读代码审查,对照 spec 检查实现符合性,输出问题清单。
tools: Read, Grep, Glob
---
你是审查者。对照 docs/specs/ 下对应 spec,逐条检查实现是否符合验收标准,输出问题清单,不修改文件。
分工的本质是把「既当运动员又当裁判」拆开。实现交给主会话或写代码的子代理,验证交给只读的 tester,审查交给只读的 reviewer。三方各自的 prompt 要自包含——子代理对你的对话没有记忆,别指望它记得 spec 内容,要把 spec 路径或关键验收标准写进派发指令。
MCP 给只读数据面。 复杂系统里 spec 的验收标准与数据模型常常要对着真实结构写——表怎么建、外键怎么走、字段类型。数据库 MCP 给 AI 一条只读的数据通路:看 schema、跑查询、验索引,就是不给写权限。写库的权限留在人手里,交给建表脚本或迁移工具(如 Liquibase/Flyway)。给 AI 的每一点数据能力,都要先想清楚「它能用这个做什么坏事」——只读能查、改不了,这就是安全边界。
对照 superpowers:技能库怎么自己组织起来
上面是我在 Claude 原生能力上一点点攒的固化层。写成回头一看,superpowers 插件把「固化流程」这件事做得比我完整得多——它把整套工程纪律打包成一个自组织的技能库。拆开看四个机制。
SessionStart hook 注入入口。 这是整个技能库的根基。插件在 hooks.json 里注册了一个 SessionStart hook(matcher startup|clear|compact,同步执行),每次会话开始(包括 /clear、/compact 之后)把 using-superpowers 这份元技能的全文注入上下文,外面裹一层 <EXTREMELY_IMPORTANT>。为什么用 hook 不用 skill?因为 skill 是模型「主动选择」才加载的——模型可以说「这题用不上技能」,deadlock 就来了。用 hook 注入,绕开模型的决定权:你不需要同意你有超能力,你有。
惰性加载。 常驻上下文的只有 using-superpowers 一份元技能,其余十几个技能(brainstorming、writing-plans、tdd、systematic-debugging……)只以 name+description 常驻,正文调用时才读。它的原则是 give little, give late, only give when used——给得少、给得晚、只在用的时候给。这一手省的是 token,本质是「skill 机制」的极端版:把全套纪律做成目录,而不是做成一本从头读到尾的书。
自动触发。 元技能里立了一条近乎霸道的规则:哪怕只有 1% 的可能某个技能适用,你也必须调用它。还配了一张「这是在找借口」的红旗对照表——「这只是个简单问题」「我得先看看代码」「我记得这个技能」全都列为 rationalization。它跟 AI 的「偷懒合理化」正面硬刚:不是等你想起技能,是把你逃课的理由先堵死。
RED 硬约束。 tdd 技能把红灯做成了铁律:没有失败的测试在前,不允许任何生产代码。先写了实现再想起 TDD?删掉重来,不留作参考、不「顺手改改」、连看都不许看。铁律黑体加粗写在技能开头,后面整页是「常见的合理化借口」逐条拆穿——「代码已经写了几小时删了浪费」是沉没成本谬误,「我先自己测过」不算数。这套比我前面那个 .red-ok 标记彻底得多:它从机制层直接定义「先于测试的实现不存在」,而不是靠检查文件。
不夸大它。superpowers 是 obra(Jesse Vincent)的开源 Claude Code 插件,MIT 协议,/plugin install superpowers@claude-plugins-official 就能装。它强在把纪律做成了不可绕过的机制,代价是你得接受它的整套节奏——技能规定「简单项目也必须先出设计再动手」,这套对一部分人是解放,对另一部分是束缚。插件迭代也一直在给注入瘦身:v6.1.0 压缩了 using-superpowers 的开机引导,把一张技能流程图换成几行文字,为的是省每次会话的 token 成本。说明「固化」也要算成本账,不是越厚越好。
验收与迭代管理:全绿之后,还有一关
固化层搭好,流程能自己转了,接下来是让它在「跑得快」和「跑得对」之间平衡。这一章讲验收和迭代。
/goal 写成可测终态。 /goal 是条件驱动的——它盯着的是一个可判定的终态,不是「做完」。机制我在工具文那篇讲过,这里只讲怎么把它当验收闸用:把目标写成「全绿 + 对照 spec 逐条过」,而不是「实现角色-权限点绑定」。
/goal RBAC 商品模块数据权限:cd backend && npm test 全部通过,
且 examples/prd2spec/02-spec.md 的 US3(运营只看自己创建的商品)对照实现成立
终态里嵌了 spec 的验收标准,等于把「人看结果」的最低线提前写死。AI 干到条件满足才停,不会自己宣布「差不多了」。
checkpoint 兜底长任务。 放手让 AI 长跑时,checkpoint 是安全网——改动前自动留档,跑砸了能回到上一个可用点,不会把一整天的工作带沟里。我跑长任务的习惯:每完成一个 TDD 周期看一次进度,checkpoint 只兜底、不顶替验收。它是「迭代不怕改坏」的底气:敢改,因为改得回来。
spec 是活的,但变动有顺序。 系列第一篇讲过,spec 是活文档不是冻结契约。落到迭代上就是一条铁序:需求变了,先改 spec,再让 AI 改代码。spec 永远领先代码一步,代码追着 spec 走,不是 spec 追着代码漂。改代码前先问「spec 要不要改」,是防止 AI 把「改了行为」当成「改了 bug」。
迭代节奏分三态。 不是所有改动都走同一套入口。我按改动性质分三态,各有各的闸:
| 迭代类型 | 入口规则 | 出口标准 |
|---|---|---|
| feature | 先改 spec,补验收标准与用例,再拆计划 | 测试全绿 + 新用例过 + 对照 spec 逐条过 |
| bugfix | 先写复现失败的测试 | 复现测试绿 + 全量无回归 |
| refactor | 全绿前提下改结构,不碰行为 | 行为不变,测试全绿 |
三态的区别在入口。feature 是新增,先动 spec;bugfix 是纠错,先写复现;refactor 是换马不换人,测试兜底。入口定错,后面全乱——把 bugfix 当 feature 做,先改 spec 再写代码,复现用例还没写就开修,改没改对全靠感觉。
把前面讲的归拢一下,这套体系的防线是三层:

第一层写规则,第二层强制规则,第三层拍板规则对不对。前两层都是机器在跑,第三层必须是人——这引到最后一章最关键的坑。
对照 superpowers:收尾也是流程
前两篇对照了 brainstorming、writing-plans、tdd,这篇再看 superpowers 怎么处理收尾——finishing-a-development-branch。名字直白:开发分支做完之后,怎么把它收掉。
这个技能把「收尾」做成了标准流程,六步:
-
验证测试:跑全量测试。红了就停下报告,菜单在绿灯之后才出现。
-
检测环境:判断当前是普通仓库还是 worktree,决定给哪套菜单。
-
确定基线分支:确认这段工作从哪个分支分出来的,合并前先问准。
-
给选项:合并回本地 / push 建 PR / 保留分支,等用户选。
-
执行选择:合并要重新验证合并后的结果,PR 保留 worktree 供后续迭代。
-
清理 worktree:只清理 superpowers 自己建的 worktree,外部的碰都不碰。
最戳我的是它把「决定权」和「清理」分得清清楚楚。丢弃工作只在用户明确说 discard 时才执行,还得敲一个「discard」确认;合并进哪个分支要跟人对齐,合并错基线是昂贵的错误;合并后还要重新跑一遍测试,因为「刚才测过」只对刚才那棵树有效。整个技能把收尾的每一步都定义成可判定的动作,而不是「差不多就行」。
这跟我在验收章说的是一件事:收尾是流程的最后一道闸,不能含糊。superpowers 把它做成菜单,是产品化——把「要不要合并」「要不要清理」变成显式选项,把「丢弃」锁死在用户明确同意之后。我的建议:收尾动作自己写一份 runbook 或一个命令,把「全绿→合并→验证→清理」固化成步骤,别每次凭感觉收。
常见坑与适用边界:这套东西不是万能的
先讲最深的那个坑:全绿全错。
AI 写 spec、AI 写测试、AI 写代码——三个环节同一个模型,共享同一套盲区。它把 spec 理解偏了,测试就照偏的 spec 写,代码照着偏的测试做,三样东西自洽地绿着,但整个系统是错的。这就是 oracle 问题:没有独立于「被测对象」的真相来源,测试就证明不了任何东西。

破法一句话:人必须至少持有验收测试。spec 里的验收标准(AC),人可以自己写关键的几条,或者至少逐条审过再让 AI 转成测试。你不能把 spec、测试、代码三样全外包——那样外包的其实是判断。全绿 ≠ 没错,验收闸必须是人拿着的。
第二个坑,越精美越危险。Anthropic 2026 年 2 月发布的 AI Fluency Index 戳破了一个反直觉现象:当 Claude 产出很精美的成品时,人反而更不挑毛病了。报告抽样了 9,830 段对话:澄清目标 +14.7pp、指定格式 +14.5pp,而识别缺失上下文 −5.2pp、事实核查 −3.7pp、质疑推理 −3.1pp——给的指令更细了,挑的毛病却更少了。研究管这叫 Artifact Paradox:成品越像样,越容易默认它对。
报告里还有个数字跟「全绿全错」直接相关:只有 30% 的对话明确设了协作条款(「我假设错了要推回我」这类),剩下 70% 把 AI 当执行器。而迭代型用户质疑模型推理的可能性,是前者的 5.6 倍。换句话说,大多数人不是不会挑错,是默认不挑;而主动迭代、主动质疑的那一小撮人,恰恰是发现问题最多的人。落到验收上就一条:把第一版成品当草稿看,主动找茬——这正是「验收闸得人拿」的心理机制。
第三个问题:这套不是所有项目都该上。得物的 Spec Coding 复盘给了个很诚实的边界判断。它自己跑得很成功——10 天做出一个企业级中后台,净增 25,546 行,提效 36%,全程 217 条人工指令、2,754 次工具调用——但复盘里明确写了 ROI 不固定:大团队规范成本可分摊,收益大于成本;小团队规范成本可能大于收益。简单明确的任务没必要上规范;复杂模糊的任务解释成本太高。
我的版本更粗暴:原型、一次性脚本、快速实验,直接让 Claude 随手写,别套这套。固化层是给「要长期迭代的复杂系统」用的,不是给「跑一次就丢的脚本」用的。什么时候该上?项目开始有人依赖它、你开始怕改它的时候。
结论:流程比提示词重要
三篇讲完,收个尾。
系列第一篇讲「第一步不是写代码,是写规格」——把心里想要什么,变成人和 AI 都不误读的文档。第二篇讲「拆计划 + TDD」——让代码被测试牵着走,不靠模型自觉。这篇讲「固化 + 验收」——把前两篇的纪律写进工具,让流程不靠人肉盯也能跑。
三篇共享同一条主线,压成一句:流程比提示词重要。提示词是每次对话都重新写一遍的临时约定,流程是项目里一次写好、长期生效的默认行为。前者靠你说,后者靠工具记住。一个好的提示词能让 AI 这一次干对,一套好流程能让 AI 每次都按规矩干。把 AI 从「代码生成器」变成「按纪律执行的工程师」,差别不在模型,在流程有没有被固化、有没有被验收。
回到开篇那句话:把纪律写进人脑,不如写进工具。AI 不记得你说过什么,但 hook 和 CLAUDE.md 记得——只要你有勇气把流程写下来、把底线焊进去、把验收拿在自己手里。spec 问「要什么」,测试证明「做对了」,人拍板「这就是我想要的」——这套脊柱和两道闸,从第一篇立到这一篇,现在有了固化层,能自己转了。
系列三篇到这里收官。这类 AI 工程化实战后面我还会聊——多代理协作怎么分工、把一次工作流固化成团队约定,都是这些天的真问题。关注不迷路,下一篇见。
你打算怎么把整套流程用在自己项目上?还是你已经踩过「全绿全错」的坑——测试全过了,线上炸了?评论区说说,我想看差在哪一步。想让下一篇展开哪块的,也评论区告诉我:多代理协作的编排、固化层的成本怎么算、spec 拆多细,你问的我会记下来。
留一句掏心窝的。AI 写得越来越快,人越来越容易被成品说服——这是 Artifact Paradox 说的。所以整套流程里最不能外包的,不是写代码,是最后那道「这真的是我要的吗」的判断。工具能替你记住纪律,替你不了判断。
系列文章:
更多推荐


所有评论(0)