Files
code-review-graph/CODE_REVIEW_GUIDE_ZH.md

730 lines
56 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码审查功能文档
> 代码审查能力由**两个独立工作流**组成:`unified-review`diff 审查)与 `project-review`(项目级审查)。本文聚焦**审查工作流**的操作细节(流程、覆盖度机制、客观指标、报告产物、实操踩坑)。
>
> 📖 **文档分工**
> - **本文**`CODE_REVIEW_GUIDE_ZH.md`)— 审查流程 / 指标解读 / 覆盖度机制 / 报告产物 / 踩坑记录
> - **工具总览**`CODE_REVIEW_GRAPH_ZH.md`)— 工具定位、MCP 工具清单、CLI 命令、配置与部署、opencode 集成现状
---
## 目录
1. [两种审查工作流](#1-两种审查工作流)
2. [unified-reviewdiff 代码审查](#2-unified-reviewdiff-代码审查)
3. [project-review:项目级代码审查](#3-project-review项目级代码审查)
4. [并行深读流水线](#36-并行深读流水线whole-project-强制)
5. [审查调用的 MCP 工具](#4-审查调用的-mcp-工具)
6. [5 项客观指标详解](#5-5-项客观指标详解)
7. [审查 Skill 体系](#6-审查-skill-体系)
8. [审查报告成果物](#7-审查报告成果物)
9. [快速上手](#8-快速上手)
---
## 1. 两种审查工作流
| 维度 | unified-review | project-review |
| --- | --- | --- |
| **审查对象** | git diff(默认 `HEAD~1..HEAD`| 全部源文件 / 单个功能 |
| **范围来源** | 自动检测 git diff | 由用户自然语言指令决定 |
| **opencode 命令** | `/code-review-graph-unified-review` | `/code-review-graph-project-review` |
| **MCP prompt** | `/code-review-graph:unified_review` | `/code-review-graph:project_review` |
**共同特性**
- **只读**:不修改代码,所有修复由人工裁决
- **客观评分**:统一走 `score_review_tool` 的 5 项量化指标
- **去重合并**:统一走 `dedupe_findings_tool`
- **双格式报告**:默认产出中文 HTML + Markdown
- **验收门禁**:任一 🔴 blocker → 判定 `❌ FAIL`
---
## 2. unified-reviewdiff 代码审查
### 2.1 执行流程
| 步骤 | 做了什么 | 起到什么功能 |
| --- | --- | --- |
| **Step 0 范围** | 固定 `standard` 档位(全层审查);检测语言/框架;判定 change/file/service/chain 级范围 | 决定审查深度与范围粒度,避免对简单改动做全量审查,对高风险模块自动升级审查强度 |
| **Step 1 图谱上下文** | `build_or_update_graph` 确保图谱最新;`get_review_context` 拿 blast radius + 源码片段;`detect_changes` 拿风险分、测试缺口、受影响流 | 让 AI 只读变更波及的最小文件集,建立"改了什么、影响了谁"的全貌,token 高效 |
| **Step 2 Layer 1 链路分解** | 八分类检查(接口/业务/数据/工具/错误处理/安全/性能/可观测性)+ gstack CRITICAL 五类(SQL 数据安全、竞态并发、LLM 信任边界、Shell 注入、枚举完整性) | 逐类排查 AI 易漏的高风险缺陷(注入、竞态、信任边界、枚举遗漏),建立分类清单 |
| **Step 3 Layer 2 量化评分** | `score_review_tool` 计算 5 项客观指标(SQL 风险/异常分支/冗余率/高风险密度/漏洞启发式);需求覆盖、逻辑对齐等由 LLM 判断(`llm_judged` 标注) | 用数据支撑评分而非主观判断:指标带 good/warn/fail 分级与证据,明确哪些需 LLM 补判 |
| **Step 4 specialist 派发** | diff ≥ 50 行时并行派发 testing/maintainability/security/performance/data-migration/api-contract 子代理,各自独立审查 | 用"多个专注专家并行独立审查"覆盖单一视角盲区,多专家确认的 finding 置信度更高 |
| **Step 5 合并去重** | `dedupe_findings_tool``path:line:category` 指纹合并、多源确认置信 +1、低置信抑制、计算 PR 质量分 | 把主审查 + 各 specialist 的 findings 合并成唯一清单,去除重复噪音,量化整体质量 |
| **Step 6 人工裁决** | **只读**。每条 finding 带 severity + 置信度 + file:line + 修复建议,按 severity 批量呈现,逐个决定修/不修 | 把关决策权交给人工,防止 AI 擅自改码;blocker 不可批量跳过,确保高风险项被确认 |
| **Step 7 验收门禁** | 任一 🔴 blocker → verdict `❌ FAIL`;分类 Ready / Needs Fix / Unusable | 给出明确的合并/上线结论,任何阻塞项都阻止通过,避免带病合并 |
| **Step 8 报告** | `generate_report_tool``format="both"`)产出中文 HTML + Markdown 报告 | 生成可分发、可存档的审查成果物,供 PR 描述、文档、评审留痕 |
| **Step 9 持久化** | 若 `gstack-review-log` 可用则记录审查结果,否则静默跳过 | 供跨次审查去重抑制与 /ship 识别,形成审查历史 |
### 2.2 档位
审查默认运行在 `standard` 档位(全层审查:Layer 1 链路分解 + Layer 2 客观评分 + 报告)。`tier` 参数支持 `fast` / `standard` / `strict` 三档,其中 `fast` / `strict``.code-review.yaml` 配置或 MCP prompt 调用时指定;`/code-review-graph-unified-review` 命令固定 `standard`。覆盖度目标固定为双目标:全库 ≥85% **且** 高风险 ≥95%project-review 三件套口径见 §3.5.4)。
---
## 3. project-review:项目级代码审查
### 3.1 范围解析(Step 0
| 用户指令 | 解析结果 |
| --- | --- |
| "对项目代码进行全面审查" / "全面审查" / "整个项目" | `scope="whole-project"` |
| "对支付功能的代码进行审查" / "审查 auth 模块" | `scope="feature"`, `target=<关键词>` |
### 3.2 全项目流程(whole-project
| 步骤 | 做了什么 | 起到什么功能 |
| --- | --- | --- |
| **Step 0 范围解析** | 从用户指令解析出 `scope="whole-project"` | 明确本次审查是整个项目而非 diff/单功能 |
| **Step 1 图谱就绪 + 最小上下文** | `build_or_update_graph_tool()` 确保图谱最新;`get_minimal_context_tool(task="project review")` 拿节点/边/社区/风险概览 | 保证后续查询基于最新代码结构;最小上下文把全库概览压缩为几百 token,避免主上下文被 500+ 文件淹没 |
| **Step 2 架构全景** | `get_architecture_overview` + `list_communities`(模块地图) | 先建立全局模块结构认知,为后续定位高风险区域提供上下文 |
| **Step 3 高风险定位** | `get_knowledge_gaps`(未测试热点/孤立节点)+ `get_hub_nodes`(热点)+ `get_bridge_nodes`(瓶颈)+ `find_large_functions`(超大函数)+ `get_surprising_connections`(异常耦合) | 图论分析(节点度、介数中心性、社区划分、测试覆盖边),不读代码内容——它能告诉你"这个函数被 20 处调用,改动影响大",但说不出代码写得如何。 |
| **Step 4 全量客观评分** | `score_review_tool(all_files=True)` 评分全部源文件;`community_health_tool()` 前置检查图谱健康 | 对每个源文件跑 5 项客观指标,文本质量扫描(_iter_source_lines 读文件逐行匹配正则),不关心调用关系——它能告诉你"这个文件异常处理薄弱",但看不出它是架构瓶颈。社区归属率 <90% 需先 `postprocess` 重建,否则覆盖度失真。 |
| **Step 5 并行深读流水线** | `deep_read_plan_tool(target_coverage=85, include_prior=True)` 生成风险加权分组 → 每次并行派发 4-6 个 explore 子代理分组深读(每组 ≤40 文件)→ 子代理返回 `deep_read_files` + findings(每条必须带 file:line 证据)→ `coverage_tool(deep_read_files=<全清单>, gate="both", include_prior=True)` 复算 | 主上下文不逐文件读 500+ 文件,改为子代理流水线分摊。**这是 whole-project 覆盖达标的唯一可行路径**。深读计划按风险权重贪心,85% 全库计划先选满全部高风险文件,天然逼近高风险 95%。详见 §3.6 |
| **Step 6 覆盖度门禁与补轮** | 双目标未达标(全库 <85% 或 高风险 <95%)→ 按 `coverage_tool` 返回的 `priority_deep_read_files` 补派 1-3 个子代理;`silent_files` 随机抽 15% 深读(G2),发现 ≥1 major 则升级全量 | 可量化兜底"该读的都读了",防止审查深度由感觉决定 |
| **Step 7 合并去重** | `dedupe_findings_tool` 合并去重 + PR 质量分 | 统一 findings 清单,量化整体质量 |
| **Step 8 人工裁决 + 门禁** | 只读裁决;任一 🔴 blocker → `❌ FAIL` | 人工把关 + 明确的审查结论 |
| **Step 9 报告 + 自检** | `generate_report_tool(review_data=..., output_path="docs/reviews/...")` 产出 HTML + MarkdownStep 9.5 打开生成的 md 自检(问题清单数、三要素、客观指标表);Step 9.6 跑 `verify-report.ps1` 命名自检 | 全项目体检报告,可分发存档;自检防"空报告/漏命名"回归 |
| **Step 10 持久化** | `save_coverage_index_tool(deep_read_files=<本轮全量深读清单>)` 写跨轮覆盖索引(file + per-file SHA | 下一轮 `include_prior=True` 自动复用未变更文件,多轮后增量归零即全覆盖 |
### 3.3 单功能流程(feature
| 步骤 | 做了什么 | 起到什么功能 |
| --- | --- | --- |
| **Step 0 范围解析** | 从用户指令解析出 `scope="feature"`, `target=<关键词>` | 明确只审查目标功能,缩小范围 |
| **Step 1 图谱就绪** | `build_or_update_graph_tool()` + `get_minimal_context_tool` | 保证查询基于最新代码 |
| **Step 2 定位功能代码** | `semantic_search_nodes(query=target)`(语义定位)+ `query_graph(children_of, target=<模块>)`(模块展开)→ 聚合功能涉及文件 `files=[...]` | 把"功能关键词"转化为具体文件清单,确定审查对象 |
| **Step 3 影响面分析** | `get_impact_radius(changed_files=files)` | 找出该功能波及的调用方/依赖方,评估改动影响范围 |
| **Step 4 客观评分** | `score_review_tool(changed_files=files + 影响文件)` | 对功能代码跑 5 项客观指标 |
| **Step 5 链路分解 + 深读** | Layer 1 八分类 + CRITICAL 检查(聚焦功能文件);文件多时也可用子代理分组深读 | 逐类排查功能内的高风险缺陷 |
| **Step 6 覆盖度计算** | `coverage_tool(deep_read_files=<功能文件>, gate="both")` 复算 | 功能级覆盖兜底 |
| **Step 7 合并去重** | `dedupe_findings_tool` 合并去重 + PR 质量分 | 统一 findings 清单 |
| **Step 8 人工裁决 + 门禁** | 只读裁决;任一 🔴 blocker → `❌ FAIL` | 人工把关 + 结论 |
| **Step 9 报告 + 自检** | `generate_report_tool(format="both")`md 自检 + verify-report.ps1 | 功能审查报告,防回归 |
| **Step 10 持久化** | `save_coverage_index_tool` 写覆盖索引 | 跨轮增量复用 |
### 3.4 `all_files` 参数
`score_review_tool(all_files=True)` 评分图谱内**全部源文件**,忽略 `changed_files` 与 git diff,用于全项目审查。默认 `False`
### 3.5 覆盖度保障机制
> 目标:保证"该深读的都读了,不该漏的没漏",用可量化的方式兜底非热点文件。本节是 project-review 的增强约定,unified-review 可复用(把"深读名单"理解为"变更文件 + 高风险文件")。
#### 3.5.1 三层覆盖模型
| 层 | 对应步骤 | 覆盖范围 | 手段 |
| --- | --- | --- | --- |
| L1 全量机械扫描 | Step 4 `all_files=True` | **100% 文件** × 5 项指标 | 正则启发式,查机械性风险 |
| L2 并集信号 → 深读名单 | Step 3 + 本节 | 任一信号点名的文件 | 多信号并集,无上限 |
| L3 深度审查 | Step 5.5 子代理深读 | 名单内全部文件 | 逐文件通读(大文件分段),产出带 file:line 证据的 finding |
#### 3.5.2 深读名单(并集信号,无上限)
```text
进名单 = 任一命中:
① 拓扑热点:hub / bridge / large_functions / knowledge_gaps 标记
② Step 4 指标 fail 或 warn 的文件
③ churn 热点(≥3 次提交)
④ 测试缺口热点(untested_hotspots
任一命中 → 必须深读(无上限)
未被任何信号点名 → 记入"未深读文件清单",在报告中显式列出
```
关键原则:**不覆盖不可怕,不知道没覆盖才可怕**。报告必须附带"未深读文件清单",把覆盖边界显式化。
#### 3.5.3 静默文件抽检
- 从未被信号点名的文件("静默文件")随机抽 **15%** 深读
- **升级规则**:抽检中发现 ≥1 个 major → 该文件升级为全量深读,并触发同社区/同类文件追加抽检一轮
#### 3.5.4 覆盖度度量(文件数口径 + 三件套质量口径,`gate="both+line"`
**由引擎 `coverage_tool` 自动计算**`code-review-graph` v2.5.0+,三件套口径 v2.5.1 起),审查代理只需传入 `deep_read_files`(本轮实际深读清单)、`gate` 与三件套数据(`file_read_ranges` / `file_semantic_units`)。
```text
双重文件数口径:
全库覆盖 coverage_pct = 已深读文件数 / 全部源文件数
高风险覆盖 high_risk_coverage_pct = 已深读高风险文件数 / 信号点名文件数
三件套质量口径(gate="both+line" 时生效):
① 单元完整性 unit_coverage_pct = 语义单元无缺口的深读文件数 / 深读文件数
语义单元 = 图谱 Function/Class/Test 节点;子代理上报 semantic_units 与其差集必须为空
巨型文件豁免(最大单元行占比 >80%,如 migrations.rs run_migrations 98%)→ 仅按行覆盖校验
② 行覆盖 line_coverage_pct = 行覆盖达标的深读文件数 / 深读文件数
分子 = union(read_ranges);分母 = 真实文件行数(非图谱 line_end,±1 偏差已修正)
③ 防伪抽验(主代理执行,非引擎) = 每组抽 2 文件 × 2-3 单元回读比对 note,每波 ≤40 次;
结果落盘 spot_check_*.json 并注入 review_data.spot_check(顶层字段),
由 verify-spot-check.ps1 强制校验"已执行"(引擎无法防假读,抽样是唯一手段)
gate 取值:
"overall" — 只看全库口径
"high_risk" — 只看高风险口径
"both" — 全库 + 高风险都要达标(保持原语义,向后兼容)
"both+line" — both + 行覆盖 ≥95% + 单元完整性无缺口(whole-project 新默认,见下)
```
**目标值(固定 standard 档位)**`gate="both+line"` 时要求**全库 ≥85% 且 高风险 ≥95% 且 行覆盖 ≥95% 且 单元完整性无缺口**(`line_target=95``unit_target=100` 差集为空)。`target_reached=false` → 报告顶部标 🔴 覆盖不足,并给出 `remaining_files_to_target`(还差几个文件)与 `priority_deep_read_files`(按风险权重降序的待深读清单)驱动补轮;`both+line` 额外返回 `line_gap_files` / `unit_gap_files` / `unit_exempt_files` 定位具体缺口文件。
**增量语义(include_prior=True**`save_coverage_index_tool` 在每轮报告后写 `.code-review-graph/coverage-index.json`v2:相对路径 → per-file SHA + `ranges` 行区间)。SHA 来源为图谱 `nodes.file_hash`(一次 SQL 查询,零 subprocess——已修复 v2.5.0 逐文件 `git hash-object` 超时问题)。下一轮 `include_prior=True` 时自动复用 SHA 未变文件的深读状态**及行区间**,只要求重读新增/变更文件,多轮后增量归零即全覆盖。
> ⚠️ **图谱同 range 多节点噪声豁免**:TS/TSX 图谱可能对**同一行**解析出多个箭头函数节点(如 `const a = ..., x = ...` 各占一行但 range 相同),导致单元完整性差集永远非空(one-to-one 匹配下无解)。处理原则:**仅当该文件行覆盖已达 100%(或 ≥95%)**,判定为图谱解析噪声,将该文件从 `unit_gap` 中豁免并在报告显式标注"图谱同 range 多节点豁免",不要反复补读。`unit_gap_files` 的判定须结合具体 uncovered 单元名与行号人工确认是否噪声(对比源码该行是否有多个符号)。
> ⚠️ **0 字节空文件处理**:深读计划可能选中空文件(如 `web/src/components/evm/EvmConfigModal.tsx`0 字节且无引用)。引擎对空文件报 `unreadable/empty file` 计入 `line_gap_files`。处理:验证文件字节数与引用情况,确认死文件后从深读清单移除,并作为一条 finding(死代码残留)记录在报告中。
#### 3.5.5 三道闸门
- **G1 名单完整性**:名单外文件须确认"被评估过"而非"被忽略",未被任何信号点名的文件记入**未深读文件清单**并在报告显式列出
- **G1.5 三件套质量门禁复核**(v2.5.1+):报告前复核三件套聚合结果——`line_gap_files unit_gap_files` 必须为空(已补读至空);**图谱同 range 多节点噪声豁免的文件须在报告中显式列出**(每文件注明"行覆盖已 100%,图谱同 range 多节点豁免"),0 字节空文件须从清单移除并单列 finding;`verified_files``coverage_tool``deep_read_files` 一致;防伪抽验记录(抽了几组/几个单元/有无假读)附入报告,并由 `verify-spot-check.ps1`Step 8.8)强制校验
- **G2 抽检执行**:用 `coverage_tool` 返回的 `silent_files` 随机抽 15% 深读;抽检中发现 ≥1 个 major → 该文件升级为全量深读,并触发同社区/同类文件追加抽检一轮;抽检记录附入报告(抽了几份、发现几个 major、有无升级)
- **G3 覆盖度收尾**`coverage_tool` 自动计算,`gate="both+line"` 时全库 ≥85% 且 高风险 ≥95% 且 行覆盖 ≥95% 且 单元完整性无缺口才算达标,低于目标在报告顶部告警
#### 3.5.6 执行前的前置检查
运行覆盖度流程前先确认图谱健康——调用 `community_health_tool`
1. `attribution_pct` = `nodes.community_id` 非空 / 非 File 节点数 的百分比
2. `needs_postprocess=true`(归属率 <90%)→ 先跑 `code-review-graph postprocess` 重建社区归属,再开始审查
> 已知问题:`incremental_detect_communities` 在 nodes 重建后可能因 `community_id` 全 NULL 而判定"无社区受影响"跳过写回,导致 `communities.size` 与 `nodes.community_id` 失同步。`community_health_tool` 可检出该状态,遇此情况用全量 `postprocess` 修复。
#### 3.5.7 报告覆盖度透传(防 0/0 渲染)
`coverage_tool` 的返回值必须**完整透传**进 `generate_report_tool``review_data.coverage`(全部字段:`coverage_pct`/`high_risk_coverage_pct`/`grade`/`deep_read_count`/`total_files`/`high_risk_total_files`/`high_risk_deep_count`/`deep_read_weight`/`total_weight`/`target_reached`/`target`/`gate`/`remaining_files_to_target`/`remaining_weight_to_target`/`priority_deep_read_files`/`uncovered_files`/`silent_files`/`note`)。**不要手挑子集**——`build_report_data` 只读固定键名,缺字段会导致报告覆盖度区块渲染成 `0/0``N/A`。报告会自动渲染 `## 覆盖度` 区块。
---
### 3.6 并行深读流水线(whole-project 强制)
> 主上下文无法逐文件深读 500+ 文件;必须用并行 explore 子代理分组深读,否则全库覆盖永远卡在 2-5%。覆盖率为**文件数口径**,且每个深读文件需通过**三件套质量门禁**(单元完整性 / 行覆盖 / 防伪抽验,见 §3.5.4)。
### 3.6.1 流程
```text
a. deep_read_plan_tool(target_coverage=85, include_prior=True)
→ 返回 groups[{name, weight, files[]}] + remaining_files
(引擎按风险权重 + 目录贪心分组,每组 ≤40 文件;include_prior 排除上轮已深读未变更文件)
b. 按组并行派发 explore 子代理(每次 4-6 个,分多批),每组深读全部文件,返回(落盘到临时目录):
- outputs[]:每文件含 path / total_lines / read_ranges[[s,e]..] /
semantic_units[{range,kind,name,note}] / findings[]
- findings[]:每条含 severity/category/confidence/file:line/message/fix
【证据铁律】无 file:line 证据的条目视为未深读 → 该文件须重读
c. 每波子代理完成后跑三件套门禁(引擎原生支持):
coverage_tool(deep_read_files=<verified_files>, gate="both+line",
file_read_ranges=<{rel:[[s,e]..]}>, file_semantic_units=<{rel:[...]}>)
→ line_gap_files unit_gap_files → 补读队列;verified → 计入本轮 deep_read_files
(引擎不可用时兜底:python skills/project-review/scripts/aggregate_deep_read.py <repo_root> <落盘目录>
d. 防伪抽验:每组抽 2 文件、该文件抽 2-3 个语义单元回读比对 note(每波 ≤40 次);
结果落盘 spot_check_<batch>.json,抽到假读 → 该组重读并升级抽验率
e. 主代理合并全部 verified_files(去重)→
coverage_tool(deep_read_files=<全清单>, gate="both+line", include_prior=True) 复算
include_prior 自动复用跨轮索引中 SHA 未变文件及其 read_ranges
f. 未达标(文件数 <85%/<95% 或行覆盖 <95% 或单元有缺口)→ 对 priority_deep_read_files / line_gap_files / unit_gap_files 补派 1-3 个子代理 → 循环直至全达标
g. G2:对 silent_files 随机抽 15% 深读(见 §3.5.5)
h. 报告生成前:聚合全部 spot_check_*.json → 注入 review_data.spot_check(顶层字段)
i. 报告生成 + 命名自检后:verify-spot-check.ps1Step 8.8)→ save_coverage_index_tool(deep_read_files=<verified_files>, file_read_ranges=<ranges>) 写跨轮索引
```
### 3.6.2 子代理分组参考(AuraSpace 实测,约 508 文件)
| # | 组 | 约文件数 |
| --- | --- | --- |
| 1-2 | `server/src/api`1/2、2/2 | 60 |
| 3-4 | `server/src/services`1/2、2/2 | 66 |
| 5 | `server/src/domain` + `deepwiki` + `infrastructure` | 88 |
| 6 | `server/tests` + `bin` + `models` | 17 |
| 7-8 | `web/src/views`1/2、2/2 | 60 |
| 9-11 | `web/src/components`1/3、2/3、3/3 | 110 |
| 12 | `web/src/store` + `utils` + `api` | 50 |
| 13 | `web/tests` + `scripts` + SQL | 57 |
> 实际分组以 `deep_read_plan_tool` 返回为准;每次并行 **4-6 个**子代理,其余排队,避免 MCP 并发压力与上下文风暴。
### 3.6.3 质量控制与防伪
| 风险 | 对策 |
| --- | --- |
| 子代理"声称读了"但没真读 | 强制三件套字段(total_lines/read_ranges/semantic_units+ findings 带行号证据;聚合脚本按①单元差集+②行并集双校验;主代理③抽样回读 |
| 子代理漏读文件 | outputs 与分组清单 diff,漏读文件计入覆盖率缺口,触发补轮 |
| 子代理宽 range 冒充全读 | 单元完整性 one-to-one 匹配:一个上报 range 只能覆盖一个单元,无法用整文件 range 覆盖所有单元 |
| 各子代理口径不一 | 统一八分类 + CRITICAL 子轮 + severity/confidence 标准(子代理 prompt 模板) |
| 增量掩盖新代码 | `include_prior=True` 按 per-file SHA 判定;变更文件自动失效重读 |
| 并发压力 | 每批 4-6 个并行,其余排队;batch_size 40 控制单组体量 |
| 主上下文被大 JSON 撑爆 | 落盘机制:子代理写临时文件,主代理只读聚合摘要 |
### 3.6.4 "深读"的含义边界
- **文件级保证(可量化)**`deep_read_files` 是文件数口径,覆盖 85% 表示 85% 的文件被"读过"(高风险子集要求 95%)
- **行级保证(v2.5.1 起可量化)**:`read_ranges` 的并集 / 真实文件行数 ≥95%;分母为真实文件行数(图谱 `line_end` 有 ±1 偏差,已弃用)。子代理对每个文件上报 `total_lines` + `read_ranges` + `semantic_units`,聚合脚本/引擎逐文件校验
- **单元级保证(v2.5.1 起可量化)**:`semantic_units` 与图谱 Function/Class/Test 节点差集必须为空(one-to-one 匹配,宽 range 不能冒充);巨型文件(最大单元行占比 >80%)豁免,仅按行覆盖校验
- **防伪机制**findings 必须带 `file:line` 证据;主代理每轮 ≤15 次单元回读(引擎无法防假读,抽样是唯一手段);抽到假读 → 该组重读并升级抽验率
### 3.6.5 跨轮增量(多轮累积)
- 每轮报告后 `save_coverage_index_tool``.code-review-graph/coverage-index.json`v2:相对路径 → per-file SHA + `ranges` 行区间)
- SHA 来源为图谱 `nodes.file_hash`(一次 SQL 查询,零 subprocess;已修复 v2.5.0 逐文件 `git hash-object` 超时)
- 下一轮 `deep_read_plan_tool` / `coverage_tool``include_prior=True` 自动复用 SHA 未变文件及其行区间 → 增量任务 = 新增文件 + 变更文件
- 多轮后增量归零即实现全库全覆盖,避免每轮从 2-5% 起步
### 3.6.6 行级覆盖率执行步骤(新窗口 / 全新审查)
> 本节给出"行级覆盖率如何落地到一次实际审查"的**分步操作**。三件套门禁中,行覆盖是唯一有精确数值的维度(`union(read_ranges) / 真实行数 ≥95%`),本节聚焦它的完整执行链路;单元完整性(差集=空)与防伪抽验在 §3.6.1/§3.6.3 已述。
**行级覆盖率的口径(引擎 `scoring.py::compute_coverage`**
```text
行覆盖率(每文件) = |union(read_ranges)| / len(Path(repo/f).read_text().splitlines())
行覆盖达标 = 行覆盖率 >= 95% line_target,可参数覆盖)
line_coverage_pct = 行覆盖达标的深读文件数 / 深读文件数
line_gap_files = 行覆盖率 <95% 的文件 [path, coverage_pct, total_lines, covered_lines]
```
- **分母 = 真实文件行数**(读取源文件,`_real_line_count` 带缓存)——**不用图谱 `line_end`**(实测 ±1 偏差)
- **分子 = `read_ranges` 并集**——子代理实际上报的读取区间(分段读即天然分段)
- **无 ranges 上报的文件**:行覆盖视为通过(不设硬卡),仅依赖单元完整性 + 抽验
**执行步骤(主代理在全新会话中)**
```text
Step 0 前置(新窗口)
- 确认无残留进程:Get-Process code-review-graph → 无输出
- 首次调用 get_minimal_context_tool 会拉起 MCP serve(加载新引擎,含 both+line
Step 1 建图 + 概览
- build_or_update_graph_tool() 图谱最新
- get_minimal_context_tool(task="project review") 全库概览
Step 2 架构 + 高风险扫描(同旧流程,产出风险权重信号)
Step 3 客观评分
- score_review_tool(all_files=True) + community_health_tool(前置健康检查)
Step 4 深读分组
- deep_read_plan_tool(target_coverage=85, include_prior=True)
→ groups[{name, weight, files[]}](≤40/组)
Step 5 并行派发子代理(严格模式)
- 每次 4-6 个 explore 子代理,prompt 见 references/deep-read-pipeline.md §二
- 每文件必须返回 total_lines / read_ranges[[s,e]..] / semantic_units[{range,kind,name,note}]
- semantic_units 必须逐一列出图谱全部单元(含 Props/interface/小函数,严格模式)
- 结果落盘到临时目录(勿回传大 JSON)
Step 6 聚合脚本(每波后)
- python skills/project-review/scripts/aggregate_deep_read.py <repo_root> <落盘目录>
- 输出 verified_files / line_gap_files / unit_gap_files / unit_exempt_files
- 行覆盖判定就在这里:line_gap_files 列出所有 <95% 的文件
Step 7 引擎门禁(行级覆盖率正式数值)
- coverage_tool(deep_read_files=<verified_files>, gate="both+line",
file_read_ranges=<{rel:[[s,e]..]}>, file_semantic_units=<{rel:[...]}>)
- 返回 line_coverage_pct / unit_coverage_pct / line_gap_files / unit_gap_files / unit_exempt_files
- ⚠️ 必须传 file_read_ranges/file_semantic_units,否则行/单元覆盖不计算
Step 8 补轮(禁止降级)
- line_gap_files unit_gap_files 非空 → 对缺口文件重派子代理补报/重读
- 禁止回退 gate="both" 静默跳过三件套;必须补至空,或报告显式标注 🔴
Step 9 报告
- generate_report_tool(review_data.coverage = coverage_tool 全量透传)
- "## 覆盖度" 区块须含行覆盖/单元覆盖(line_coverage_pct/unit_coverage_pct
Step 10 持久化
- save_coverage_index_tool(deep_read_files=<verified_files>, file_read_ranges=<ranges>)
- 写 v2 索引(sha + rangesSHA 来自 nodes.file_hash<1s
```
**新窗口首轮验证(Step 0 后建议先跑一次冒烟)**
```text
coverage_tool(deep_read_files=["server/src/services/auth_service.rs"],
gate="both+line",
file_read_ranges={"server/src/services/auth_service.rs": [[1,66]]},
file_semantic_units={"server/src/services/auth_service.rs": [全部 6 单元]})
→ 期望 line_coverage_pct=100, unit_coverage_pct=100, line_gap_files=[]
(即 V2.1 E2E 验证通过,确认引擎加载了新代码)
```
**常见坑**
- 子代理漏 `read_ranges` → 该文件行覆盖不计算(不算 gap 但也不验证),依赖单元完整性兜底;严格模式应要求 read_ranges 覆盖整文件
- 大文件分段时 read_ranges 各段要有重叠边界或连续覆盖,避免 1 行缝隙拉低覆盖率
- 全库 508 文件行覆盖聚合约 1-2s(分母读取带缓存);`save_coverage_index` 用 file_hash 后 <1s(不再有逐文件 git subprocess 超时)
### 3.6.7 实测踩坑与绕行方案(2026-08-18 全库审查实战记录)
> 本节是 508 文件全库审查(`full-project-review-2026-08-18-144500`)实测中暴露的、文档正文未覆盖的坑与对应解法。深读流水线各环节的坑按出现顺序记录。
| # | 环节 | 坑 | 症状 | 解法 |
| --- | --- | --- | --- | --- |
| 1 | 子代理输出落盘 | 子代理 JSON 是**容器格式** `{"group":..., "outputs":[{path, read_ranges, semantic_units, findings}]}`,而 `aggregate_deep_read.py` 期望**单文件记录**(每条记录的 `path` 在顶层) | 聚合脚本 `verified=0`、全部文件被跳过,且无任何报错 | 先**解包**:读容器 → 把 `outputs` 数组逐条拆成独立 JSON 文件(`batchN_idx_<path>.json`)→ 再跑聚合脚本。可用一段 Python 脚本完成,见下 |
| 2 | 聚合脚本 | 子代理用 `write` 写 JSON 可能带 **UTF-8 BOM** | 报错 `Unexpected UTF-8 BOM (decode using utf-8-sig)` | 聚合前批量剥 BOM:读字节,若前 3 字节为 `EF BB BF` 则写入 `bytes[3:]` |
| 3 | 单元完整性补读 | 首轮聚合 `unit_gap` 可达 **92 个文件**(子代理漏报 serde `visit_*` 小方法、几行的 struct/Props interface | `unit_gap_files` 非空 → 三件套门禁不通过 | **不要整文件重读**。生成 `gap_manifest.json`(含每文件 `uncovered` 单元及精确 range),派 1-2 个"补读子代理"按 range 精准读取缺失单元、产出 `supplement.json`,主代理按 `(path, range)` 合并回原 payload 后再聚合 |
| 4 | 单元完整性 | 图谱对**同一行解析出多个箭头函数节点**(如 `AgentConfigPanel.tsx``a`/`x` 同 range 420`DataPermissionManagement.tsx``d`/`childrenOf` 同 range 76 | 该文件 `total_units` 高但永远 `covered_units` 差 1-3 个,**one-to-one 匹配不可能满足**(一个 range 只能匹配一个单元) | 判断为图谱解析噪声:文件行覆盖已 100% 即视为已深读,在报告中显式标注"该文件存在图谱同 range 多节点,单元完整性豁免";不要反复重读 |
| 5 | 深读清单 | 深读计划可能选中 **0 字节空文件**(如 `web/src/components/evm/EvmConfigModal.tsx` | `line_gap_files``unreadable/empty file`,且该文件无任何引用(死文件残留) | 验证文件大小与引用:空文件 + 无引用 → 从深读清单移除,作为一条 finding(死代码)单独记录 |
| 6 | 补读子代理 | 补读子代理若只返回"补了 X 个单元"的**一句话**而不落盘补充 JSON | 主代理拿不到补充的 semantic_units,无法合并 | 强制补读子代理也必须**落盘** `supplement*.json`(结构化 range+note),主代理脚本合并后重跑聚合 |
| 7 | MCP 大 JSON | `dedupe_findings_tool` / `generate_report_tool` 直传 400+ 条 findings 时 **MCP JSON 解析失败**`Invalid escape character` / `Expected '}'`) | 工具调用直接报 invalid input,报告无法生成 | **绕行**:直接在 Python 里调用引擎函数 `code_review_graph.tools.generate_report_func(review_data, output_path=...)` / `coverage_func(...)``sys.path.insert(0, <CRG 引擎路径>)` 后 import),输出与 MCP 工具完全一致(见 §4.5) |
| 8 | 报告统计 | MD 报告的"问题统计"只读 `counts`**`critical` / `informational` 键**(不读 blocker/major/minor | 传 `{"blocker":20,"major":141,"minor":315}` 时统计显示 "20 严重 · 0 次要"major 数丢失 | `counts``{"critical":<blocker数>, "major":<major数>, "informational":<minor数>}` 才能正确渲染 |
**解包 + 剥 BOM 参考脚本(主代理每轮可复用)**
```python
# 1) 剥 BOM
for f in glob.glob(dir + "/*.json"):
b = Path(f).read_bytes()
if b[:3] == b"\xef\xbb\xbf":
Path(f).write_bytes(b[3:])
# 2) 解包 outputs → 单文件记录
data = json.load(open(batch, encoding="utf-8"))
for i, rec in enumerate(data["outputs"]):
out = dir / f"{basename}_{i}_{rec['path'].replace('/','_')}.json"
out.write_text(json.dumps(rec, ensure_ascii=False), encoding="utf-8")
```
**合并 supplement 参考脚本**
```python
bypath = defaultdict(list) # path -> [semantic_units...]
for s in supp1 + supp2: bypath[s["path"]] += s["semantic_units"]
for rec in payloads: # 每个单文件记录
if rec["path"] in bypath:
have = {tuple(u["range"]): u for u in rec.get("semantic_units", [])}
for u in bypath[rec["path"]]:
have.setdefault(tuple(u["range"]), u) # 按 range 幂等合并
rec["semantic_units"] = list(have.values())
```
**教训**:深读流水线的"三件套门禁"把质量控制在引擎侧,但**子代理与主代理之间的数据契约(容器格式、BOM、补读落盘)是纯人工编排**,任何一处契约不一致都会让 verified 计数归零或三件套不达标。建议每波子代理完成后先跑一次聚合脚本看 `verified` 数,而非等到全部结束才校验。
---
## 4. 审查调用的 MCP 工具
### 4.1 核心审查工具
| 工具 | 功能 |
| --- | --- |
| `score_review_tool` | **客观 Layer-2 审查指标**:SQL 风险、异常分支覆盖、代码冗余率、高风险场景密度、漏洞启发式 5 项,每项带 good/warn/fail 分级、阈值与证据。支持 `all_files` 全量评分、`include_churn``llm_judged` 列出需 LLM 补判的指标 |
| `dedupe_findings_tool` | **Finding 合并去重**:按 `path:line:category` 指纹合并;多源确认置信 +1(上限 10);低置信移附录或抑制;计算 PR 质量分 `max(0, 10 - (critical×2 + informational×0.5))`;支持抑制历史已跳过项 |
| `generate_report_tool` | **审查报告生成**:中文 HTML + Markdown(默认 `format="both"`);`output_path` 传归档目标文件名(含 `docs/reviews/` 前缀,不含扩展名) |
| `coverage_tool` | **覆盖度计算**v2.5.0 / 三件套 v2.5.1):传入 `deep_read_files` + `gate`(默认 `high_risk`,可选 `overall` / `both` / `both+line`),引擎自动算双重文件数口径(全库 / 高风险)、未深读清单、静默文件清单、达标状态与待补深读优先级。`gate="both+line"` 时额外支持 `file_read_ranges` / `file_semantic_units`,返回行/单元覆盖三件套(`line_coverage_pct` / `unit_coverage_pct` / `line_gap_files` / `unit_gap_files` / `unit_exempt_files` |
| `deep_read_plan_tool` | **深读分组规划**v2.5.0):`deep_read_plan_tool(target_coverage=85, include_prior=True, batch_size=40)`**无 `gate` 参数**),按风险权重 + 目录贪心生成 `groups[{name, weight, files[]}]` + `remaining_files`,主代理据此并行派发子代理 |
| `save_coverage_index_tool` | **跨轮覆盖索引持久化**v2.5.0 / v2 索引):把本轮深读清单(file + per-file SHA + 可选 `ranges` 行区间)写入 `.code-review-graph/coverage-index.json`,供下一轮 `include_prior=True` 增量复用。v2.5.1 起 SHA 来源为图谱 `nodes.file_hash`(零 subprocess,根治逐文件 `git hash-object` 超时) |
| `community_health_tool` | **社区归属健康检查**v2.4.0+):返回 `nodes.community_id` 归属率与 `needs_postprocess`,前置检查用 |
### 4.2 图谱上下文工具
| 工具 | 用途 |
| --- | --- |
| `build_or_update_graph_tool` | 确保图谱最新 |
| `get_minimal_context_tool` | 全库概览压缩(节点/边/社区/风险/受影响的流),project-review 主上下文第一步 |
| `get_review_context_tool` | 变更文件 + blast radius + 源码片段 + 审查指引 |
| `detect_changes_tool` | 风险评分 + 变更函数 + 测试缺口 + 受影响流 |
| `get_affected_flows_tool` | 受影响执行流 |
| `query_graph_tool` | 调用方/被调用方/测试/继承查询 |
### 4.3 图谱全景工具(project-review 用)
| 工具 | 用途 |
| --- | --- |
| `get_architecture_overview_tool` | 社区耦合总览 |
| `list_communities_tool` | 模块清单 |
| `get_knowledge_gaps_tool` | 结构弱点:孤立节点、未测试热点 |
| `get_hub_nodes_tool` | 最连接节点(架构热点) |
| `get_bridge_nodes_tool` | 架构瓶颈 |
| `find_large_functions_tool` | 超大函数/类 |
| `get_surprising_connections_tool` | 意外跨社区耦合 |
| `semantic_search_nodes_tool` | 按关键词定位代码(单功能) |
| `get_impact_radius_tool` | 单功能的 blast radius |
### 4.4 工具调用链
```text
unified-reviewdiff:
build_or_update_graph → get_review_context → detect_changes
→ score_review_tool → [specialist 子代理] → dedupe_findings_tool → generate_report_tool
project-review(全项目):
build_or_update_graph → get_minimal_context → 架构全景 + 高风险定位(6 个图谱工具)
→ score_review_tool(all_files=True) + community_health_tool(前置检查)
→ deep_read_plan_tool → [4-6 并行 explore 子代理 × N 批] → coverage_tool(gate="both+line")
→ [未达标]补轮 → [G2]silent 抽检 15%
→ dedupe_findings_tool → generate_report_tool(coverage 完整透传)
→ verify-report.ps1(命名自检) → save_coverage_index_tool(跨轮索引)
project-review(单功能):
build_or_update_graph → get_minimal_context → semantic_search_nodes / query_graph 定位功能文件
→ get_impact_radius → score_review_tool(changed_files=<功能文件>)
→ coverage_tool(deep_read_files=<功能文件>) → dedupe_findings_tool → generate_report_tool
```
### 4.5 引擎函数直调绕行(MCP 大 JSON 失败时的兜底)
`dedupe_findings_tool` / `generate_report_tool` / `coverage_tool` 通过 MCP 传输 JSON 参数,当 findings 超过数百条(如全库审查 476 条)时可能因 **MCP 参数解析失败**`Invalid escape character` / `Expected '}'`)而无法调用。此时可**绕开 MCP 直调引擎 Python 函数**,输出与 MCP 工具完全一致:
```python
import sys, json
sys.path.insert(0, r"D:\code-review-graph\code-review-graph-main") # CRG 引擎本地路径
import code_review_graph.tools as t
# 等价于 generate_report_tool
res = t.generate_report_func(
review_data, # 与 MCP 版同构的 review_data dict
output_path=r"D:\AuraSpace\docs\reviews\full-project-review-2026-08-18-144500",
repo_root=r"D:\AuraSpace",
format="both",
)
# 等价于 coverage_tool(gate="both+line")
res = t.coverage_func(
deep_read_files=paths,
gate="both+line",
file_read_ranges=ranges_map, # {rel: [[s,e]..]}
file_semantic_units=units_map, # {rel: [{range,kind,name,note}..]}
repo_root=r"D:\AuraSpace",
)
```
> 触发条件:工具调用返回 invalid input / JSON Parse error,且 payload 确认超过 MCP 承载上限(实测 ≥400 条 findings 时出现)。直调入口统一为 `code_review_graph.tools` 下的同名函数;`coverage_func` 的签名见 §3.6.6 的 `compute_coverage` 说明。
### 4.6 深读子代理类型选择
深读子代理使用 **`explore` 类型**(与 project-review skill 及实际审查一致)。子代理需**写 JSON 落盘**回传三件套数据(容器格式 `{group, outputs[]}`,每条含 path/read_ranges/semantic_units/findings),因此派发前须确认子代理工具集包含 **write 落盘能力**。若子代理无法落盘,主代理拿不到三件套数据,verified 计数会归零。
---
## 5. 5 项客观指标详解
`score_review_tool`unified-review Step 3 / project-review Step 4)计算 5 项客观 Layer-2 指标。这些指标由**静态正则启发式**实现(源码:`code_review_graph/scoring.py`),扫描变更文件的行级文本(`_iter_source_lines`),不执行代码、不理解语义,因此**会产生误报和漏报,需人工核实后再行动**。阈值定义在 `scoring.py``THRESHOLDS`
### 5.1 指标总览
| 指标 | 值类型 | 评级方向 | good | warn | fail |
| --- | --- | --- | --- | --- | --- |
| `SQL 注入风险` | 命中行数 | 越低越好 | `= 0` | `1-2` | `≥ 3` |
| `异常分支覆盖` | 异常行占比 % | 越高越好 | `≥ 50%` | `30%50%` | `< 30%` |
| `代码冗余率` | 重复行占比 % | 越低越好 | `≤ 10%` | `10%20%` | `> 20%` |
| `high_risk_density` | 覆盖行占比 % | 越高越好 | `≥ 90%` | `≥ 70%` | 其余;无高风险行则为 `na` |
| `vulnerability_risk` | 命中行数 | 越低越好 | `= 0` | `1-2` | `≥ 2` |
**整体客观评级取 5 项中最差项**`worst`,任一 fail → 整体 FAIL;否则任一 warn → WARN;全 good → GOOD)。
### 5.2 这些指标有什么用处
核心用途一句话:**把"凭感觉"的审查,变成"有证据、有优先级"的审查清单**。它们不是给代码打分的考试成绩,而是**审查者的导航仪**——告诉你这份代码里"哪些行值得停下来细看",并给这个判断一个可复现的数字证据。
| 指标 | 实际帮你做什么 | 没有它的代价 |
| --- | --- | --- |
| `SQL 注入风险` | 直接给出行号,逐个确认"这些是不是注入点",其余 SQL 不用看 | 要么漏看注入,要么浪费时间通读所有 SQL |
| `异常分支覆盖` | 一眼知道错误处理厚薄,决定是否要重点查边界 | 不知道这模块错误处理是优是劣,全靠碰运气 |
| `代码冗余率` | 告诉你有没有"复制粘贴三份"的代码,决定是否值得重构 | 靠肉眼扫重复,扫不全 |
| `high_risk_density` | 标出所有含并发/事务/权限/缓存的行,提醒"这些别跳过" | 容易忽略数据一致性风险点 |
| `vulnerability_risk` | 机械扫硬编码密钥/XSS/shell 特征,做安全快筛 | 靠人工记忆检查常见漏洞模式 |
**为什么这个设计关键**:审查最贵的成本是**注意力**。一份 diff 或一个功能可能上千行,逐行细看不现实。这 5 项指标把注意力压缩到三件事:
- **哪些行**evidence 给出 file:line
- **怀疑什么**sql/vulnerability 给出关键词模式)
- **薄弱在哪里**exception_coverage 低 = 错误处理该补查)
**注意"触发器而非结论"**:命中 ≠ 缺陷。每个指标都带 `evidence` 供逐一人工核实,`score_review_tool` 的 note 反复强调这一点(如 `sql_risk=1` 可能是 JSX 误报)。
**与 git diff 无关**:指标扫描的是传入 `changed_files` 清单的**文件全文**,不依赖 git diff。unified-review 的清单来自 git diffproject-review 的清单来自用户指令 → `semantic_search`/`query_graph` 聚合(feature)或 `all_files=True`whole-project),二者喂给指标的只是不同的文件来源,指标引擎同一套。project-review 的只读、不碰 git 约束天然成立。
**分工边界**:指标只覆盖"机械化风险",抓不住 IDOR、越权、竞态、LLM 信任边界等业务逻辑缺陷——那些由 Layer 1 人工链路分解 + specialist 子代理覆盖(见 §5.3 与 §5.2.5 注意)。
### 5.3 各指标收集方式与评分标准
#### 5.3.1 `sql_risk` — SQL 注入风险
- **收集方式**:用 6 条正则扫描每行,匹配字符串插值/未参数化 SQL 特征:`"SELECT ..." + 变量``f"...{var}"` 含 SQL、`exec("SELECT...``WHERE x = '" + var``.format(...)` 含 SQL、`% "..." % (...)` 含 SQL 等。
- **评级**:计数命中行数,0 → good1-2 → warn,≥3 → fail。
- **注意**:命中只是警告不是证据。前端 JSX/模板字符串含 SQL 字样易误报(如后端明明全部参数化绑定,报告仍可能报 1 处来自 `.tsx` 的"风险"),需逐一确认上下文,必要时跑 `EXPLAIN` 评估性能。
#### 5.3.2 `exception_coverage` — 异常分支覆盖
- **收集方式**:用 10 条正则统计"异常/错误路径"行数(`try``except``catch(``raise``throw``if err != nil``if ... error``.catch(``else { return None` 等)。
- **计算**`异常行 / 总行 × 100%`。经验基线来自"每 2 条正常路径约有 1 条异常路径"。
- **评级**:≥50% → good30%-50% → warn<30% → fail。
- **注意**:阈值命名易误解——`warn_min=50` 实际是 good 的及格线,`good_min=30` 是 warn 的下限。该指标对 `?` 一元错误传播(Rust)、`Result` 链等"无显式异常关键字"的代码会低估,需人工补查。
#### 5.3.3 `redundancy_rate` — 代码冗余率
- **收集方式**:将每行标准化为签名(去空白、数字→`N`、字符串→`"s"`),统计**在 ≥3 处出现相同签名**的行数。
- **计算**`重复行 / 总行 × 100%`
- **评级**:≤10% → good10%-20% → warn>20% → fail。
- **注意**:签名长度 <24 字符的短行被忽略。重复的函数参数签名(如 `State(state)`/`Extension(claims)`)会高频命中,属结构性信号而非缺陷。
#### 5.3.4 `high_risk_density` — 高风险场景密度
- **收集方式**:用 7 条正则标记"高关注行":并发(async/await/thread/mutex)、事务(transaction/commit/rollback)、原子性(atomic/race)、SQL 关键字、锁、缓存失效(cache/invalidate/evict)。
- **计算**`覆盖行 / 相关行 × 100%`——因所有相关行都被计入"覆盖",实际**恒为 100%**。无高风险行时返回 `na`(不适用),不计入整体评级。
- **评级**:≥90% → good,≥70% → warn,其余 fail。
- **重要**:这是**审查关注信号,不是正确性评分**——只提示"哪些行含并发/事务/安全模式需要人工验证",值高不表示代码好。
#### 5.3.5 `vulnerability_risk` — 漏洞风险
- **收集方式**:用 6 条正则扫描 OWASP/密钥特征:硬编码 `password/api_key/secret/token``eval/exec(``subprocess(shell=True)``innerHTML/dangerouslySetInnerHTML/v-html``<script>`/`onerror=`/`javascript:` 等。
- **评级**:计数命中行数,0 → good1-2 → warn,≥2 → fail。
- **注意**:这只是**文本级启发式**,真实漏洞需依赖扫描器补充(`npm audit``pip-audit``govulncheck`)。业务逻辑型安全缺陷(如 IDOR、越权、竞态)**检不出**,须由 Layer 1 人工审查 + specialist 子代理覆盖。
### 5.4 与 LLM 判断指标的边界
以下 5 项指标**不进入** `score_review_tool` 的客观计算,而是列在返回的 `llm_judged` 字段,须由审查代理在 Layer 1 链路分解中人工/LLM 补判:
`requirement_coverage`(需求覆盖)、`logic_alignment`(逻辑对齐)、`llm_trust_boundary`LLM 信任边界)、`shell_injection`Shell 注入)、`enum_completeness`(枚举完整性)。
### 5.5 实测示例
issues 功能 feature 审查(`issues-feature-review-2026-08-10`)实测结果:
| 指标 | 值 | 评级 | 人工核实结论 |
| --- | --- | --- | --- |
| `sql_risk` | 1 | warn | **误报**:命中的是 `IssuesView.tsx` 的 JSX,后端全部参数化绑定,实际无注入 |
| `exception_coverage` | 0.39% | fail | 异常路径占比极低,错误多统一走 `internal_error` 返回 500 泛化信息,符合人工审查发现的错误处理不足 |
| `redundancy_rate` | 9.74% | good | 临界值;issue_api.rs 存在大量重复的 `State/Extension` 签名与权限校验模板 |
| `high_risk_density` | 100% | good | 仅提示含事务/并发/权限模式的行,需人工验证 |
| `vulnerability_risk` | 0 | good | 文本扫描无命中;真正的 IDOR 漏洞由人工 Layer 1 审查捕获 |
---
## 6. 审查 Skill 体系
### 6.1 核心 skill
| Skill | 对应工作流 | 位置 |
| --- | --- | --- |
| `unified-review` | diff 三层统一审查 | `skills/unified-review/SKILL.md` |
| `project-review` | 项目级审查 | `skills/project-review/SKILL.md` |
### 6.2 references 检查清单
两个 skill 共享一套 `references/`
```text
references/
├── review-checklist.md # 通用审查清单(Layer 1 八分类 + CRITICAL 五类)
├── common-mistakes.md # 常见审查错误
├── report-template.html # 报告模板
├── manual-review/ # 高风险模块人工审查清单
│ ├── payment.md order.md inventory.md permission.md
│ └── distributed-lock.md data-migration.md
└── specialists/ # specialist 子代理检查清单
├── testing.md maintainability.md security.md performance.md
└── data-migration.md api-contract.md red-team.md
```
### 6.3 旧 skill
`review-changes` / `review-delta` / `review-pr` 保留供兼容,提供轻量审查;unified-review / project-review 为增强版。
---
## 7. 审查报告成果物
| 产物 | 文件 | 内容 |
| --- | --- | --- |
| HTML 报告 | `code-review-report.html` | 自包含单文件:结论、PR 质量分、覆盖度、客观指标表、问题清单(severity + 置信度 + 位置 + 修复建议)、人工审查清单、LLM 判断指标 |
| Markdown 报告 | `code-review-report.md` | 同内容 Markdown 版,便于 git 提交 / PR 描述复用 |
参数:`format``html` / `markdown` / `both`,默认 `both`)。**报告均为只读产物**。
> ⚠️ **review_data.counts 键名陷阱**MD 报告的"问题统计"行只读 `critical` / `informational` 两个键(`counts` dict 里的 `blocker`/`major`/`minor` 会被忽略)。传 `{"blocker":20,"major":141,"minor":315}` 会渲染成 "20 严重 · 0 次要"major 数丢失。正确写法:`{"critical":20, "major":141, "informational":315}`。HTML 报告不受影响(读全量键)。
>
> ⚠️ **MCP 大 JSON 兜底**findings 超数百条时 MCP 传参可能解析失败,可直调引擎函数生成报告(见 §4.5),输出与 MCP 工具一致。
>
> ⚠️ **review_data.metrics 格式陷阱**`generate_report_tool` 的 `metrics` 值**必须是 `{grade, value, note}` 字典**(如 `{"sql_risk": {"grade":"good","value":0,"note":"全部参数化"}}`)。若传扁平标量(如 `"sql_risk": 3`),`build_report_data` 会静默丢弃,报告的"客观指标"表格不渲染。生成后自检必须确认指标表存在。
>
> ⚠️ **findings 字段名陷阱**`build_report_data` 只读 `findings` 键(不读 `issues`);每条只读 `path`+`line`(合成 `location`)、`message`(或 `summary`)、`fix`、`severity`、`category`、`confidence`。用错键名(如 `title`/`detail`/`issues`)会导致问题清单缺失或只剩类别+位置。
>
> ️ **HTML `<script>` 安全转义**:引擎在注入 `const data = {...}` 前会把 `<` 全部转义为 `\u003c`v2.5.0 修复,`scoring_tools.py`)。因此 finding 文本里即使含字面 `</script>`、`<!--` 等序列也不会截断报告脚本;JS 解析后还原为 `<`,渲染时再转成 `&lt;`,显示不受影响。**人工无需转义 finding 文案**。
### 7.1 生成后自检(防空报告回归)
1. 打开生成的 `.md`,确认 `## 问题清单(N``N` == findings 数量(不为 0
2. 每条 issue 同时含**问题描述**、**位置**、**修复建议** 三要素(位置形如 `` `server/...:111` ``
3. **`## 客观指标` 表格存在且非空**(缺失多为 metrics 传成扁平标量)
4. 若发现缺描述/缺修复建议/问题数=0 → 修正 `review_data` 后**重新调用** `generate_report_tool` 覆盖
### 7.2 归档命名规范
审查报告归档到 `docs/reviews/`,文件名格式:`{范围}-review-{YYYY-MM-DD-HHMMSS}.{md,html}`(时间戳精确到时分秒,避免同日多次审查互相覆盖)。
| 审查类型 | 范围值示例 | 示例文件名 |
| --- | --- | --- |
| 全项目审查 | `full-project` | `full-project-review-2026-08-12-141343.html` |
| 功能审查 | `{功能}-feature` | `evm-feature-review-2026-08-06-151522.md` |
| 变更级审查 | `pr-{branch}` | `pr-dev-init-xwj-v0.1-review-2026-08-06-151522.md` |
`generate_report_tool``output_path` 直接传 `docs/reviews/{文件名}`(不含扩展名)。**不传 `output_path` 会默认写到仓库根目录 `code-review-report.*`,属违规命名**,需在命名自检中检出并修复。
### 7.3 命名自检(verify-report.ps1
生成完成后对仓库运行命名自检(脚本独立于 code-review-graph CLI,任何版本可用):
```powershell
powershell -File "C:\Users\Administrator\.config\opencode\skills\project-review\verify-report.ps1" -Repo D:\AuraSpace
```
- 退出码 **0** → 通过:所有报告都在 `docs/reviews/` 且文件名带 `-YYYY-MM-DD-HHMMSS` 后缀
- 退出码 **1** → 存在根目录残留 `code-review-report.*`。用 `-Fix` 自动归档,或重新以正确 `output_path` 调用 `generate_report_tool` 覆盖,然后重跑脚本确认退出码 0
- 脚本列出的 historic naming warnings 无需处理(仅提示),但本次生成的报告必须满足规范
### 7.4 行级覆盖自检(verify-line-coverage.ps1,防"无行覆盖还全绿"
v2.5.1 起,whole-project/feature 报告必须含**行覆盖**字段(引擎 fail-closed:缺三件套数据 → 计入 line/unit gap → `target_reached=false`)。生成完成后对仓库运行行覆盖自检:
```powershell
powershell -File "C:\Users\Administrator\.config\opencode\skills\project-review\verify-line-coverage.ps1" -Repo D:\AuraSpace
```
- 退出码 **0** → 通过:报告 `## 覆盖度` 区块含**行覆盖**字段且 ≥95%(`gate="both+line"` 已跑、三件套数据完整)
- 退出码 **1** → 阻塞:报告无行覆盖字段(漏跑 `both+line` 或漏传三件套数据)或行覆盖 <95%。**必须**补数据重新生成报告后重跑,直到退出码 0
### 7.5 防伪抽验自检(verify-spot-check.ps1,防"漏抽验还全绿"
whole-project / feature 审查**必须**执行防伪抽验(三件套③)并注入 `review_data.spot_check`(顶层字段)。生成完成后对仓库运行:
```powershell
powershell -File "C:\Users\Administrator\.config\opencode\skills\project-review\verify-spot-check.ps1" -Repo D:\AuraSpace
```
- 退出码 **0** → 通过:报告 `## 覆盖度` 区块含**防伪抽验**字段且单元数 >0
- 退出码 **1** → 阻塞:报告无防伪抽验字段或渲染"未执行 🔴"。**必须**补抽验(每组 2 文件 × 2-3 单元,每波 ≤40 次)+ 注入 `spot_check` + 重新生成报告后重跑,直到退出码 0
- **诚实声明**:该脚本只能验证抽验**声明完整性**,无法验证主代理是否真读了文件(引擎防伪能力的已知边界)
- **命名自检(Step 8.6)、行覆盖自检(Step 8.7)、防伪抽验自检(Step 8.8)三脚本必须全过**才算审查完成
> 该脚本检测的是报告渲染后的 `## 覆盖度` 区块:引擎渲染模板对缺失行覆盖会显示"行覆盖:未执行 🔴",脚本据此拦截。
---
## 8. 快速上手
### 前提:构建图谱
```bash
code-review-graph build # 或 /code-review-graph-build-graph
```
### 三种审查用法
```text
# 1. 审查本次 git diffunified-review
/code-review-graph-unified-review
# 2. 全项目代码体检(project-review
/code-review-graph-project-review 对项目代码进行全面审查
# 3. 审查单个功能(project-review
/code-review-graph-project-review 审查支付功能的代码
```
### MCP prompt 直接调用
```text
/code-review-graph:unified_review base="HEAD~1"
/code-review-graph:project_review scope="whole-project" target=""
/code-review-graph:project_review scope="feature" target="payment"
```
### 全项目审查的深读约定(v2.5.1)
- 全项目审查必须走**并行深读流水线**(§3.6):`deep_read_plan_tool` 分组 → 4-6 个并行 explore 子代理深读(三件套字段落盘)→ 每波 `coverage_tool(gate="both+line", file_read_ranges=..., file_semantic_units=...)` 三件套门禁 → 未达标补轮 → **防伪抽验每组 2 文件 × 2-3 单元,每波 ≤40 次**(结果落盘 spot_check_*.json 并注入 `review_data.spot_check`
- 覆盖度目标(三件套):全库 ≥85% **且** 高风险 ≥95% **且** 行覆盖 ≥95% **且** 单元完整性无缺口(`gate="both+line"``gate="both"` 保持旧双目标语义)
- 报告生成后执行 **三自检**`verify-report.ps1`(命名)→ `verify-line-coverage.ps1`(行覆盖)→ `verify-spot-check.ps1`(防伪抽验)→ `save_coverage_index_tool`v2 索引含 rangesSHA 来自 nodes.file_hash)写跨轮索引
### 修复流程(人工裁决)
1. 审查产出 findingsseverity + 置信度 + file:line + 修复建议)
2. 按 severity 批量呈现,逐个决定:**修 / 不修 / 自己改**
3. 🔴 blocker 不可批量跳过
4. 确认后人工执行修复,重新审查验证