AI PR 范围漂移:从宽泛 diff 到可评审规格包

这个案例展示如何评审一个 AI 生成的 PR:它解决了表面问题,但也可能悄悄修改团队没有批准的内容。

风险未批准文件、API、策略变更
边界允许路径和明确非目标
证据Diff 映射、测试、评审签字

规格前的需求长什么样

弱提示词

用 AI 修复个人资料设置 bug。
用户修改 display name 后应该能正确保存。
顺手清理相关代码。

Spec-first 改写

Feature: 保存个人资料 display name
Owner: Account Settings
Status: Ready for review

目标:
- 从设置页持久化 display_name。
- 保存后显示成功状态。
- 保持现有 email、avatar 和 billing settings 行为不变。

允许路径:
- apps/web/routes/settings/profile.tsx
- packages/account/profile-service.ts
- packages/account/profile-service.test.ts

非目标:
- 不改 billing settings。
- 不改 auth/session。
- 不重做 settings layout。
- 不升级依赖。

约束 PR 的规格包

tasks.md

- [ ] 复现 display_name 保存失败。
- [ ] 只修复 profile update service。
- [ ] 增加 display_name 持久化回归测试。
- [ ] 保持 email、avatar、billing、auth 流程不变。
- [ ] 发起 review 前补充 PR diff map。

acceptance-criteria.md

- Given 一个已登录用户
  When 修改 display name
  Then 刷新后名称仍然保存。

- Given 这次 PR 被评审
  When 检查 changed files
  Then 没有任何文件超出允许路径。

evidence.md

自动化:
- profile display_name 回归测试
- settings form submit 测试

人工:
- 修改前后截图
- changed-file list 映射到 tasks
- 评审者确认没有范围扩大

AI 实现约束

只能修改允许路径。
如果发现 billing、auth、layout、dependencies 或 email settings 相关问题,写成 follow-up note,不要直接改代码。
每个变更行都必须映射到一个已列出的 task。

评审者如何抓住范围漂移

评审问题通过信号拒绝信号
Diff 是否留在允许路径内?每个 changed file 都出现在规格包里。PR 触碰 billing、auth、layout、依赖或无关测试。
每个文件是否映射到 task?PR 描述把每个文件对应到任务和验收标准。改动被描述为 cleanup、refactor 或 improvement,但没有 task。
助手是否发明了产品策略?产品行为和规格完全一致。助手修改验证规则、session 行为或保存字段。
合并前证据是否存在?回归测试、截图和 diff map 都已附上。PR 只要求评审者相信 demo 或总结。

评审走查

原提示里最危险的一句是“顺手清理相关代码”。这听起来积极,但实际上给了助手重新解释工单的权限。实际 PR 里,它可能顺手改设置布局、账单表单、验证策略、依赖版本,以及评审者原本没准备看的测试。

规格包把这个决定权还给团队。允许路径不是装饰,而是评审契约。如果助手需要修改规格包外的文件,正确动作是解释为什么当前范围不够,而不是直接提交更大的 PR。

范围检查

先看文件列表,再看代码风格。超出范围的整洁 diff 仍然是失败的规格执行。

任务检查

每个文件都应对应一个 task。只对应“清理”的文件,应删除或拆成新规格。

证据检查

测试证明原 bug 被修复;截图和 diff map 证明 PR 没有扩大行为。

如何复用这个案例

当 AI 被要求修复成熟产品区域里的小 bug 时,都可以用这个模式:设置、计费、权限、onboarding、通知、后台工具或报表。文件路径会变化,但评审动作不变:定义允许路径,写出能阻止范围漂移的非目标,并要求评审前提供 changed-file map。

允许路径不要窄到无法实现。它的目的不是刁难助手,而是把范围协商提前到规格阶段,而不是让它悄悄发生在 diff 里。如果模型发现 bug 真正在 shared service 中,应该先更新规格并请人复核,再接受更大的改动。

这也能让 PR 评审少一点主观争论。评审者不用说“我不喜欢这个重构”,而是直接指向规格包:文件超出范围、行为属于非目标,或证据不能证明验收标准。

PR 模板应该怎么配合

让这个模式真正生效,最简单的办法是在 PR 模板里增加三个字段。第一,必须链接规格包。第二,必须提供 changed file 到 task 的映射表。第三,必须列出助手发现但没有纳入当前 PR 的 follow-up。这样评审就不只是看代码风格,而是先看这次实现是否遵守了已批准边界。

这一点对 AI 生成代码尤其重要。助手经常会发现相邻问题,这些发现本身有价值,但不能悄悄塞进 bug fix。PR 可以把它们记录成 follow-up note,附上 owner 和风险等级,再由团队决定是否另开规格,而不是让当前 diff 越滚越大。

团队还可以把“超出规格包即退回”写成默认规则。这样作者不会把退回理解成否定实现质量,而是知道需要先更新规格、确认范围,再继续让 AI 修改代码。

规格链接

评审从已批准行为和边界开始,而不是从助手总结开始。

文件映射

每个 changed file 都要对应到一个 task、验收标准或证据项。

后续记录

保留有价值发现,但不扩大当前 PR 的合并范围。

应该拒绝的反模式

看似有帮助的无关清理

当评审者预期是 bug fix 时,重构并不是免费的。把清理拆成单独规格。

功能 PR 里顺手升级依赖

包升级会扩大测试面和回滚风险,应单独提交升级规格包。

把策略变更藏在验证逻辑里

验证规则是产品行为,需要验收标准,而不是实现备注。

评审 AI 生成 PR 前先用这个模式

先生成规格包,再让 PR 证明每个 changed file 都属于规格包。

编辑说明

这个案例关注 AI 辅助实现评审:明确规格边界,是防止未批准范围扩大的主要保护。