# 并行深读流水线(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_.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(替换 `` / `` / `<输出目录>` / `<临时输出文件>`): ``` 你是一个代码审查深读子代理。请深读以下 组的全部文件: 要求: 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": "", "outputs": [ { "path": "相对路径", "total_lines": , "read_ranges": [[s,e], ...], "semantic_units": [{"range":[s,e],"kind":"...","name":"...","note":"..."}], "findings": [ {"path": "...", "line": , "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 <输出目录> ``` 返回 `verified_files / line_gap_files / unit_gap_files / unit_exempt_files / summary`。 ## 四、主代理收尾流程 ``` 1. 每波子代理完成后(不等全部结束): aggregate_deep_read.py <落盘目录> 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_.json`(schema:groups_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=, 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.ps1(Step 8.8)→ save_coverage_index_tool(deep_read_files=, file_read_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` 保留作独立校验兜底(引擎不可用时的替代),两者判定逻辑一致。