Files

169 lines
12 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.
# 并行深读流水线(Step 5.5 操作手册)
> 目的:让 whole-project 审查达到全库覆盖 ≥85% 且高风险 ≥95%(双目标 gate="both",文件数口径)**且**每个深读文件通过三件套质量门禁(单元完整性 / 行覆盖 / 防伪抽验)。
> 核心:主上下文不逐文件深读,改用并行 explore 子代理分组深读,每个子代理返回
> 「深读确认清单 + semantic_units + read_ranges + findings(带 file:line 证据)」,结果**落盘**给聚合脚本校验。
## 〇、三件套质量门禁(V2.1,每个深读文件必须通过)
| # | 门禁 | 定义 | 阈值 | 角色 | 防伪性 |
|---|---|---|---|---|---|
| ① | **单元完整性** | `semantic_units` 与图谱单元节点差集 = 空 | **100%(硬)** | 防漏读(跳过后半段) | 引擎/脚本自动核验 |
| ② | **行覆盖** | `union(read_ranges)` / 真实行数 | **≥95%** | 防"每单元只读一行" | 低(自报粒度) |
| ③ | **防伪抽验** | 主代理回读抽样单元 | **每组 2 文件 × 2-3 单元/文件,每波 ≤40 次** | 防假读(抄清单不读) | 唯一防假读手段 |
判定规则:
- ① 单元被覆盖 = 图谱单元 `[ls,le]` 与上报 `semantic_units` 中某 range **精确匹配**(优先)或**重叠 ≥80%**(兜底),且该单元 ≥80% 行落在 `union(read_ranges)` 内;一个上报 range 只能匹配一个单元(one-to-one),宽 range 无法覆盖多个单元。
- 巨型文件豁免 ①:`单元数<3``最大单元行占比>80%`(如 migrations.rs run_migrations 6200/6331=98%)→ 该文件仅按 ② 行覆盖校验,引擎/脚本返回 `unit_exempt_files`
- ② 分母 = **真实文件行数**(读取源文件,非图节点 line_end——已验证 ±1 偏差),行数缓存。
- ③ 预算:**每波 ≤40 次 read**(每波 4-6 组 × 2 文件 × 3 单元 ≈ 24-36,留余量);每组抽 2 文件、每文件抽 2-3 单元回读比对 note;抽到假读 → 该组重读并升级抽验率。结果**必须落盘 `spot_check_<batch>.json`** 并在 Step 8 聚合注入 `review_data.spot_check`(顶层字段),否则报告渲染"防伪抽验:未执行 🔴"且 verify-spot-check.ps1 exit 1。
> **诚实声明**:引擎无法防假读。① 防漏单元、② 防读不全、③(抽样)才是唯一防假读的手段,且为预算化而非全量。verify-spot-check.ps1 只能验证抽验**声明完整性**(抽了、单元数>0、range 落在 read_ranges 内),无法验证主代理是否真读了文件。
## 一、分组规则
以 AuraSpace 实测分组表为基准(约 508 源文件 → 14-16 组,每组 ≤40 文件):
| # | 组(目录) | 约文件数 | 子代理职责 |
|---|---|---|---|
| 1 | `server/src/api`1/2 | 30 | 后端 API 层前半 |
| 2 | `server/src/api`2/2 | 31 | 后端 API 层后半 |
| 3 | `server/src/services`(1/2) | 33 | 核心业务服务前半 |
| 4 | `server/src/services`(2/2) | 33 | 核心业务服务后半 |
| 5 | `server/src/domain` | 43 | 领域模型/DTO |
| 6 | `server/src/deepwiki` + `infrastructure` | 25 | DeepWiki + 基础设施 |
| 7 | `server/tests` + `bin` + `models` | 14 | 测试与工具二进制 |
| 8 | `web/src/views`(1/2 | 34 | 前端视图前半 |
| 9 | `web/src/views`(2/2 | 33 | 前端视图后半 |
| 10 | `web/src/components`1/3 | 38 | 前端组件(issues/evm/ci |
| 11 | `web/src/components`2/3 | 38 | 前端组件(docs/wiki/time-log/settings |
| 12 | `web/src/components`3/3 | 37 | 前端组件(ppt/agent/opencode/common/ui |
| 13 | `web/src/store` + `utils` + `api` | 31 | 状态管理/工具函数/API 客户端 |
| 14 | `web/src/hooks` + `services` + `generated` + `web/tests` | 大 | 前端其余(可分 2 组) |
> 实际以 `deep_read_plan_tool(gate="overall", target_coverage=85, include_prior=True)`
> 返回的 `groups` 为准(引擎按风险权重贪心 + 目录分组,数量随仓库变化)。
> 每次并行派发 **4-6 个子代理**,其余排队分批,避免 MCP 并发压力与上下文风暴。
## 二、子代理 Prompt 模板(V2.1,含三件套协议)
对每个组派发如下 prompt(替换 `<GROUP>` / `<FILE_LIST>` / `<输出目录>` / `<临时输出文件>`):
```
你是一个代码审查深读子代理。请深读以下 <GROUP> 组的全部文件:
<FILE_LIST(每行一个相对路径)>
要求:
1. 逐文件用 read 读取完整内容(文件大则分段读),**不要跳过任何文件**。
2. 对每个文件,从八个类别审视:interface / business / data / utility /
error handling / security / performance / observability,并叠加
CRITICAL 子轮:SQL 与数据安全、竞态条件、LLM 输出信任边界、Shell 注入、枚举完备性。
3. 只报告**真实问题**(严重度 blocker/major/minor,置信度 1-10)。
每条 finding 必须含 file:line 证据(实际读取到的行),没有证据视为未深读。
4. 良好实践、无问题的文件,在 deep_read 确认清单中标注,不产出 finding。
5. **每个文件必须填写质量字段**(这是硬性要求,缺失视为未深读):
- total_lines: 文件真实总行数(从 read 输出得知)
- read_ranges: 实际读取的行区间数组 [[s,e],...](分段读即天然区间),
闭区间,并集应覆盖你读过的每一行
- semantic_units: 你实际读过的每个语义单元(函数/类/接口)摘要,
{"range":[s,e],"kind":"Function|Class|Test","name":"<符号名>","note":"<≤60字摘要>"}
**严格模式(强制)**:必须**逐一列出该文件图谱中的全部语义单元**,
包括:
- 所有 Function(含私有/辅助小函数)
- 所有 Class / interface / Type(含 Props 接口、仅数行的小接口——如
`MermaidBlockProps`3 行)、`NavEntry`5 行)也必须单独列出)
- 不得把 Props interface / 小接口并入父组件或跳过
- 一个语义单元 = 一个 semantic_units 条目,range 取该符号的实际
[line_start, line_end]
跳过任一图谱单元 = 漏读,会被单元完整性差集校验检出(unit_gap)→ 该文件须补报重读
输出(结构化,仅此格式),写入 <临时输出文件>(JSON 文件,不要回传大文本):
{
"group": "<GROUP>",
"outputs": [
{
"path": "相对路径",
"total_lines": <int>,
"read_ranges": [[s,e], ...],
"semantic_units": [{"range":[s,e],"kind":"...","name":"...","note":"..."}],
"findings": [
{"path": "...", "line": <int>, "severity": "blocker|major|minor",
"category": "security|business|data|...", "confidence": <1-10>,
"message": "问题描述(含具体行内容证据)", "fix": "修复建议"}
]
}
],
"summary": "本组一句话结论(主要风险点)"
}
```
深读确认 = 该组文件全部出现在 outputs 中,且每个都有 `total_lines`/`read_ranges`/`semantic_units`
> **禁止退化格式**:不得把子代理输出简化为 `DEEPREAD_CONFIRM: <路径|总行数|已读范围|单元数>` 单行文本。
> 那会使主代理拿不到分段 `read_ranges` 与逐单元 `semantic_units`,导致 coverage_tool 三件套门禁
> 无法执行、报告"无行覆盖"。子代理 prompt 必须使用本节模板原样复制。
## 三、输出 Schema(子代理 → 落盘 → 主代理)
- **落盘机制(强制)**:子代理把上述 JSON 写入主代理指定的临时目录(如
`C:\Users\ADMINI~1\AppData\Local\Temp\opencode\review\batchN.json`)。
**不要把 1-2MB JSON 回传主上下文**——主代理只读聚合结果摘要。
- 每波子代理完成后,主代理运行聚合脚本:
```
python skills/project-review/scripts/aggregate_deep_read.py <repo_root> <输出目录>
```
返回 `verified_files / line_gap_files / unit_gap_files / unit_exempt_files / summary`。
## 四、主代理收尾流程
```
1. 每波子代理完成后(不等全部结束):
aggregate_deep_read.py <repo_root> <落盘目录>
2. 汇总三件套判定:
- line_gap_files unit_gap_files → 补读队列(下波派发,禁止跳过)
- unit_exempt_files → 仅按行覆盖校验(已由脚本处理)
- verified_files → 计入本轮 deep_read_files
3. 防伪抽验(**强制,每波必做**):对每组抽 **2 文件**、每文件抽 2-3 个语义单元,
回读源文件对应行比对 semantic_units.note。每波 ≤40 次 read。抽到假读 → 该组重读并升级抽验率。
结果落盘 `spot_check_<batch>.json`schemagroups_sampled / files_sampled / units_sampled /
fake_read_found / groups_rereread / samples[{group,file,unit,range,note_match,in_read_ranges}])。
4. 全部达标后,三件套数据传入引擎 coverage_tool(B 阶段引擎原生支持):
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
⚠️ 禁止降级:unit_gap_files 或 line_gap_files 非空时,**不得**改回 gate="both" 静默跳过;
必须补轮重读至空,或在报告中显式标注"三件套未达标 🔴"并列出缺口文件。
5. 未达标(文件数 <85%/<95% 或行覆盖 <95% 或单元有缺口)→
按 priority_deep_read_files / line_gap_files / unit_gap_files 补一轮(可再派 1-3 个子代理)。
6. G2:对 silent_files 随机抽 15% 深读(本轮未覆盖的静默文件)。
7. 报告生成前:聚合全部 `spot_check_*.json` → 注入 `review_data.spot_check`(顶层字段)。
8. G3:三件套 + 文件数双口径达标 → 报告生成(覆盖度区块须含行/单元覆盖 + 防伪抽验)→
跑 verify-spot-check.ps1Step 8.8)→ save_coverage_index_tool(deep_read_files=<verified_files>, file_read_ranges=<ranges>) 写 v2 索引。
```
## 五、质量控制与防伪
| 风险 | 对策 |
|---|---|
| 子代理"声称读了"但没真读 | 强制 `total_lines`/`read_ranges`/`semantic_units` 三字段 + findings 带行号;聚合脚本按①差集+②行并集双校验;主代理③抽样回读 |
| 子代理漏读文件 | outputs 与分组清单 diff,漏读计入覆盖率缺口,触发补轮 |
| 子代理宽 range 冒充全读 | ① one-to-one 匹配:一个上报 range 只能覆盖一个单元,无法用整文件 range 覆盖所有单元 |
| 子代理漏报小单元(Props interface / Type / 小函数) | 严格模式:semantic_units 必须逐一列出图谱全部单元(含 3-5 行的小接口);unit_gap 非空 → 该文件补报重读,不得视为已深读 |
| 子代理各自为政口径不一 | 统一八类 + CRITICAL 子轮 + severity/confidence 标准(见上模板) |
| 增量掩盖新代码 | `include_prior=True` 按 per-file SHA 判定;变更文件自动失效重读 |
| 并发压力 | 每批 4-6 个并行,其余排队;batch_size 40 控制单组体量 |
| 主上下文被大 JSON 撑爆 | 落盘机制:子代理写临时文件,主代理只读聚合摘要 |
## 六、跨轮增量(多轮累积)
- 每轮报告后 `save_coverage_index_tool` 写 `.code-review-graph/coverage-index.json`
(相对路径 → per-file SHA)。
- 下一轮 `deep_read_plan_tool(include_prior=True)` / `coverage_tool(include_prior=True)`
自动复用 SHA 未变文件 → 增量任务 = 新增文件 + 变更文件。
- 多轮后增量归零即实现全库全覆盖,避免每轮从 2-5% 起步。
- 引擎 B 阶段(`compute_coverage` 支持 `file_read_ranges`/`file_semantic_units``gate="both+line"`**已落地**。主代理优先用
`coverage_tool(deep_read_files=..., gate="both+line", file_read_ranges=..., file_semantic_units=...)`
做三件套门禁(引擎返回 `line_coverage_pct`/`unit_coverage_pct`/`line_gap_files`/`unit_gap_files`/`unit_exempt_files`)。
A 阶段聚合脚本 `scripts/aggregate_deep_read.py` 保留作独立校验兜底(引擎不可用时的替代),两者判定逻辑一致。