
第四课结束时,ticket 已经完成,Red -> Green 记录齐全,类型检查和完整测试套件也全部通过。
是不是可以合并了?
还不能只看绿灯。测试通过只能说明被测试的行为成立,不能自动证明代码符合仓库标准,也不能证明原始 spec 中的所有承诺都已经交付。
一份代码可能命名漂亮、结构整洁,却把"停止续订"做成了"立即终止";也可能完全实现用户故事,却绕过项目约定的模块边界,留下很高的维护成本。
第五课要完成最后一道工程检查:使用 /code-review 对同一份 diff 进行 Standards + Spec 双轴评审,分别回答"代码是不是按项目方式写的"和"代码是不是做了规格要求的事"。

为什么不能只给一个"通过或不通过"
传统 review 很容易把所有发现混进同一张列表:变量名不清楚、漏了一个用户故事、重复代码、错误处理不完整、顺手加了一个 spec 没要求的扩展点。
这些问题的性质完全不同。
text
Standards:Is it built right? 代码是否符合仓库标准和可维护性要求
Spec: Is it the right thing? 是否完整、准确地实现了原始规格
如果合并成一个总分,几个轻微命名问题可能冲淡一个严重需求遗漏;功能全部实现,也可能让团队忽略代码已经违背架构约定。
所以 /code-review 会让两个独立 sub-agent 并行工作,各自只看一条轴,最后把结果并排展示。它不会把两边的问题重新排序,也不会挑一个"全场最严重问题"。
第一步:先钉死 fixed point
评审不是"看看最近改了什么",而是比较 HEAD 与一个明确的已知正确点。
fixed point 可以是:
main或其他目标分支;- 一个 commit SHA;
- 一个 release tag;
HEAD~5这类明确提交位置。
调用时需要告诉 /code-review 从哪里开始比较,例如:
text
/code-review main
或者:
text
/code-review v2.4.0
skill 会先确认这个引用确实存在,然后固定使用三点 diff:
bash
git diff <fixed-point>...HEAD
三个点意味着从 fixed point 与当前分支的 merge-base 开始比较,只看当前分支真正引入的改动。同时还会记录提交列表:
bash
git log <fixed-point>..HEAD --oneline
如果 fixed point 无法解析,或者 diff 为空,评审应该立即停止。不要让两个评审 Agent 在一个错误范围上产出看似专业的意见。
课堂中常见的错误是:有人以 main 为基点,有人看本地未提交文件,有人直接打开整个 PR。范围不同,讨论自然无法对齐。
第二步:找到原始 spec
Spec 轴必须知道这次改动承诺过什么。/code-review 会按下面顺序寻找来源:
- 从 commit message 中查找 issue 引用,例如
#318、Closes #318或 GitLab 引用; - 使用调用
/code-review时提供的 spec 路径; - 在
docs/、specs/或.scratch/中查找与当前分支匹配的规格; - 仍然找不到时,向用户询问 spec 在哪里。
通过 issue 引用查找时,它会按照第一课的 docs/agents/issue-tracker.md 读取 tracker,而不是自行猜测 GitHub、GitLab 或本地 Markdown。
如果团队明确回答"这次没有 spec",Spec 轴会跳过并报告 no spec available。它不能根据代码反推一个需求,再用自己猜出来的需求评审自己。
这也暴露了一个流程信号:经常找不到 spec,说明第三课的产物和第四课的 commit 没有建立可追溯关系。
第三步:找到仓库自己的代码标准
Standards 轴会读取项目里真正约束代码写法的文件,例如:
CODING_STANDARDS.md;CONTRIBUTING.md;- 仓库根目录或相关模块里的工程说明;
- 当前改动范围适用的明确架构规则。
能被 formatter、lint、type checker 自动检查的事情,不应该重复浪费 review 注意力。review 更适合判断工具难以替代的问题:命名是否表达意图、模块职责是否合理、抽象是否过早、变化是否散落。
仓库文档是最高优先级。如果项目明确认可某种写法,内置的通用经验不能把它判成违规。
没有完整标准文档,也不是"随便看一眼"
即使仓库没有 CODING_STANDARDS.md,Standards 轴仍会携带一组 Fowler code smell 基线。它们不是硬规则,而是需要工程判断的提醒。
| Smell | 在 diff 中通常表现为 |
|---|---|
| Mysterious Name | 名字无法说明变量、函数或类型的真实含义 |
| Duplicated Code | 多个 hunk 重复相同逻辑结构 |
| Feature Envy | 一个方法频繁读取另一个对象的数据 |
| Data Clumps | 同一组参数或字段总是一起出现 |
| Primitive Obsession | 用 string、number 代替稳定的领域概念 |
| Repeated Switches | 多处重复按同一种类型进行条件分支 |
| Shotgun Surgery | 一个逻辑变化迫使许多文件到处修改 |
| Divergent Change | 一个模块因为多个无关原因被同时修改 |
| Speculative Generality | 为 spec 没有提出的未来需求增加抽象和扩展点 |
| Message Chains | 调用方依赖过长的对象导航链 |
| Middle Man | 新增的类或函数主要只是把调用转发出去 |
| Refused Bequest | 子类或实现者拒绝了大部分继承契约 |
这里有两个必须守住的边界:
第一,仓库标准覆盖 smell 基线。项目有意采用某种结构时,不要拿通用模式压过本地决定。
第二,smell 永远是 judgement call。报告应该写"可能存在 Speculative Generality",并引用具体 hunk 和影响,而不是写成"违反第九条,必须修改"。
Standards 轴到底检查什么
Standards sub-agent 会拿到完整 diff、提交列表、标准文件和全部 smell 基线。它需要分别报告:
- 违反已记录仓库标准的地方,并引用标准文件和具体规则;
- diff 中可能出现的 smell,说明属于哪一种并引用代码片段;
- 哪些是硬性标准违反,哪些只是需要团队权衡的判断。
以停止续订为例,可能出现这样的发现:
text
Hard violation:Billing 模块标准要求公开 service 返回 Result,
但新接口直接抛出通用 Error。引用 CONTRIBUTING.md 的错误处理规则。
Judgement call:新增 CancellationStrategyFactory,
目前只支持一个策略,可能属于 Speculative Generality。
第一条有项目文档依据,可以明确判定。第二条需要结合后续方向和维护成本讨论,不能机械要求删除。
Spec 轴到底检查什么
Spec sub-agent 不评价变量名,也不判断代码够不够优雅。它只对照原始规格检查三类问题:
- spec 要求但 diff 没有实现,或者只实现了一部分;
- diff 新增了 spec 没要求的行为,形成 scope creep;
- 看起来实现了要求,但行为方式明显错误。
每条 finding 都要引用 spec 中对应的原句,避免"我觉得需求应该这样"的主观评审。
例如 spec 写着:
text
重复提交停止续订请求必须保持幂等,不产生额外状态事件。
如果代码每次调用都会追加一条 RenewalStopped,Spec 轴应该报告"要求实现但行为错误",并引用上面这条规格。
又比如 diff 增加了"立即终止并按比例退款",而 Out of Scope 明确写着第一版不处理退款,这就是 scope creep。即使代码写得很好,也应该从当前 ticket 移除或另开规格。
为什么两个 sub-agent 要并行、隔离上下文
如果同一个评审 Agent 先看到 spec 严重遗漏,再去看代码标准,它很容易把注意力全部放在需求问题上;反过来也一样。
/code-review 把两个轴放进独立上下文:
text
同一份 diff
├── Standards Agent:只看项目标准与 code smells
└── Spec Agent:只看原始承诺与实现行为
这样可以减少相互污染,也保证两份报告各自有完整注意力。最终聚合阶段只做轻微整理,不把两边 finding 合成一张重新排序的列表。
最终报告应该长什么样
一份合格输出至少有两个独立章节:
markdown
## Standards
- [Hard violation] ...
- [Judgement call: Speculative Generality] ...
## Spec
- [Missing] ...
- [Scope creep] ...
- [Incorrect behaviour] ...
结尾只总结每条轴各有多少发现,以及各自最严重的问题:
text
Standards:2 项,最严重的是错误处理违反模块标准。
Spec:1 项,最严重的是重复请求没有保持幂等。
不要再给一个"综合评分 76 分",也不要说"总体最大问题是......"。两个轴故意分开,就是为了避免其中一边遮住另一边。
同一段代码可能在两条轴上同时出现
这不是重复报告,而是两个不同理由。
例如 Agent 为未来可能出现的退款、冻结、暂停订阅设计了一个复杂 CancellationStrategy 体系。
Standards 轴可能把它标为 Speculative Generality:当前只有一个真实策略,抽象带来了额外维护成本。
Spec 轴可能把它标为 scope creep:本次规格只要求停止续订,退款和暂停明确不在范围内。
一个是设计风险,一个是交付范围偏移。修复动作可能相同,但判断依据不同,必须分别保留。
Review 发现以后,先分类再行动
团队拿到报告后,不要让 Agent 立刻一股脑修复。先给每条 finding 指定处理方式:
- Fix now:当前 ticket 必须修复的硬性标准或规格问题;
- Clarify spec:规格本身存在歧义,需要回到需求负责人确认;
- Follow-up ticket:真实存在但不属于当前范围的改进;
- Accept:团队接受的权衡,并在必要时记录原因;
- False positive:引用证据关闭误报。
Spec finding 通常优先阻止交付,因为它意味着承诺没有完成或做错。Standards finding 是否阻止合并,要看它是仓库硬规则还是 judgement call。仍然不要把两边合成一个统一严重度排行榜。
用第四课的 diff 做一次完整演练
假设第四课分支是 feature/stop-renewal,目标分支是 main,commit message 引用了 #318。
课堂操作可以这样开始:
text
/code-review main
Agent 应该依次完成:
text
1. 确认 main 可以解析
2. 确认 git diff main...HEAD 非空
3. 记录 main..HEAD 的 commit 列表
4. 从 commit message 找到 #318
5. 根据 issue-tracker.md 获取 spec
6. 查找仓库标准文件
7. 并行运行 Standards 和 Spec 评审
8. 分轴汇总 findings
任何一步缺失,都可能让评审失去依据。例如没有 fixed point,diff 范围不可信;没有 spec,不能声称需求全部实现;没有引用标准,Standards 容易退化成个人代码偏好。
团队使用约定应该写什么
培训最后不要只总结"这几个命令很好用"。要把团队自己的触发条件写具体。
可以从下面这份模板开始:
markdown
## Agent Skills 团队约定
### 必须先 /grill-with-docs 的场景
- 出现未定义或多义的业务术语;
- 改动涉及状态机、权限或多个领域上下文;
- 存在难回滚且有真实取舍的架构决定。
### 必须先 /to-spec 的场景
- 用户可见行为发生变化;
- 改动跨越多个模块或需要明确 out of scope;
- 需要提前确认 testing seam。
### 必须拆 /to-tickets 的场景
- 工作无法在一个 fresh context 中完成;
- 存在可以并行的纵向交付路径;
- 工单之间存在必须显式记录的 blocking edges。
### 测试约定
- 测试 seam 在写测试前确认;
- 测试公开行为,不测试私有方法和内部调用次数;
- 每个行为必须先出现有效 Red;
- expected 来自 spec、固定示例或其他独立真值。
### Review 约定
- 默认 fixed point 是目标分支 merge-base;
- Standards 与 Spec 分开报告,不给综合评分;
- 无 spec 时必须明确跳过 Spec 轴;
- code smell 是 judgement call,仓库文档优先。
这份约定应该进入 AGENTS.md、CLAUDE.md 或团队已有的工程手册,并在项目工作方式变化时更新。
常见误区
没有指定 fixed point 就开始 review。 先停下来明确比较点。范围不可信,finding 也不可信。
使用两点 diff。 skill 要求 git diff <fixed-point>...HEAD,通过 merge-base 隔离当前分支引入的变化。
找不到 spec,就按代码猜需求。 Spec 轴应该跳过并明确说明,没有资格虚构验收标准。
Standards 只有个人偏好。 每条硬性问题引用仓库标准;通用 smell 必须标记为 judgement call。
formatter 能发现的问题又报告一遍。 跳过工具已经强制执行的事项,把注意力留给语义和设计。
把两个轴合成一个严重度列表。 保持分开。代码标准和规格符合度的修复路径不同。
一看到 finding 就全部自动修改。 先分类,确认哪些属于当前 ticket,哪些需要补规格或另开 follow-up。
只做一次 review。 Fix now 完成后,使用同一 fixed point 再跑一次,防止修复引入新的需求偏移。
第五课真正完成的是一条可重复的工程闭环
到这里,五节课串成了一条完整链路:
text
setup
-> grill-with-docs
-> to-spec
-> to-tickets
-> implement + tdd
-> code-review
第一课让 Agent 知道项目规则,第二课不让猜测进入需求,第三课把共识切成可交付工作,第四课让每一步实现都有失败和通过的证据,第五课再把代码标准与规格符合度分开验收。
真正的收益不是团队多记住五个斜杠命令,而是建立了一套 AI 也必须遵守的工程反馈系统。Agent 可以写得很快,但它不能跳过上下文、决定、测试和评审,也不能用"代码能跑"代替"工作已经正确交付"。