--- name: code-review description: 用于代码评审(code review)、评审 PR,或检查一组提交和 diff。 --- 从三个维度评审 `HEAD` 与用户指定的比较基准之间的 diff: - **规范**:代码是否符合本仓库成文的编码规范? - **规格**:代码是否准确实现了来源 issue 或规格? - **上线风险**:代码合并、部署之后,是否会丢数据、破坏现有调用方、引入安全问题或拖垮性能? 评审由子代理完成,子代理数量按 diff 的规模和风险决定(见第 4 步),然后由本 skill 按三个维度分别汇总。 ## 过程 ### 1. 确定比较基准 用户指定的就是比较基准(提交 SHA、分支名、tag、`main`、`HEAD~5` 等)。用户没有指定时,询问用户。 记下 diff 命令:`git diff <比较基准>...HEAD`(三个点,与 merge-base 比较),以及提交列表命令:`git log <比较基准>..HEAD --oneline`。 继续之前,确认比较基准可以解析(`git rev-parse <比较基准>`)且 diff 不为空。无效的引用或空 diff 应该在这一步报错,而不是在子代理中报错。 把提交列表、`git diff --stat` 和带上下文的完整 diff(`git diff -U10 <比较基准>...HEAD`)写入临时目录中一个名称唯一的文件,作为**评审材料**。子代理读取这个文件,diff 本身不进入你的上下文。 ### 2. 查找规格来源 按以下顺序查找来源规格: 1. 提交信息中的 issue 引用(`#123`、`Closes #45`、GitLab `!67` 等),按 `docs/agents/issue-tracker.md` 中的方式获取。 2. 用户通过参数传入的路径。 3. `docs/`、`specs/` 或 `.scratch/` 下与分支名或功能相符的规格文件。 4. 都没有找到时,询问用户规格在哪里。用户表示没有规格时,跳过**规格**维度,并报告“无可用规格”。 找到规格后,同时收集它的依据文档:规格或任务链接的功能清单和设计依据。它们与规格一起交给负责规格维度的子代理。 ### 3. 查找规范来源 查找仓库中所有说明代码应该如何编写的文档,例如 `CODING_STANDARDS.md`、`CONTRIBUTING.md`,以及 `CLAUDE.md` 或 `AGENTS.md` 中的约定。 除仓库文档外,**规范**维度始终附带本 skill 目录下的[代码坏味道基线](CODE-SMELLS.md)。你不需要读它;把它的绝对路径交给负责规范维度的子代理,由子代理读取。 ### 4. 按规模和风险派出子代理 根据 `git diff --stat` 的行数和文件路径选择评审档位。拿不准时选更重的一档。 - **小**:改动不超过约 150 行、5 个文件,且不涉及下面的风险路径。派 1 个,按三个维度的任务依次评审,报告分三个标题。 - **中**:超过小档,但不超过约 800 行,且不涉及风险路径。派 2 个:规范与规格合并为一个,上线风险单独一个。 - **大**:超过约 800 行,或涉及风险路径:迁移、schema、持久化格式、公开接口定义、认证授权、密钥与凭据、依赖清单或锁文件、CI 与部署配置、并发与事务相关代码。派 3 个,规范、规格、上线风险各一个,并行派出,互不影响上下文。 合并维度时,子代理的提示同时包含被合并维度的材料和任务,并要求按维度分标题输出,每个维度保持各自的字数上限。规格缺失时,规格任务从提示中去掉。 每个子代理的提示都包含下面的**评审守则**: > 本次评审对当前检出只读:不修改工作区、暂存区、HEAD 或分支状态。需要查看其他版本时,使用 `git show`,或检出到单独的临时目录。评审由你亲自完成,不再派子代理。 > > 每条发现都写明 `文件:行号`、问题是什么、为什么重要、怎么修复(不明显时)。按实际严重度分级: > > - **严重**:bug、安全问题、数据丢失风险、功能损坏。 > - **重要**:不修复就无法信任这项改动,例如遗漏需求、吞掉错误、断言为空的测试、逻辑块原样重复。 > - **次要**:打磨类建议,以及“覆盖可以更广”之类的建议。 > > 只看 diff 无法核实的要求(位于未改动的代码中,或跨越多处改动),标为 ⚠️ 并说明需要检查什么,不要遍历整个代码库。直接以结论开头,不写铺垫和收尾总结。 **规范维度**的提示还包含: - 评审材料文件路径。 - 第 3 步找到的规范来源文件清单,以及 [CODE-SMELLS.md](CODE-SMELLS.md) 的绝对路径,要求先读取它。 - 任务:“按文件或 hunk 报告:(a) diff 违反成文规范的每一处,并引用规范(文件和条目);(b) 发现的每个基线坏味道,写出名称并引用 hunk。区分硬性违规和判断性建议:成文规范的违反可以是硬性的,基线坏味道始终是判断性建议,成文规范优先于基线。工具已经强制检查的内容不报告。400 字以内。” **规格维度**的提示还包含: - 评审材料文件路径。 - 规格的路径或获取到的内容,以及依据文档(功能清单、设计依据)的路径。 - 任务:“报告:(a) 规格要求但缺失或只完成一部分的内容;(b) diff 中有、但规格没有要求的行为(范围蔓延);(c) 看起来已经实现、但实现方式似乎不对的内容;(d) 规格或任务列出的功能项在当前批次中缺失的状态,缺少可以重复运行的自动化检查的状态,或只打通一条链路却没有覆盖其余变体的情况(延后到更晚批次的状态不算缺失);(f) 任务涉及的非功能需求没有验证或没有达到目标值;(e) 页面改动与设计依据不一致之处,只看 diff 无法对比截图时标为 ⚠️,写明需要对比哪些状态。每条发现都引用规格原文或功能项编号。400 字以内。” 规格缺失时跳过规格维度,并在最终报告中注明。 **上线风险维度**的提示还包含: - 评审材料文件路径。 - 规格的路径(有时),用来判断兼容性要求和性能目标;非功能需求中的目标值是性能类检查的依据。 - 任务:“逐类检查 diff 是否涉及以下风险。每一类先写一行‘涉及’或‘未涉及’,依据是 diff 中能看到的改动;涉及时,在这一行下面列出发现: 1. **数据**:diff 修改了 schema、迁移脚本、数据模型或持久化格式时,检查迁移能否在已有数据上执行、失败时能否回滚、新旧版本代码同时运行期间(滚动发布)能否读写同一份数据、是否有删除或截断数据的操作。 2. **对外契约**:diff 修改了公开 API、接口定义(OpenAPI、proto、GraphQL schema)、导出的公共类型、事件或消息格式、CLI 参数、配置项或环境变量名时,检查已有调用方是否会被破坏,破坏性变更是否有版本、兼容层或迁移说明。 3. **安全**:diff 涉及认证、授权、会话、加密、密钥或凭据,或者外部输入进入查询、命令、文件路径、HTML 输出时,检查权限校验是否完整、输入是否经过校验或转义、密钥是否可能进入日志或代码。 4. **依赖与运行环境**:diff 修改了依赖清单或锁文件、CI 或部署配置、容器镜像、运行时版本时,检查新依赖的必要性、版本约束和部署步骤是否需要同步调整。 5. **并发与性能**:diff 涉及锁、事务、队列、缓存、批处理、重试,或在循环中发起查询或网络请求时,检查竞态、死锁、N+1 查询、无上限的重试或内存增长;规格有性能目标时,判断改动是否可能超出目标。 发现要写明触发条件,例如‘在已有 100 万行数据的表上执行时’。只报告能从 diff 看出的具体风险,不写与这次改动无关的通用建议。400 字以内。” ### 5. 汇总 无论派出几个子代理,都把结果分别放在 `## 规范`、`## 规格` 和 `## 上线风险` 标题下,原样呈现或稍作整理,并在开头注明评审档位。**不要**合并维度,也不要跨维度重新排序发现,各个维度是有意分开的(见“为什么分维度”)。 每个维度末尾给出该维度的结论:**可合并**、**修复后可合并**或**不可合并**,并附一两句技术理由。⚠️ 项由你亲自核实,因为你掌握子代理没有的跨改动上下文;确认是真实缺口的,按规格失败处理。 最后一行总结每个维度的发现数量,以及*每个维度内*最严重的问题(如有)。不要跨维度选出唯一的“最严重”问题,分开报告正是为了避免这种重新排序。 收到评审结果后,使用 `receiving-code-review` skill 处理。 ## 为什么分维度 一项改动可能在一个维度通过,却在另一个维度失败: - 遵守了每一条规范,却实现了错误的功能:**规范通过,规格失败。** - 完全实现了 issue 的要求,却破坏了项目约定:**规格通过,规范失败。** - 规范和规格都满足,迁移却会在已有数据上失败,或者改名的字段让旧版客户端报错:**规范、规格通过,上线风险失败。** 规格很少写到这些,规范也很少成文,所以单独成为一个维度。 分开报告,一个维度的结果就不会掩盖另一个维度的问题。