CHAPTER 01 — 04
与 Agent 一起做工程:
计划、执行、验证的证据闭环
让 Agent 写代码并不难。难的是让它在不猜需求的前提下,持续拿到可信证据,判断自己是否做对,并在失败后恢复。

一条主线:有边界的证据闭环
Agent 的工作不应被定义为"不问人",而应被定义为:在证据充分时独立推进;否则准确升级。
- 第一章 打地基:边界与反馈设施,让证据可获得。
- 第二章 把模糊需求变成可执行、可评审、可验证的规格。
- 第三章 小步推进,控制变更半径,失败可恢复。
- 第四章 用证据收敛,而不是口头宣布完成。

目录
构建 Agent-Friendly 的工程基础
安全护栏 · 入场路径 · 命令契约 · 验证阶梯 · 规则文件 · 环境模式 · CI 同构 · 外部系统接入 · 遗留改造
计划:从需求到可执行方案
何时计划 · 头脑风暴 · 苏格拉底式提问 · 任务输入 · 事实发现 · BDD Spec · 多视角评审 · 修改计划
执行:小步推进,控制变更半径
任务 DAG · 串行与并行 · 子代理分工 · TDD · 小步修改 · 长任务与上下文 · 失败处理
验证:用证据收敛,而不是口头完成
证据基线 · BDD 回放 · 证据矩阵 · 验证手段 · 四级强度 · 回归阶梯 · 反向评审 · 交付格式
CHAPTER 01
构建 Agent-Friendly 的工程基础
Agent-friendly 不是某个工具、某份规则文件或某套容器方案的属性,而是仓库、执行环境和工程约定共同形成的工程能力:Agent 能发现正确入口,通过稳定命令执行任务,获得可行动的反馈,并在意图或证据不足时及时升级给人。
本章搭建这层工程基础;后三章沿"计划 → 执行 → 验证"的闭环,用它完成一次完整的开发任务。

学完本章,你应该能够
- 用证据审计一个仓库的 Agent-friendly 程度,而不是凭"Agent 好像能跑"判断。
- 识别开发闭环中缺失的环节:发现、执行、观察、验证、恢复或升级。
- 为项目设计最小可用的命令、反馈、环境和 CI 契约。
- 区分工具偏好与工程原则,把同一原则迁移到不同语言和平台。
- 为破坏性操作建立机械护栏,而不是依赖提示词提醒。
边界:本章不承诺 Agent 能独立判断业务是否正确。工程只能提供验证业务意图的手段,不能替代清晰需求、领域判断和责任人批准。
原则可迁移:示例用 Node.js / TS,结论不依赖语言
| 通用能力 | Node / TS | Python | Go | Rust |
|---|---|---|---|---|
| 固定工具链 | packageManager + lockfile | pyproject.toml + lockfile | go.mod / go.sum | rust-toolchain.toml + Cargo.lock |
| 快速静态检查 | tsc --noEmit | Ruff、Pyright | go vet、go build | cargo check、Clippy |
| 定向测试 | Vitest / Jest | pytest | go test | cargo test |
| 稳定任务入口 | package scripts / Make | Make / tox / nox | Make / Task | Cargo / Just |
闭环的七个环节
| 环节 | 含义 |
|---|---|
| 发现 | 知道项目入口、目录结构、命令和约束在哪里 |
| 计划 | 识别将修改的范围、风险和验证方式 |
| 执行 | 通过稳定、非交互的入口完成开发操作 |
| 观察 | 获得结构清晰、位置明确的输出 |
| 验证 | 用与任务匹配的证据判断结果,而不只看退出码为 0 |
| 恢复 | 失败后可以重试、回滚或缩小问题范围 |
| 升级 | 意图、安全或验收依据不足时,停止猜测并询问责任人 |
为什么退出码不是证据:训练期研究已观察到模型会用 sys.exit(0) 伪造测试全绿。

工程建设的三个层级,顺序不能颠倒
- 安全护栏:凭据隔离、破坏性操作的机械防护、可逆性。
- 证据闭环:操作可发现、可执行、可观察、可验证、可恢复。
- 效率优化:降低反馈延迟、上下文消耗和重复工作。
CLI、MCP、LSP、容器和缓存都属于实现手段。不能为了更快而绕过护栏,也不能用更多工具掩盖验收标准缺失。

三件必须做的实事
- 凭据隔离:开发、测试、生产凭据分离,Agent 默认拿不到生产写权限;不把 SSH key、云凭据、浏览器配置和 Docker socket 无条件暴露给执行环境。
- 破坏性命令的机械护栏:脚本本身拒绝危险目标,而不是靠规则文件写一句"不要在生产运行"。
- 危险操作留人工批准点:外部写操作提供 dry-run 或变更预览;push、merge、deploy、改写共享历史留给人确认。
规则文件只能提醒,脚本校验和 CI 才能形成真正的控制。
示例:db:reset 的机械护栏
#!/usr/bin/env bash
set -euo pipefail
case "${DATABASE_HOST:-}" in
localhost|127.0.0.1|db) ;;
*)
echo "refusing to reset a non-local database" >&2
exit 1
;;
esac
test "${APP_ENV:-}" = "development" || {
echo "APP_ENV must be development" >&2
exit 1
}
pnpm exec prisma migrate reset --force
Golden Path:从零到可验证状态
三个职责不同的入口
| 入口 | 职责 |
|---|---|
doctor | 只读检查,报告缺少的工具、端口、配置和服务状态 |
bootstrap | 幂等初始化工具链、依赖和开发数据 |
dev | 启动应用,不偷偷执行破坏性迁移或重置 |

Node.js 项目至少固定这五件事
- Node 版本:
.nvmrc、Volta、asdf 或等价机制。 - 包管理器版本:
package.json#packageManager。 - 依赖解析结果:提交 lockfile,CI 使用 frozen install。
- 配置契约:提交
.env.example,启动命令显式加载环境变量。 - 数据初始化:迁移和 seed 可重复执行,失败信息明确。
要求不是"任意顺序都成功",而是依赖关系显式、初始化幂等、重复执行可预测。不要让 test 暗中重置共享数据库,也不要让 dev 隐式修改不可恢复的状态。
示例入场命令
pnpm run doctor pnpm install --frozen-lockfile docker compose up -d --wait pnpm run db:migrate pnpm run db:seed pnpm run check:quick pnpm run dev
一组位于根目录、本地与 CI 共用的任务入口
| 能力 | 建议入口 | 契约 |
|---|---|---|
| 环境诊断 | doctor | 只读,指出缺失项和修复方式 |
| 初始化 | bootstrap | 幂等,可重复执行 |
| 开发 | dev | 一条命令启动,能正常停止 |
| 快速检查 | check:quick | 面向编辑循环,快速且无外部副作用 |
| 定向测试 | test -- <target> | 参数可透传,能指定文件或用例 |
| 完整验证 | check | 覆盖提交前必须通过的检查 |
| 构建 | build | 产物可重复,失败可定位 |
| 数据准备 | db:migrate / db:seed | 目标环境明确,seed 幂等 |
Node.js / TypeScript 示例
pnpm run dev pnpm run build pnpm run typecheck pnpm run test -- src/services/PricingService.test.ts pnpm run test -- -t "rejects expired codes" pnpm run lint pnpm run lint:fix pnpm run check
命令名称不必跨团队统一,但同一仓库必须只有一套权威入口。Makefile、Justfile、Taskfile、package scripts 都可以实现这份契约。
命令设计的七条要求
- 从项目根可发现:不要求 Agent 猜先进哪个子目录。
- 支持非交互模式:CI 和 Agent 不会无限等待输入。
- 退出码可信:成功为 0,失败为非 0,不吞掉子进程错误。
- 输出可行动:指出失败文件、位置、期望值、实际值和最小复现命令。
- 参数可透传,支持单文件级运行:file-scoped 反馈回路比全项目扫描快一个量级,直接决定 Agent 的迭代速度。
- 副作用显式:普通检查不暗中安装依赖、修改共享数据或访问生产系统。
- 本地与 CI 共用:CI 调用项目命令,不复制另一套隐藏脚本。

与任务风险匹配的验证阶梯
| 层级 | 目的 | 常见检查 |
|---|---|---|
| 编辑循环 | 数秒内发现局部错误 | 格式化、静态检查、单文件测试 |
| 任务验证 | 证明改动满足局部目标 | 定向单元测试、契约测试、局部集成测试 |
| 提交前验证 | 发现跨模块影响 | 构建、相关测试集、迁移检查 |
| PR 门禁 | 保护主分支 | 全量检查、集成测试、安全扫描 |
| 定期验证 | 覆盖高成本场景 | 全量 e2e、性能、兼容性、长时间测试 |
速度目标按仓库规模、硬件和冷热缓存建基线。可以给编辑循环设激进预算,但不要为了"一分钟"指标删除必要测试——优化反馈路径,不牺牲覆盖范围。

确定性 = 受控、可记录、可重放
确定性不等于禁止时间、随机数和网络:
- 时间通过 fake clock 或固定时区控制。
- 随机测试记录 seed,失败可复现。
- 单元测试不访问真实第三方服务。
- 集成测试可以访问隔离的本地数据库和 Compose 网络。
- e2e 使用受控环境,保存截图、trace、日志等失败产物。
好的失败输出直接支持下一步行动
FAIL tests/users.test.ts:42 Expected: HTTP 409 with code USER_EXISTS Received: HTTP 500 with code INTERNAL_ERROR Reproduce: pnpm test -- tests/users.test.ts \ -t "rejects duplicate email" Artifact: artifacts/users-duplicate/server.log
避免只输出"测试失败",或把真正错误吞进数千行日志。Agent 能否恢复,取决于反馈是否包含定位、差异、复现方式和相关产物。
写 CLAUDE.md / AGENTS.md:一条官方判据,两条实践检验法
对每一行问"删掉这行会不会导致 Agent 出错?"不会就删。过长的指令文件会让重要指令淹没在噪音中——写得越多,遵循得越差。
让 Agent 逐字复述你的构建命令。凡是它复述不出的内容,它就不会遵循。"be careful""where possible"这类模糊指令会被可靠地忽略。
每条指令尽量回答"什么命令能证明这件事做对了"。Agent 可以通过退出码验证命令,却无法验证一段描述性文字。
维护方式建议被动维护:Agent 每犯一次错,就追加一行针对性规则防止复发;定期用逐行判据清理。这比一次性写"大而全"的文档有效得多。

约束放在哪里:四步判断顺序
权威任务入口和推荐验证顺序 · 偏离默认约定的风格与测试指令 · 无法由工具判断的业务/历史背景 · 哪些操作需要批准、哪些资源禁止访问 · 典型实现的文件指针
能由 formatter、Linter、类型系统机械检查的规则 · 通用 git / 语法教程 · 频繁变化无人维护的文件清单 · 仅靠提示词无法真正阻止的安全要求
- 能否通过脚本或系统机制直接禁止?
- 能否通过 CI、测试或 Linter 自动检查?
- 能否通过生成器或脚手架减少错误路径?
- 前三者都不适合时,再写进项目指令。
例:"不能修改既有迁移"不应只写进规则或 pre-commit hook。关键约束应在 CI 中与目标分支比较,并提供有审批记录的例外流程——pre-commit 只能提供更早反馈,不能保护主分支。
四个目标,四种模式,按条件选择
Agent-friendly 环境的四个目标:工具链可固定 · 环境可从零重建 · 测试与多 checkout 相互隔离 · 反馈延迟可接受。
| 模式 | 优势 | 主要风险 | 适用条件 |
|---|---|---|---|
| 工程在 host,依赖在容器 | 本地 IO 快、调试直接 | 主机凭据暴露、跨平台漂移 | 可信仓库、受控开发机 |
| Dev Container | 环境一致、隔离较好 | 文件 IO、镜像维护成本 | 跨平台团队、统一工具链 |
| 一次性 VM / 云端沙箱 | 隔离最强、易清理 | 启动和资源成本 | 不可信代码、高自主度任务 |
| Nix / 声明式环境 | 复现性强 | 学习和维护成本 | 已有相关基础设施的团队 |
并行 checkout:隔离 Compose 项目与端口
export COMPOSE_PROJECT_NAME="app-$(basename "$PWD")" export POSTGRES_PORT=55432 export REDIS_PORT=56379 docker compose up -d --wait
依赖容器要有 healthcheck 与 --wait;真实项目固定补丁版本或 digest;开发密码不得复用到共享或生产环境;测试数据用临时数据库或独立 schema,不与开发环境共用持久状态。
CI 保护主分支,并与本地共用同一套入口
| 门禁 | 是否阻塞 | 说明 |
|---|---|---|
| 冻结依赖安装 | 是 | lockfile 不一致或安装失败即阻塞 |
| 静态检查和 Linter | 是 | warning 是否阻塞由项目明确规定 |
| 构建 / 打包 | 是 | 防止类型通过但产物失败 |
| 单元与集成测试 | 是 | 按项目风险决定覆盖范围 |
| 迁移和结构检查 | 涉及相关变更时 | 验证升级、回滚或兼容性 |
| secret / 依赖 / 许可证 | 按组织策略 | 确定性工具 + 明确基线 |
| e2e、性能、兼容性 | 按成本分层 | PR、合并队列或定时任务 |
| LLM Review | 默认建议性 | 发现疑点,不作为正确性证明 |
健康的 CI 还应明确
- 使用哪个运行时和 lockfile 安装方式。
- 服务依赖如何启动并等待 ready。
- 超时、重试、缓存和并发取消策略。
- 失败日志、截图、trace 和产物保存在哪里。
- 哪些检查可豁免、谁能批准、记录在哪里。
敏感目录用 CODEOWNERS、人工审批和针对性安全检查。LLM Review 可以要求"必须运行并处理意见",但不能把概率性输出当作正确性批准。
没有天然更优的协议,只有更合适的通路
| 维度 | CLI | 直接 API / SDK | MCP |
|---|---|---|---|
| 可组合性 | shell 管道强 | 需要代码编排 | 取决于客户端与工具设计 |
| 结构化输出 | 取决于 --json 等能力 | 通常最好 | 通常为结构化结果 |
| 状态保持 | 取决于具体工具 | 可自行设计 | 适合封装有状态能力 |
| 上下文成本 | 可控,但日志可能很大 | 由适配层控制 | 取决于发现与加载机制 |
| 审计 | 依赖凭据和 shell 环境 | 可精细设计 | 可由服务端集中治理 |
| 部署成本 | 安装、认证、升级 | 维护集成代码 | 本地或远程服务运维 |
选择顺序:只读还是有副作用?→ 能否最小权限 + 审计?→ 是否需要结构化结果或长状态?→ 上下文消耗多少?→ 团队能否稳定维护?
实践原则
- 能用脚本就不用 MCP:脚本可版本管理、可测试、不额外占上下文;常见例外只有浏览器自动化这类有状态会话。
- 统一日志:控制台、服务器、浏览器日志写到同一位置,给 Agent 一个"读最近 N 行"的简单命令。
- 查询优先结构化输出,在工具侧过滤,不把整份日志塞入上下文。
- 数据库"只读"由专用账号和权限保证,名称本身不提供只读性。
- 批量写操作先 dry-run,再逐项执行并保留结果。
先查硬门槛,再按阻塞程度投资
| 层级 | 内容 |
|---|---|
| P0 安全与可运行 | 无生产写权限与无关凭据 · 干净环境可安装启动 · 至少一个可信验证入口 · 破坏性命令有机械护栏。P0 未通过,不让 Agent 自主开发新功能。 |
| P1 快速证据闭环 | 定向测试与快速静态检查 · 失败输出可定位可复现 · 测试状态隔离 · 本地与 CI 同一组入口 |
| P2 知识与护栏 | 项目指令说明命令/结构/边界/批准点 · 高频错误由 Linter/测试/CI 自动阻止 · 规则有作用域与负责人 |
| P3 环境与外部接入 | 环境可重建、支持并行 checkout · 外部工具最小权限、可审计 |
| P4 效率 | 缓存、增量、并行 · LLM Review、自动分诊 · 基于真实遥测优化成本 |
证据分级与排序依据
- 0:不存在。
- 1:存在,但依赖人工步骤、偶尔失败或缺少证明。
- 2:可重复执行,有命令输出、CI 记录或测试结果作证据。
改造优先级 = 风险 × 发生频率 × 阻塞时间。不要用未经测量的"必然提速十倍",记录改造前后的首次启动时间、定向测试延迟、失败恢复时间和 CI 通过率。
审计一个故意有缺陷的 starter repo
仓库预埋的缺陷
- 只有全量测试,没有定向测试入口。
- 环境变量缺少示例和启动前检查。
- Compose 没有 readiness 检查,数据库偶发启动失败。
- 开发和测试共用数据库状态。
- 项目指令只写风格偏好,没有命令和边界。
- CI 使用一套与本地不同的隐藏命令。
练习任务
- 运行
pnpm run doctor或现有命令,记录可复现证据。 - 找出证据闭环断点(至少 3 个),按 P0~P4 排序。
- 只修复最阻塞的一项,不做无关重构。
- 浏览朴素基线裸跑记录(
artifacts/chapter-1/baseline/):"支持创建用户"一句需求直接交给 Agent 的完整过程。
裸跑记录是后三章的对照物:第二章复盘它猜了什么,第三章复盘它怎么失控,第四章复盘它的完成声明缺什么证据。裸跑结果不计分——价值恰恰在于暴露问题。
不通过红线
- 使用生产或共享环境写凭据。
- 删除测试、跳过检查让审计结果变绿。
- 没有可复现命令和证据,只报告"已经完成"。
验收量表 7 个维度(安全护栏、入场路径、定向反馈、错误输出、环境隔离、本地与 CI、裸跑记录),每项 0–2 分;总分 ≥11 / 14 且无 0 分项为通过。复盘三问:哪个断点浪费最大?为什么先改这一项?裸跑中哪个行为最意外?
Agent-friendly = 有边界的证据闭环
- 凭据隔离,破坏性命令有机械护栏,危险动作留人工批准点。
- 项目入口可发现,环境可以从零重建。
- 命令稳定,反馈快速且可行动,支持单文件级验证。
- 规则文件只写 Agent 猜不到且删掉会出错的内容,能机械检查的交给工具。
- 本地和 CI 使用同一套工程契约。
- CLI、API、MCP、Host 和 Container 都按条件选择;能用脚本就不用 MCP。
- 意图或证据不足时,Agent 必须正确升级。
这层基础建好以后,后续章节才有条件讨论如何让 Agent 规划需求、执行修改和验证结果。

CHAPTER 02
计划:从需求到可执行方案
让 Agent 直接跳到写代码,可能产出"解决了错误问题"的代码。回看第一章的裸跑记录:拿到"支持创建用户"后,Agent 自行选定了校验规则、猜测了错误码、顺手引入了团队没在用的库。
计划阶段做三件事:把人的意图澄清成任务边界;把项目现状查清楚;把需求固化成可执行、可评审、可验证的规格和计划。

学完本章,你应该能够
- 判断任务需要多厚的计划,以及什么时候不需要计划。
- 把模糊需求改写为 Agent 可执行的任务说明。
- 用苏格拉底式提问把隐含假设、边界和验收标准问出来。
- 在制定实现方案前完成事实发现。
- 把需求整理成 BDD Spec,让业务预期可以被执行和验证。
- 使用子代理从多角度评审计划,降低单一路径偏见。
- 用有据可查的修改与验证计划为后续执行指路。
小到中等规模新功能 · 局部 bug 修复 · 有清晰验收标准的重构 · 测试补齐、接口调整、配置修复等工程任务
多团队大型架构迁移 · 高风险生产变更 · 没有明确责任人的业务决策 · 需要真实用户、合规、财务或法务判断的需求取舍——可复用本章闭环,但需额外的审批、灰度、审计和回滚机制
计划不是免费的:三档投入
| 任务规模 | 判据 | 计划投入 |
|---|---|---|
| 一句话 diff | 一句话能说清改动 | 不计划,直接做;仍走常规验证兜底 |
| 常规任务 | 涉及 2 个以上模块,或有失败路径需要设计 | 走完整闭环,产物保持轻量 |
| 高风险任务 | 涉及数据、兼容性、外部系统,或需求歧义大 | 完整闭环 + 多视角评审 + 人工确认点 |
原则:先处理需求侧的不确定性,再处理工程侧的不确定性,最后再让 Agent 制定执行计划。有疑问按上一档处理;但发现自己在给改错字的任务写 BDD Spec,停下来。
能用一句话描述 diff——改错字、加一行日志、重命名变量——就跳过计划直接做。给这类任务写计划纯属浪费。
spec 驱动工具曾把一个小 bug 修复膨胀成 4 个用户故事、16 条验收标准。"我宁愿审代码,也不愿审这些 markdown"——计划把审阅负担从代码转移到文档,文档比代码还难审时,计划就是负资产。
打开可能性,而不是马上决定实现
适合发散的问题
- 这个需求可能有哪几种业务解释?
- 成功路径、失败路径和边界路径分别是什么?
- 现有系统里有没有相似功能可以复用?
- 可能影响哪些模块?最小可交付版本是什么?
- 哪些部分可以延后,不应混入本次任务?
收敛后的产物
| 产物 | 作用 |
|---|---|
| 候选解释 | 暴露需求歧义 |
| 候选实现路径 | 比较成本和风险 |
| 不做清单 | 防止范围膨胀 |
| 待确认问题 | 明确需要人决策的点 |
让 Agent 每次只问一个问题,连续追问迭代出详细规格,最后固化成文档。人能认真回答的问题一次只有一个,而 Agent 的下一个问题可以基于上一个回答调整方向。
一开始就让 Agent 选定单一路径 · 把所有想法都放进本次实现范围

用连续问题逼近清晰需求
这个需求最终要改变哪个用户可观察行为?只交付最小版本,必须包含什么?
哪些模块允许修改?哪些现有行为必须保持不变?哪些场景明确不在本次范围?
成功路径、失败路径如何判断?需要什么测试、日志、截图或请求记录作为证据?
是否涉及权限、删除、计费、隐私或外部系统?是否允许连接共享环境?现有代码和需求冲突时以谁为准?
目标不是让人回答所有问题,而是把"不知道"显性化。只要某个问题会改变实现或验证方式,就应该在计划阶段暴露出来。
什么时候用
- 用户给的是愿望,不是验收标准。
- 需求里有"合理""默认""正常""优化"等模糊词。
- Agent 准备自行补全业务规则。
- 修改可能影响权限、数据或兼容性。

给 Agent 的任务不应只有一句业务愿望
"帮我加一个用户创建接口。"
新增 POST /users。请求体包含 email 和 name。创建成功返回 201 和用户 id;重复 email 返回 409 和 USER_EXISTS;email 格式非法返回 400 和 INVALID_EMAIL。只修改 API、service、repository 和相关测试,不改认证模块。使用本地测试数据库验证,不能连接共享环境。交付时给出修改文件、测试命令和结果。
| 类型 | 作用 | 示例 |
|---|---|---|
| 目标 | 定义最终行为 | 新增 POST /users |
| 范围 | 限制修改边界 | 不改认证模块 |
| 验收标准 | 定义完成证据 | 成功、重复 email、非法 email 三类测试通过 |
| 禁止事项 | 定义风险边界 | 不连共享环境,不 push,不 merge |
任务输入不需要提前设计所有实现细节。实现路径可以让 Agent 发现和建议,但业务行为、修改边界和验收标准必须尽量明确。
输出必须可引用、可检查,而不是泛泛总结
事实发现应回答
- 入口在哪里?现有类似实现是什么?
- 数据模型、接口约定和错误格式是什么?
- 现有测试如何组织?
- 哪些命令可以验证本次任务?
- 哪些文件或模块属于高风险区域?
"项目结构比较清晰。" · "应该可以加一个 controller。" · "我会参考现有代码风格。" —— 没有可检查证据,不能支撑后续计划。
- 已有
POST /projects实现路径,可复用路由、service、repository 分层。 - 错误响应统一由
AppError转换,错误码在errors.ts维护。 - repository 层已有唯一键冲突处理。
- API 测试使用独立测试数据库。
- 提交前验证
pnpm run check;定向测试pnpm run test -- tests/api/users.test.ts。
Given / When / Then:业务预期的可执行表达
Feature: Create user
Scenario: create user successfully
Given the local test database has no user
with email "a@example.com"
When I submit POST /users with email
"a@example.com" and name "Alice"
Then the response status should be 201
And the response should contain a user id
And the user should be persisted in the
local test database
Scenario: reject duplicate email
Given the database already has a user
with email "a@example.com"
When I submit POST /users with the same email
Then the response status should be 409
And the error code should be "USER_EXISTS"
Scenario: reject invalid email
When I submit POST /users with email "bad-email"
Then the response status should be 400
And the error code should be "INVALID_EMAIL"
计划阶段的三个作用
- 逼出成功路径、失败路径和边界条件。
- 让实现计划围绕可验证行为展开。
- 为执行阶段的 TDD、验证阶段的证据矩阵提供输入。
BDD Spec 不一定要求引入 Cucumber 之类的框架。它首先是一种需求表达方式,其次才是测试工具选择。
写在事实发现之后:这样规格既反映业务预期,也贴合项目已有接口、错误码、数据模型和测试方式。它比自然语言需求更精确,又比直接写测试更容易让业务方 review。
本课的"任务输入 + BDD Spec"是 SDD 的轻量版
| 工具 | 流程固化方式 | 特点 |
|---|---|---|
| GitHub Spec Kit | Specify → Plan → Tasks → Implement 四阶段斜杠命令 | 每阶段内置人工检查点——审查、发现缺口、纠偏后才进入下一阶段 |
| Kiro | 三个文件:requirements.md(用户故事 + EARS 记法验收标准:WHEN/THEN/SHALL)、design.md(架构、数据流、错误处理、测试策略)、tasks.md(带需求编号追溯的任务清单) | spec 即项目资产,可追溯 |
是否采用这类工具,回到 §2 的规模判断:工具的阶段再完整,也救不了与任务规模不匹配的计划。

价值不是"人数更多",而是主动暴露偏见
| 评审视角 | 关注点 |
|---|---|
| 产品评审 | 需求是否完整,用户行为是否明确 |
| 架构评审 | 实现路径是否符合项目结构,是否引入不必要复杂度 |
| 测试评审 | 验收标准是否可测,失败路径是否覆盖 |
| 安全评审 | 是否涉及权限、数据、凭据、外部系统 |
| 运维评审 | 配置、迁移、日志、回滚和环境影响 |
评审对象是任务输入 + 事实发现 + BDD Spec 这组可检查产物,而不是一句模糊需求。没有子代理能力时,也可以要求同一个 Agent 按视角逐项自查。
使用方式
- 主 Agent 给出任务输入、事实发现、BDD Spec 和初步计划。
- 评审者只输出风险、缺口和建议,不直接改代码;只报影响正确性或既定需求的缺口。
- 每个子代理只能访问完成评审所需的上下文。
- 主 Agent 汇总冲突意见,形成最终计划。
- 人只需要 review 关键分歧和高风险点。
典型发现:验收标准不完整 · 错误路径缺失 · 任务触碰权限判断 · Agent 准备绕过现有 service 层。
每一步都要有理由和验证方式
"修改后端、补测试、运行检查。" —— 无法判断风险,也无法定位失败。
合格计划应包括
- 修改哪些文件或模块;每一步为什么必要。
- 每一步之后运行什么验证。
- 哪些地方可能破坏兼容性。
- 如果失败,如何缩小问题范围。
- 是否需要用户确认业务问题。
示例计划
- 阅读现有用户路由、错误格式和测试工具,不修改代码。
- 把 BDD Spec 转成失败的 API 测试。
- 在 service 层补充用户创建逻辑,复用现有唯一键冲突处理。
- 增加
POST /users路由和输入校验。 - 跑定向测试,让失败收敛到当前任务。
- 跑提交前验证入口,检查跨模块影响。
执行中偏离了计划,正确动作是回到计划修改它,而不是在对话里一句句追加纠正——计划是共同的锚点,锚点错了就改锚点。
进入执行前,至少检查
- 计划厚度是否与任务规模匹配。
- 是否经过发散和收敛,而不是直接选定第一个方案。
- 苏格拉底式提问是否暴露了关键假设。
- 任务目标是否可测试;范围和禁止事项是否明确。
- Agent 是否读取了相关现有实现。
- BDD Spec 是否覆盖成功路径和关键失败路径。
- 多视角评审是否发现计划盲点。
- 修改计划是否具体到模块或文件;每一步是否有验证方式。
- 是否存在需要用户确认的业务问题。
- 如果计划失败,是否知道从哪里开始缩小问题。
计划产物不是为了占用时间,而是为了降低返工成本——如果某份产物既不降低返工也不帮助评审,砍掉它。

从"支持创建用户"到完整计划
核心必做
- 规模判断(口头,1 分钟内):属于三档中哪一档,为什么。
- 快速发散(5 分钟上限):候选解释 + 不做清单,随即用苏格拉底式提问补齐目标、边界、验收标准和风险;可采用"每次只问一个问题"。
- 把需求改写成完整任务输入。
- 只做事实发现,不修改代码,给出至少 3 条可引用结论。
- 写 BDD Spec:至少覆盖成功、重复 email、非法 email。
- 形成最终修改计划和验证计划。
进阶(课后):子代理或单 Agent 多视角自查。复盘:裸跑中哪次猜测(错误码、校验规则、依赖选择)会被本章哪个环节拦住?产物存入 artifacts/chapter-2/。
练习环境卡(讲师课前提供)
| 技术栈 | Node.js / TypeScript / PostgreSQL |
| 安装 | pnpm install --frozen-lockfile |
| 定向测试 | pnpm run test -- tests/api/users.test.ts |
| 提交前验证 | pnpm run check |
| 相似实现 | POST /projects |
| 任务约束 | 不 push、不 merge、不连共享环境、不改认证模块 |
对业务歧义自行猜测 · 未明确环境和禁止事项 · BDD Spec 无法支撑后续验证
好的计划让执行和验证高效清晰
- 规模匹配:一句话 diff 不计划,高风险任务厚计划;计划产物本身有成本。
- 发散再收敛:暴露可能性,最后明确边界。
- 问出隐含假设:把"以为"变成"确认";每次只问一个问题。
- 事实先行:读代码,找相似实现,再定方案。
- 可执行的规格:BDD Spec 既是业务预期,也是验收标准。
- 多视角评审:动手前让产品、测试、安全、架构都发言。
- 计划是锚点:执行偏离时改计划,而不是追加纠正。

CHAPTER 03
执行:小步推进,控制变更半径
两个有实证的失败模式:Agent 有强烈的"一次做太多"倾向——同时铺开多个功能,每个都半途而废;失败轨迹高度集中于"重复生成修复"的循环——连续产出修复却不运行测试。
这两类失败都不是靠"更强的模型"解决的,而是靠执行纪律:先确定依赖,再分配工作,最后执行修改——每一步都小、都有反馈。

学完本章,你应该能够
- 用有向无环图表达任务依赖,区分串行和并行任务。
- 判断什么时候需要子代理分工,什么时候单个代理足够。
- 用 TDD 建立可解释的反馈锚点。
- 用小步修改控制变更半径,使失败定位更容易。
- 用进度文件和上下文管理支撑跨会话的长任务。
- 在执行中失败时先定位,而不是连续大改。
- 保留关键命令和错误输出,为后续验证和交付提供证据。
本章假设计划已完成:任务输入、事实发现、BDD Spec、修改计划都已明确。
任务约束(不 push、不连共享环境、不修改认证模块)沿用计划阶段的任务输入,执行中不得自行放宽;边界发生变化时,暂停并升级给人。
超过三五步的任务,用 DAG 替代线性待办
- 节点表示任务,边表示依赖。
- 没有入边的节点可以先做。
- 没有互相依赖的节点可以并行。
- 出现环,说明计划里有未解决的设计问题。
TDD 的 red-green-refactor 是节点内部的微循环,不是 DAG 里的依赖环。DAG 仍然表示任务之间的单向依赖。DAG 的价值不是画图,而是让"为什么这一步现在能做"变得可检查。
一个可执行的节点至少包含
| 字段 | 作用 |
|---|---|
| owner | 谁负责该节点 |
| input / output | 依赖的上游产物 / 预期产物 |
| read set / write set | 需要读取的范围 / 允许修改的范围 |
| external state | 是否涉及数据库、队列、缓存、浏览器 profile、外部服务 |
| validation | 完成该节点后运行什么验证 |
| status / blocked reason | pending / running / blocked / done;卡住时缺什么 |
示例:POST /users 的执行 DAG
A 写 API 测试 (input: BDD Spec) B service 实现 (input: BDD Spec、事实发现) C route 接入 (input: B) D 文档更新 (input: 任务输入) E 定向验证 (input: A、C) F 提交前验证 (input: D、E) A → E B → C C → E D → F E → F
计划阶段的产物——任务输入、事实发现、BDD Spec——是所有节点的公共输入,不再作为节点出现。A 与 B 可并行,D 与主链可并行;E 必须等 A、C 齐备。

这些情况应该串行
- 需求尚未澄清,后续实现依赖业务决策。
- 数据模型未确定,接口和测试都依赖它。
- 一个步骤的输出会改变下一个步骤的输入。
- 多个步骤会修改同一文件或同一模块。
- 验证结果会决定下一步是否继续。
串行执行示例
串行的好处是反馈清晰,代价是速度较慢。对高风险或依赖强的任务,清晰度比速度更重要。

并行的三个前提与共享状态清单
并行前需满足
- 输入已经足够明确。
- 输出之间没有强依赖。
- 不会同时写同一文件或同一状态。
适合并行的例子
- 一个子代理做只读事实发现,另一个整理测试现状。
- 一个评审安全风险,另一个评审测试覆盖。
- 文档更新与不重叠文件的测试补齐。
并行的收益来自减少等待,不来自放弃协调。多个会话并行时,用 git worktree 隔离各自工作区。
共享写入不只指同一个文件
| 共享对象 | 典型风险 |
|---|---|
| API contract / schema | 测试和实现基于不同接口 |
| migration 链 | 顺序冲突、回滚失败 |
| fixture / seed / 测试数据库 | 数据污染、重复运行失败 |
| lockfile / 依赖配置 | 安装结果不可预测 |
| generated client / snapshot | 生成物覆盖、语义冲突 |
| 环境变量 / 配置文件 | 本地和 CI 行为不一致 |
| 缓存、队列、外部服务状态 | 验证结果不可复现 |
执行型子代理必须有明确边界
| 子代理 | 适合任务 | 边界 |
|---|---|---|
| 测试代理 | 根据 Spec 写测试、补回归 | 不改生产代码 |
| 实现代理 | 在既定范围内实现功能 | 不改验收标准 |
| 审查代理 | 检查 diff、找风险 | 不直接修复,先报告 |
| 文档代理 | 更新本次任务相关文档 | 不改变业务逻辑 |
| 迁移代理 | 处理 schema 或配置变更 | 必须有回滚和验证计划 |
审查代理的任务包必须写明:只报影响正确性或既定需求的缺口。被要求找问题的评审者总会找出问题——不加边界,评审产出会淹没在风格意见里,追逐每条发现只会导致过度工程。
两个被实践验证有效的分工模式
实现和评审分别使用独立会话。新鲜上下文的评审者不会偏袒"自己刚写的代码",评审质量显著更高。
对一批同构任务(如逐文件迁移),先在 2-3 个文件上试跑,根据出错情况修正任务说明,再对全量执行。
主 Agent 是 integration owner
子代理任务包至少包含
任务目标: Spec / DAG 版本: 可读范围: 可写范围: 禁止事项: 依赖节点: 预期产物: 必跑验证: 停止并上报条件:
子代理不能主导任务,只能在授权任务包内产出局部结果。分工的核心不是"并发越多越好",而是把不同类型的工作隔离,降低互相污染。
主 Agent 的编排职责
- 明确每个子代理的输入和输出;避免两个子代理写同一文件。
- 先检查文本冲突,再检查语义冲突、测试预期冲突、schema/API 契约冲突。
- 合并后运行受影响测试,并更新 DAG 状态。
- 某个子代理改了接口、schema、fixture 或测试约定,其他相关子代理必须刷新上下文。
- 对高风险建议做人工升级;保留关键决策理由。
未被执行过的代码,只能靠运气正确
Agent 执行的四步操作法
- 先运行 baseline,区分既有失败和本次失败。
- 从 BDD Spec 选一个最小场景写测试,明确声明在做 TDD(否则 Agent 可能顺手把实现也写了)。
- 运行定向测试,确认失败且失败原因符合预期(red)。
- 确认 red 后再做最小实现,明确指示不许修改测试;迭代到 green,再处理下一个场景。
这个过程防止 Agent 同时改实现、改测试、改配置,最后不知道哪个动作让结果变绿。测试本身需要调整时,必须作为 Spec 或测试设计变更单独说明。
TDD 尤其适合
- bug 修复:先写复现测试。
- API 行为变更:先写接口测试。
- 重构:先补行为保护测试。
- 边界规则:先写失败路径测试。

"每次只做一个 feature"是被实测验证的关键约束
| 任务类型 | 建议分段 |
|---|---|
| 新接口 | Spec → 测试 → route → service → repository → 验证 |
| Bug 修复 | 复现测试 → 最小修复 → 回归验证 |
| 重构 | 行为保护测试 → 机械迁移 → 删除旧路径 |
| 配置修复 | 复现环境 → 最小配置变更 → 启动验证 |
每一步都能解释
- 为什么现在做这一步;预期改变什么。
- 这一步不应该改变什么;做完后如何获得反馈。
把需求维护成一份 feature 清单(每项带验收标准和 pass/fail 状态),Agent 只被允许修改状态字段,不允许删除需求或修改验收标准。这是"实现代理不能改验收标准"的机器护栏版本——不靠提示词自觉,靠数据结构约束。
每完成一步,用描述性 commit message 提交。清晰的提交历史让 Agent(和人)能够回滚糟糕的变更、恢复到可工作状态。
上下文腐化是真实的失败模式
窗口填满后 Agent 会"遗忘"早期指令、犯更多错误;新会话对之前的工作一无所知,甚至看到已有进展就直接宣布任务完成。
| 技术 | 适用 | 具体做法 |
|---|---|---|
| 压缩 compaction | 大量来回对话的任务 | 接近窗口上限时压缩历史,保留架构决策、未解决 bug 和实现细节,丢弃冗余工具输出 |
| 结构化笔记 | 有明确里程碑的迭代开发 | 进度持久化到窗口之外:NOTES.md、todo 清单、进度文件 |
| 子代理隔离 | 可并行探索的复杂任务 | 调研和大范围搜索交给子代理,只回报摘要,不污染主上下文 |
跨会话恢复的最小机制
- 维护进度文件(如
PROGRESS.md):已完成节点、当前节点、未解决问题、下一步。 - 新会话先读 git log 和进度文件,恢复状态后再动手。
- 会话结束前更新进度文件——给"下一个自己"的交接。
同一问题纠正超过两次就不要再纠正:重开会话,用吸收了教训的更好任务说明重来——反复纠正的对话堆满失败尝试,持续干扰后续判断。不相关的任务之间清空上下文:上一个任务的细节对下一个任务是噪声。
失败很正常,关键是失败后的动作
- 保留失败命令和错误输出。
- 判断失败属于实现、测试、环境还是需求问题。
- 缩小复现范围。
- 做最小修复。
- 重新运行对应验证。
删除失败测试 · 放宽断言 · 扩大 mock 让真实路径不再被测试 · 跳过检查 · 不解释原因就连续大改
实证研究归纳的失败反模式与这份清单逐条对应:重复相同动作却不跟进结果、连续生成修复而不运行测试、未经测试验证就终止任务。失败轨迹的共同特征是循环而不定位;成功轨迹每次失败后都先解释再行动。
测试变绿不是唯一目标。更重要的是确认变绿的方式是否仍然证明需求成立。
执行中持续检查
- 是否仍在原定范围内;DAG 中是否存在未解决的环。
- 串行任务按依赖顺序推进;并行任务避免共享写入。
- 子代理有清晰边界,没有互相覆盖。
- 先建立可解释反馈,再推进实现。
- 每次修改都有明确目的,每步都有提交。
- 长任务有进度文件与恢复路径。
- 优先复用现有模式;避免无关重构、格式化和依赖升级。
- 失败后先定位再修复;保留关键命令和错误输出。
- 风险变化时暂停升级。
执行阶段的核心能力不是"写代码",而是控制变更半径和依赖顺序。

从计划到 TDD 执行
核心必做
- 把计划整理成任务 DAG(白板或文本),标出串行和可并行任务。
- 从一个 BDD 场景开始 TDD:先写失败测试,再做最小实现,过程中不许改测试。
- 每个关键步骤后运行定向反馈(
pnpm run test -- <target>),并做一次描述性提交。 - 维护轻量进度文件(已完成、进行中、下一步各一行)。
- 避免无关重构、格式化和依赖升级;失败时保留错误输出,先定位再修复。
进阶(课后):子代理分工,审查子代理的任务说明必须包含"只报影响正确性或既定需求的缺口"。"新会话恢复"课后自行练习。产物存入 artifacts/chapter-3/。
复盘
对照裸跑记录:哪次失控(一次改太多、失败后盲目重试、改了测试来过关)会被本章哪个方法拦住?
未经计划就开始修改 · 忽视 DAG 任意并行造成冲突 · 删除测试或放宽断言绕过失败 · 超范围大规模重构 · 没有记录失败和修复过程
验收量表:执行控制、失败处理两类,各 0–2 分;总分 ≥3 且无 0 分项为通过。
执行 = 在计划的指导下有序推进
- DAG 梳理依赖:先搞清楚什么必须先做,什么可以并行。
- 串行和并行边界:依赖强就串行,无共享写入才并行。
- 子代理有边界:任务包明确可写范围,评审者只报影响验收的缺口。
- TDD 建立反馈:每个场景先红再绿,实现时不许改测试。
- 每次只做一个 feature:小步推进加描述性提交,失败随时可回滚。
- 长任务靠外部记忆:进度文件加 git log,新会话先恢复再动手。
- 先定位再修复:失败不可怕,循环和掩盖才可怕。

CHAPTER 04
验证:用证据收敛,而不是口头完成
Thoughtworks 实测:即使明确指令要求测试必须通过,AI 仍会频繁谎报构建和测试成功。训练期研究观察到的作弊手法:sys.exit(0) 伪造全绿、把 verify 改成恒返回 true、在 conftest.py 里跳过全部测试。
结论:干活的 Agent 不能给自己打分。验证必须外部化、证据化——不是"跑测试",而是建立证据和结论之间的对应关系。进入本章时功能开发已停止:只允许针对失败做最小修复,不允许借验证之名继续加功能。

学完本章,你应该能够
- 逐条回放 BDD Spec,不合并结论。
- 将每条验收标准映射到证据矩阵,并区分证据强弱。
- 为每条验收标准选择合适的验证手段。
- 为任务选定匹配风险的验证强度等级。
- 按回归阶梯收敛:从 BDD 回放和定向测试逐步扩展到提交前验证。
- 用反向评审找完成声明中的漏洞。
- 明确未验证内容和遗留风险,而不是隐瞒或假装完成。
本章假设计划和执行已完成,现在需要验证改动是否真的符合需求。上一章交来:按计划完成的修改、TDD 过程记录和关键命令输出。本章产出:每条验收标准的证据、明确的未验证清单,以及一份可审计的交付。
证据的环境或来源说不清楚,完成声明不成立
- 本地隔离环境中的命令输出。
- 自动化测试结果、数据库断言。
- 脱敏日志和网络请求记录。
- 浏览器 trace、截图或 video。
- CI 运行结果。
- 无法复现的环境。
- 含生产数据、个人信息、密钥或真实账号的记录。
- 只有口头描述,没有命令和环境说明。
- 来自共享或生产环境的数据。
每个场景单独出结果
| BDD 场景 | 验证动作 | 通过证据 |
|---|---|---|
| create user successfully | 运行成功路径 API 测试 | 状态码 201、返回 user id、数据库断言 |
| reject duplicate email | 运行重复 email 测试 | 状态码 409、错误码 USER_EXISTS |
| reject invalid email | 运行非法 email 测试 | 状态码 400、错误码 INVALID_EMAIL |
不要把多个场景合成一句"用户创建测试通过"。某个场景没有验证,就必须单独标记为未验证。

每条验收标准都应该有对应证据
| 验收标准 | 对应证据 |
|---|---|
| 创建成功返回 201 | 成功路径 API 测试或请求记录 |
重复 email 返回 409 + USER_EXISTS | 冲突路径测试 |
非法 email 返回 400 + INVALID_EMAIL | 校验失败测试 |
| 写入本地测试数据库 | 测试环境配置和数据库断言 |
| 不修改认证模块 | 修改文件列表和 diff review |
| 不连接共享环境 | 环境变量、命令输出或配置证明 |
矩阵直接暴露三类问题
- 有需求但无证据。
- 有测试但不对应验收标准。
- 有证据但来自不可信环境。
矩阵还应记录环境证明:本地或测试 URL、脱敏数据库端点、测试账号类型、分支或 commit、测试数据来源、是否连接外部服务。
证据要区分强弱
| 强度 | 示例 | 说明 |
|---|---|---|
| 强证据 | CI 结果、自动化测试、数据库断言、contract 验证 | 可重复、可审计,优先采用 |
| 中证据 | 本地命令结果、Playwright trace、脱敏日志 | 有价值,但要说明环境和数据 |
| 弱证据 | 截图、人工描述、口头确认 | 只能辅助说明,不能单独证明关键行为 |
| 不可接受 | 无命令、无环境、无数据来源、无法复现 | 不能支撑完成声明 |
截图始终是弱证据,只能作为 trace、日志和测试结果的附属,不能单独作为浏览器验证的交付物。

标准不是工具是否高级,而是能否证明当前验收标准
| 手段 | 适合证明什么 | 常见证据 |
|---|---|---|
| 单元测试 | 纯函数、规则、边界条件 | 测试名称、输入输出、失败断言 |
| 集成测试 | 模块协作、数据库读写、service/repository 行为 | 测试命令、数据库断言、测试日志 |
| API 测试 | HTTP 状态码、请求响应、错误码、鉴权行为 | 请求样例、响应体、测试结果 |
| 契约测试 | 前后端、服务间接口兼容性 | contract 文件、consumer/provider 验证结果 |
| 数据库 / 迁移验证 | schema 变更、数据兼容、回滚风险 | migration 命令、schema diff、回滚记录 |
| 浏览器 smoke | 少量关键 UI 能否打开和交互 | 命令、浏览器项目、截图或 trace |
| E2E 测试 | 用户跨页面、跨服务的完整路径 | E2E 命令、测试报告、必要时 trace/video |
| 视觉回归 | 布局、样式、关键 UI 变化 | screenshot diff、基准图、差异图 |
| 日志 / 可观测性验证 | 后台任务、异步流程、告警和审计 | 日志片段、job 状态、metric 或 event |
| 脚本化人工验收 | 暂无自动化能力但必须验证的关键路径 | 操作步骤、环境、截图、操作者记录 |
Playwright 是工具,不是验证层级,所以不出现在上表中。
Playwright 的两种用法与验证组合
把用户路径写成可重复运行的测试:登录、创建用户、提交表单、检查页面状态。
让 Agent 或工程师用真实浏览器执行固定步骤,保留截图、trace、video、console 和 network 作为证据。
浏览器验证尤其适合
- 前端表单校验、按钮状态、路由跳转。
- 登录态、cookie、localStorage、权限页面。
- 前后端联调路径;异步加载、错误提示、toast。
- 上传、下载、弹窗、多标签页等纯 API 测试覆盖不到的交互。
合理组合:浏览器验证不替代低层测试
验证手段:Playwright E2E 命令:npx playwright test tests/e2e/create-user.spec.ts \ --project=chromium --trace=on 环境:local app + local test database 覆盖场景:创建成功、重复 email、非法 email 证据:test result、trace.zip、失败时 screenshot
纯后端、无 UI 变更的任务不默认要求浏览器验证。
浏览器验证的常见风险
- 选择器不稳定,页面轻微调整就误报。
- 测试数据污染,重复运行不可靠。
- 等待策略错误,把异步问题误判为功能失败。
- 只验证 UI 文案,没有确认真实请求和数据状态。
- 使用共享或生产环境,证据不可复现且有安全风险。
因此优先使用隔离 profile、测试账号、测试 URL、稳定 selector、隔离测试数据和明确等待条件,并尽量同时检查 UI、网络响应和后端状态。trace、截图和 network 日志必须脱敏。
三个代理构成"计划 → 生成 → 自愈"的 E2E 工作流:planner 探索应用并产出 specs/ 下的测试计划;generator 把计划转成 tests/ 下可执行测试;healer 在测试失败时重放失败步骤、检查 UI、给出补丁重跑,直到通过或按 guardrails 停止——自动修复也有停止条件,不能无限重跑到变绿。初始化:npx playwright init-agents --loop=claude
给 Agent 一个能自己运行的通过 / 失败检查,回路就能闭合
| 强度 | 做法 | 适用 |
|---|---|---|
| 1 会话内迭代 | 同一任务中要求 Agent 运行检查并迭代到通过 | 常规任务默认 |
| 2 跨会话目标 | 检查设为会话级目标条件,未达成不算完成 | 多轮推进的中型任务 |
| 3 确定性门禁 | Stop hook 等机制以脚本运行检查,不通过则阻止回合结束 | 必须机械保证的关键检查 |
| 4 独立复核 | 拥有新鲜上下文的验证子代理尝试反驳结果 | 高风险任务、完成声明存疑 |
无论哪一级,同一条证据要求:让 Agent 出示证据,而不是声称成功——测试输出、执行的命令及其返回、截图。
按任务风险选强度
| 任务风险 | 最低验证要求 | 强度 |
|---|---|---|
| 文案、注释、低风险文档 | 人工 review 或格式检查 | 1 |
| 局部纯函数修改 | 定向单元测试 | 1 |
| API 行为变更 | 接口测试和错误路径测试 | 1–2 |
| 前端交互变更 | 组件测试或浏览器验证 | 2 |
| 跨页面用户路径 | E2E 测试和截图或 trace | 2–3 |
| 数据库变更 | 迁移验证、数据断言、兼容性检查 | 3 |
| 权限、支付、删除类 | 自动化测试、人工确认、审计记录 | 4 |
验证不足时,不强行宣称完成,而是明确:已验证什么、未验证什么、为什么、需要什么条件、当前能否交付 review。
测试通过 ≠ 真实路径已验证
Mock 的基本规则
- 单元测试 mock 外部边界合理。
- API / 集成验证尽量启动真实 app,走真实路由、中间件、service、repository 和本地测试数据库。
- 不可控第三方可以 mock,但必须说明 mock 边界。
- 权限、持久化、关键 API、外部副作用和审计路径不能只靠 mock 证明。
- 删除测试、放宽断言、扩大 mock,默认视为风险,需单独解释。
API 验证至少关注
- 鉴权和权限边界。
- 输入校验和错误格式。
- 状态码、响应体、序列化格式。
- 幂等性、重复请求、并发冲突。
- 外部依赖失败、超时和重试。
- 审计日志或关键事件。
数据库 / 迁移验证
- 隔离测试库、fixture 或 seed;可重复创建和清理。
- 唯一约束、并发写入和事务边界。
- migration up/down 或回滚策略;旧数据兼容、重复运行幂等。
CI 证据应包含
- commit 或 diff 基线。
- CI job 链接、编号或日志位置。
- 运行命令和测试报告路径。
- artifact、coverage 或 trace 位置。
- 失败重跑记录,尤其是 flaky test 处理说明。
没有 CI 时,说明为什么没有。
全书统一的回归阶梯
- 运行最小复现或定向测试。
- 修复当前任务直接相关失败。
- 运行相关模块测试。
- 涉及 UI 的任务运行浏览器验证。
- 运行提交前验证入口(
pnpm run check)。 - 存在 CI 时,确认 CI 使用同一权威入口。
BDD 回放和定向测试不是先后两个层级:回放是验收视角(逐条确认场景有结果),定向测试是实现手段(大多数场景正是通过定向测试回放的)。分开写是为了强调"先对照验收标准,再谈测试范围"。
浏览器 / E2E 是条件层:用户可观察行为发生在浏览器里,至少应有一个浏览器层面证据;只是后端纯规则变化,强行跑浏览器测试只会增加噪声。
不建议一开始就只运行全量检查:失败反馈过慢且噪声过大。也不建议只运行定向测试就交付:可能漏掉跨模块影响。
不证明完成,而是带着怀疑找反例
反向评审问题清单
- 是否有验收标准没有证据?
- 是否有测试只验证 mock,没有走真实路径?
- 是否有前端行为变更但没有浏览器层证据?
- 浏览器验证是否只检查页面出现,没查请求和数据状态?
- 是否存在 flaky test,只是重跑变绿却没有解释?
- 是否有失败路径缺失?环境证据不足(误连共享数据库)?
- 是否有无关改动混入?测试被删除、断言被放宽、检查被跳过?
- 是否有风险被写进总结,但没有处理方案?
任务说明必须包含边界:只报影响验收结论的缺口。被要求找问题的评审者总会找出问题,不加边界会产出大量与验收无关的意见,稀释关键风险。
产物:阻塞项与非阻塞项
| 类型 | 含义 | 处理 |
|---|---|---|
| 阻塞项 | 影响验收结论 | 修复或升级给人 |
| 非阻塞项 | 不影响本次交付,但值得记录 | 写入遗留风险或后续任务 |
交付不应只是"已完成"
至少应包含
- 修改摘要、涉及文件。
- BDD 场景验证结果;环境和数据隔离说明。
- 验证手段选择理由和强度等级。
- 验证命令和结果;mock / stub / fake 使用说明。
- CI 状态或未运行原因。
- 浏览器验证产物(截图、trace 或 video)。
- 未验证内容、遗留风险、需要用户决策的事项。
交付格式的价值是降低 review 成本:人不需要重新推理 Agent 做过什么,而是直接检查证据是否支持结论。
推荐交付模板
完成内容: - ... 修改文件: - ... BDD / 验收映射: - ... 环境 / 数据: - ... 验证手段与强度: - ... 验证: - 命令:... - 结果:... - 产物:... Mock / CI: - ... 未验证 / 风险: - ... 建议下一步: - ...
这些情况下,完成声明本身不成立
- 验收标准没有对应证据。
- 验证环境不可信,或可能连了共享 / staging / 生产。
- 验证命令无法复现,或没记录命令、环境、数据来源。
- 关键失败路径未验证。
- 测试被删、断言被放宽、mock 被扩大,却没有合理说明。
- 同类失败反复出现,只靠重跑变绿。
- 涉及浏览器行为但没有浏览器层证据,且未说明原因。
- 涉及不可逆操作,但没有审批、备份、dry-run、回滚和环境证明。
这份清单必须机械执行:Agent 有"让结果变绿"的系统性倾向,谎报成功在第三方实验中被反复观察到。
- 只交付截图,不提供命令、环境、数据和 trace。
- 删除测试、扩大 mock 或降低校验绕过失败。
- 跳过 flaky 测试,或重跑变绿后不解释原因。
- 为了让测试通过安装未知依赖或修改 lockfile。
- 把含敏感信息的截图、trace、network 日志当证据。
- 只写自然语言总结,不给验证证据。
- 不区分强弱证据,混淆交付。
- 用一条 E2E 替代所有低层测试,失败定位困难。
交付前至少检查
- 每条 BDD 场景是否都有结果;每条验收标准是否都有证据。
- 证据是否来自可信环境;强度是否足够支撑完成声明。
- 是否选定了与风险匹配的验证强度等级。
- 是否说明环境、数据来源、mock 边界和 CI 状态。
- 验证手段是否能证明对应行为。
- 涉及浏览器行为时是否有浏览器层证据。
- 是否覆盖成功路径和关键失败路径。
- 是否按回归阶梯运行了与风险匹配的回归验证。
- 是否经过反向评审;是否说明了未验证内容。
- 是否列出修改文件和验证命令;是否存在需要用户决策的问题。

没有完整测试,也能建立降级版验证
- 没有测试框架:至少提供可复现命令和请求样例。
- 没有 CI:至少记录本地验证环境和版本。
- 没有类型检查:至少补充关键路径的运行验证。
- 无法自动化所有 BDD 场景:至少明确哪些由人工验收,证据是什么。
- 没有浏览器自动化但任务涉及浏览器行为:至少提供脚本化人工验收步骤、浏览器版本、截图和关键请求记录。
遗留项目中的目标不是一次性补齐所有工程基础,而是在每次任务中增加一点可验证性。

用证据矩阵收敛这次交付
核心必做
- 逐条回放 BDD Spec,每条验收标准映射到证据矩阵(一张表完成)。
- 为每条验收标准标注验证手段,为任务选定验证强度等级。
- 按回归阶梯收敛到提交前验证(
pnpm run check)。 - 明确未验证内容和遗留风险。
- 按交付格式提交最终结果,产物存入
artifacts/chapter-4/。
反向评审由讲师现场演示:用新会话对某组的完成声明做反驳式检查。进阶(课后):Playwright 或等价浏览器验证。
终局复盘三问(对照裸跑记录)
- 裸跑时 Agent 的完成声明缺少哪些证据?按本章标准它能否声明完成?
- 计划、执行、验证三个环节各避免了裸跑中的哪次返工或哪个风险?
- 如果只允许保留一个环节的方法,你会保留哪个?为什么?
没有任何可复现验证证据 · BDD Spec 与验证证据无法对应 · 涉及浏览器交互但没有浏览器层证据且未说明 · 删除测试或放宽断言隐瞒失败
验证的核心是建立清晰的对应关系
- 干活的 Agent 不能给自己打分:验证外部化,证据必须出示而非声称。
- 逐条回放 BDD:每个场景都要有结果,不能合并。
- 证据矩阵:每条验收标准都要有对应证据,并区分强弱。
- 选择合适手段:不是所有问题都用 E2E,匹配风险选手段。
- 四级验证强度:从会话内迭代到独立子代理反驳,按风险选级。
- 回归阶梯:从定向到全局,逐步扩展验证范围。
- 反向评审:专门找完成声明的漏洞,只报影响验收结论的缺口。
- 明确遗留风险:没验证的内容、需要什么条件、当前能否交付。

闭环合上了
- 裸跑记录在每一章被重新打开:第二章看它猜了什么,第三章看它怎么失控,第四章看它的完成声明缺什么证据。
- 同一原则跨语言、跨工具迁移:固定工具链、定向测试、稳定入口、机械护栏。
- Agent 的自主边界始终一致:证据充分时独立推进,否则准确升级。
下一步:第五章考试与第六章作业,把这套闭环用在你自己的仓库里。
