code-review · diff

git:20261031.08e3b0f to git:20261031.11f39d2

11 added, 14 removed. Audit A to A.

---
name: code-review
description: 写完代码、提交前或合并 PR 时使用;用户说“帮我 review”“提交前检查一下”“看看这次改动”“PR review”,或要求严格审查/合入门禁时也使用。
---
# 代码审查
> 检查代码缺陷、测试质量和变更影响,并确认已发现的问题是否闭环。
## 资源与执行位置
- `references/`、`scripts/`、`assets/` 均相对本次读取的 `SKILL.md` 所在目录。
- shell 命令从**被审项目根目录**执行;`docs/work/`、代码路径与项目规则均属于被审项目。
- 下文的 `CODE_REVIEW_SKILL_DIR` 指 skill 安装目录的绝对路径。每次 shell 调用都设置它,
或直接用实际绝对路径替换;不要假定不同 shell 调用会保留变量。
- 脚本需要 Bash、Git、awk、grep、sed 等基础命令。通过 `bash` 调用,无需依赖可执行位。
- 报告解析需要 Python 3.9+ 和 PyYAML;默认调用 `python`,可用 `CODE_REVIEW_PYTHON` 指定解释器绝对路径。
安装方式见发布仓库 README;仓库内入口为 `scripts/install.py`。
## 适用范围
- 审查影响代码行为的功能、bug 修复、重构或配置变更。
- 不影响代码行为的纯文档、格式或错别字改动可跳过。
- 测试失败或尚未运行时仍可静态审查;报告须注明测试状态,不得宣称已满足合入条件。
## 输入
| 输入 | 必需 | 说明 |
|---|---|---|
| git 仓库 + 可 diff 的改动 | 是 | 明确审查未提交改动,还是已提交的 base/head 区间 |
| 验收标准 | 否 | 提供时按“可选收尾”逐条勾验 |
| 覆盖率数据 / 命令 | 否 | 提供时用于 `test-coverage` 的可选测量;未提供时仍执行相关测试代码的静态质量检查 |
| 规则包 | 否 | 默认 skill 内置 `references/rules/`;可用 `CODE_REVIEW_RULES_DIR` 覆盖 |
| 审查标识 | 否 | `{slug}`,默认 `<日期>-<主题>` |
产物根目录为 `docs/work`,本次产物写入 `docs/work/active/{slug}/`。
## 审查流程
> 检查项不写死在本文件里,由 `references/probes.yaml` 注册表驱动。
> 探针的实现细节在 `references/probes/<探针名>.md`;本文件只管**编排**。
>
> **执行边界:默认进行静态审查。** 覆盖率检查优先复用已有报告;
> 需要测量时,可在本轮任务允许的范围内执行覆盖率命令。其他检查仍只读证据。
> 审查过程中不得修改被审源代码。测试与 CI 输出作为审查证据,
> **测试全绿不等于行为正确**。
1. **确定审查范围**:
**选择模式**,未指定时默认审查未提交改动。下文的 base/head 均记录为具体 commit ID。
- **未提交改动(worktree)**:base 为当前 HEAD。
用 `git diff <base> --` 收集暂存与未暂存改动;
用 `git ls-files --others --exclude-standard -z` 收集未跟踪文件,
并将其完整内容作为新增代码审查。
- **已提交区间(commits)**:按用户指定的 base/head,
用 `git diff <base> <head> --` 收集差异。
省略 head 时使用当前 HEAD,只审查该提交区间。
**读取对应源码**:
- worktree 模式读取工作区文件。
- commits 模式用 `git show <head>:<路径>` 读取目标版本,
需要旧版本时读取 base。文件扫描仅使用与目标版本一致的磁盘文件。
**记录并交接**:保存模式、base、目标(工作区或 head)、变更文件清单及差异,
供所有探针使用。程序处理文件清单时使用 NUL 分隔,并逐路径引用传参。
收集完成后,再创建报告目录 `docs/work/active/{slug}/probes/`,
避免将新生成的报告纳入待审查文件。
2. **读注册表,确定探针清单**:Read `references/probes.yaml`,按 `requires` 判断:
- 指定了但缺失 / 不可读 → `error`
- 未提供可选输入 → `skipped`,写明缺什么
- 可用 → 执行
3. **并行执行探针**:
**派发**:主执行器按注册表为每个探针派发独立代理,提供审查范围、diff、项目根、skill 路径及该探针的输入和 `params`,并在派发提示中包含上文“执行边界”和本步骤“执行”段的公共约束。
代理读取注册项 `file` 指向的说明和 `references/output-schema.md`,按参数执行。
并发槽位不足时分批执行;没有独立代理能力时串行执行并记录限制。
**执行**:代理只检查自己的探针,不重跑完整流程或再派发代理;源码版本遵循步骤 1。
finding 须说明本次改动如何引入或触发该问题;无关的存量问题单独说明,不纳入本轮 finding 清单及计数。
复用已读上下文,仅按需补读;独立只读查询尽量合并,有依赖的查询逐步执行,首次读取源码时保留准确行号和必要上下文。
本地搜索限于被审仓库、明确提供的关联目录及依赖坐标对应的缓存路径;缺失或不可读时记录具体证据缺口,
不扩大到磁盘根目录或用户目录,也不在没有新线索时反复搜索。
只写 `docs/work/active/{slug}/probes/<探针名>.json`,不写共享报告或指标。
返回前须等待自己启动的后台任务结束,或停止已不再需要的任务并确认退出,再通知完成。
**交接**:每个探针均须有 JSON,错误或跳过时写明原因;执行错误时保留已发现问题,其他探针继续。
代理启动失败或未返回有效结果时,由主执行器记录 `error` 及原因,不沿用该探针的旧结果。
有可靠计时时记录 `duration`,各探针耗时之和不代表本轮墙钟时间。
`test-coverage` 通过返回消息交接有效覆盖率的报告路径、目标版本、分子/分母及百分比,
由主执行器执行 `collect-metrics coverage`;未测量时不补数值。
**汇总**:等待所有代理结束,由主执行器统一同步;`required` 从注册表读取,各级数量从 JSON 逐条计算,不改写原始结果或手填计数。
暂不做跨探针去重,按原始 finding ID 计数,可能包含同根因问题的重复记录:
```bash
bash "$CODE_REVIEW_SKILL_DIR/scripts/collect-metrics" sync {slug} --probes
```
此步使上一批对账状态失效,尚不记录 reconciliation。
历史重复记录保留原始 ID、证据及计数;展示独立问题数量时按同一根因和后果归组并列明对应 ID,不因重复提高严重级别,也不以展示数量替代探针或对账计数。
4. **reconciliation 完整性检查**:
从探针 JSON 自动派生最终计数:
```bash
bash "$CODE_REVIEW_SKILL_DIR/scripts/collect-metrics" sync {slug}
```
`required` 只控制该探针的 `failed/error` 执行状态是否直接阻塞,不改变 finding 的闭环要求。
任意探针的 high finding 都进入 reconciliation,未闭环仍阻塞合入;medium/low 记入报告,不计入对账总数:
```text
RECONCILE_TOTAL = high finding 总数
RECONCILE_DONE = 其中 status 为 reconciled 或 false-positive 的条数
RECONCILE_PENDING = TOTAL - DONE
```
没有 high 也须执行,显式记录零结果,不能用旧计数或手填数字代替。
格式错误须先修正;记录缺失、重复 ID 或状态矛盾时同步失败,不写计数,不以旧结果放行。
有未处置问题(`RECONCILE_PENDING > 0`)时,本轮仍交付报告并列明阻塞项,不得推进合入。
后续修复任务完成后,再复审受影响的部分;重派上限见 `references/probes.yaml` 的 `execution.max_retry`。
- 5. **汇总报告**:汇总 findings、未验证项及阻塞结论。
- 若提供了验收标准,须先按“可选收尾”完成勾验,再形成最终结论。
- 按模板撰写范围、解释、修复建议和限制,保留自动区域标记;原始 JSON 不写入正文。
- 脚本只替换报告 §5、§6 的 `code-review:probes` / `gate` 标记区域,保留其他正文。
- 执行以下命令直接生成事实区域并校验,不经模型复制表格:
+ 5. **汇总报告**:
+
+ 按模板填写审查范围、解释、修复建议和限制,原始 JSON 不嵌入正文;提供了验收标准时,先完成逐条勾验。
+
+ 执行以下命令,从 JSON 生成报告事实区、校验展示并取得最终答复摘要:
```bash
bash "$CODE_REVIEW_SKILL_DIR/scripts/collect-metrics" render-report {slug}
bash "$CODE_REVIEW_SKILL_DIR/scripts/collect-metrics" verify-report {slug}
bash "$CODE_REVIEW_SKILL_DIR/scripts/collect-metrics" summary {slug}
```
- 校验从相同 JSON 重新渲染并比对 ID、位置、证据、严重级别、状态和计数。
- `summary` 会先校验报告,再输出最终答复可引用的机器摘要;报告不一致时不输出摘要。
- 最终答复中的 high/medium/low 数量、finding ID 和阻塞结论必须来自该摘要,
- 不得新增 JSON 中不存在的 finding,也不得省略 JSON 中已记录的 high。
- 渲染先完整校验再原子更新,可重复执行,不更改 JSON;标记缺失或重复时停止,不覆盖整份报告。
- 主执行器可补充解释及修复建议,不得改写自动区域或推翻已知阻塞结论。
- 自动区域的“无已记录阻塞项”不代表验收、覆盖率、测试或 CI 已通过。
- `gate-pass {slug} code-review` 与有审查记录的 `archive` 会先校验报告,再同步计数,不会自动修正被改写的展示。
- 向用户交付报告、阻塞结论和未验证项。
+
+ 脚本只更新报告 §5、§6 的 `code-review:probes` / `gate` 标记区域,保留其他正文和原始 JSON。渲染先校验再原子更新,可重复执行;标记缺失或重复时停止。校验覆盖 ID、位置、证据、严重级别、状态和计数。
+
+ `summary` 先校验报告,一致后才输出机器摘要。最终答复中的 high/medium/low 数量、finding ID 和阻塞结论须以摘要为准,不新增未记录的问题,不遗漏已记录的 high。向用户交付报告、阻塞项及未验证内容,可补充解释和建议,不得改写自动区域或推翻已知阻塞事实。
+
+ “无已记录阻塞项”不代表验收、覆盖率、测试或 CI 已通过。`gate-pass {slug} code-review` 与有审查记录的 `archive` 均先校验报告,再同步计数,不自动修正被改写的展示。
## <HARD-GATE>
**以下任一条件命中,阻塞合入**。本轮报告列明阻塞项,下表为后续处置要求:
| 阻塞条件 | 后续动作 |
|---|---|
| required 探针为 `failed` / `error`(`PROBE_REQUIRED_FAILED > 0`) | 处理已发现的问题或执行错误后,重跑对应探针 |
| high finding 未完成 reconciliation(`RECONCILE_PENDING > 0`) | 按步骤 4 处理,修复后重派受影响的探针 |
| reconcile 从未上报(`RECONCILE_REPORTED=0`) | 完成步骤 4;没有 high 也须执行最终同步 |
| 提供了覆盖率数据且低于基线(默认增量行覆盖率 < 85%) | 列明覆盖缺口,补测试后重新审查 |
| 提供了验收标准且未全过,或标准本身有歧义 | 列明未通过项;有歧义先澄清,不得自行勾验通过 |
| 有 high finding 停在 `wontfix` | 按输出契约完成处置,不能用“不修”冲抵待办 |
执行状态与输入错误的判定见 `references/output-schema.md` 第 4 节。
脚本验证报告记录与计数的一致性,不能证明检查是否充分。
## 输出契约
- **主报告**:`docs/work/active/{slug}/code-review.md`,按
`assets/code-review.md` 的章节和填写要求生成。
- **机器记录**:`docs/work/active/{slug}/probes/<探针名>.json`,
格式见 `references/output-schema.md`;人类报告的事实区域由它们派生。
历史记录须明确迁移为独立 JSON;不从自由文本猜测或自动补零,也不同时读取旧内嵌区块。
## 可选收尾
以下事项按条件执行,不作为每轮必经步骤:
- **验收**:提供了验收标准时,在最终结论前逐条勾验;没有则跳过并说明。
- **lessons**:有 `lessons/` 目录,且本次有实际发生、值得复用的经验时记录,说明具体问题、原因和处理方式:
- 实现过程中踩的坑 → `lessons/pitfalls.md`
- AI 产出被纠正的案例 → `lessons/bad-cases.md`
- 新的架构决策 → `lessons/arch-decisions.md`
- **归档**:如需归档,将 `docs/work/active/{slug}/` 移到 `docs/work/archive/{YYYY}/`。