56 KiB
代码审查功能文档
代码审查能力由两个独立工作流组成:
unified-review(diff 审查)与project-review(项目级审查)。本文聚焦审查工作流的操作细节(流程、覆盖度机制、客观指标、报告产物、实操踩坑)。📖 文档分工:
- 本文(
CODE_REVIEW_GUIDE_ZH.md)— 审查流程 / 指标解读 / 覆盖度机制 / 报告产物 / 踩坑记录- 工具总览(
CODE_REVIEW_GRAPH_ZH.md)— 工具定位、MCP 工具清单、CLI 命令、配置与部署、opencode 集成现状
目录
- 两种审查工作流
- unified-review:diff 代码审查
- project-review:项目级代码审查
- 并行深读流水线
- 审查调用的 MCP 工具
- 5 项客观指标详解
- 审查 Skill 体系
- 审查报告成果物
- 快速上手
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-review:diff 代码审查
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 + Markdown;Step 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 深读名单(并集信号,无上限)
进名单 = 任一命中:
① 拓扑热点: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)。
双重文件数口径:
全库覆盖 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:
attribution_pct=nodes.community_id非空 / 非 File 节点数 的百分比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 流程
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.ps1(Step 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):
行覆盖率(每文件) = |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 上报的文件:行覆盖视为通过(不设硬卡),仅依赖单元完整性 + 抽验
执行步骤(主代理在全新会话中):
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 + ranges,SHA 来自 nodes.file_hash,<1s)
新窗口首轮验证(Step 0 后建议先跑一次冒烟):
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 参考脚本(主代理每轮可复用):
# 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 参考脚本:
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 工具调用链
unified-review(diff):
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 工具完全一致:
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 diff,project-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 → good,1-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% → good,30%-50% → warn,<30% → fail。
- 注意:阈值命名易误解——
warn_min=50实际是 good 的及格线,good_min=30是 warn 的下限。该指标对?一元错误传播(Rust)、Result链等"无显式异常关键字"的代码会低估,需人工补查。
5.3.3 redundancy_rate — 代码冗余率
- 收集方式:将每行标准化为签名(去空白、数字→
N、字符串→"s"),统计在 ≥3 处出现相同签名的行数。 - 计算:
重复行 / 总行 × 100%。 - 评级:≤10% → good,10%-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 → good,1-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/:
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两个键(countsdict 里的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 解析后还原为<,渲染时再转成<,显示不受影响。人工无需转义 finding 文案。
7.1 生成后自检(防空报告回归)
- 打开生成的
.md,确认## 问题清单(N)中N== findings 数量(不为 0) - 每条 issue 同时含问题描述、位置、修复建议 三要素(位置形如
`server/...:111`) ## 客观指标表格存在且非空(缺失多为 metrics 传成扁平标量)- 若发现缺描述/缺修复建议/问题数=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 -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 -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 -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. 快速上手
前提:构建图谱
code-review-graph build # 或 /code-review-graph-build-graph
三种审查用法
# 1. 审查本次 git diff(unified-review)
/code-review-graph-unified-review
# 2. 全项目代码体检(project-review)
/code-review-graph-project-review 对项目代码进行全面审查
# 3. 审查单个功能(project-review)
/code-review-graph-project-review 审查支付功能的代码
MCP prompt 直接调用
/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 索引含 ranges,SHA 来自 nodes.file_hash)写跨轮索引
修复流程(人工裁决)
- 审查产出 findings(severity + 置信度 + file:line + 修复建议)
- 按 severity 批量呈现,逐个决定:修 / 不修 / 自己改
- 🔴 blocker 不可批量跳过
- 确认后人工执行修复,重新审查验证