chore: sync local changes, add Chinese docs and opencode config

This commit is contained in:
AuraK Developer
2026-08-31 11:37:03 +08:00
parent 307d2fd471
commit ecc55158c1
81 changed files with 7645 additions and 144 deletions
+729
View File
@@ -0,0 +1,729 @@
# 代码审查功能文档
> 代码审查能力由**两个独立工作流**组成:`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. 确认后人工执行修复,重新审查验证