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
@@ -0,0 +1,39 @@
---
description: Build or rebuild the code review knowledge graph for this project.
agent: build
---
# Build Graph
Build or incrementally update the persistent code knowledge graph for this repository.
$ARGUMENTS
## Steps
1. **Check graph status** by calling the `list_graph_stats_tool` MCP tool.
- If the graph has never been built (last_updated is null), proceed with a full build.
- If the graph exists, proceed with an incremental update.
2. **Build the graph** by calling the `build_or_update_graph_tool` MCP tool:
- For first-time setup: `build_or_update_graph_tool(full_rebuild=True)`
- For updates: `build_or_update_graph_tool()` (incremental by default)
3. **Verify** by calling `list_graph_stats_tool` again and report the results:
- Number of files parsed
- Number of nodes and edges created
- Languages detected
- Any errors encountered
## When to Use
- First time setting up the graph for a repository
- After major refactoring or branch switches
- If the graph seems stale or out of sync
- The graph auto-updates via hooks on edit/commit, so manual builds are rarely needed
## Notes
- The graph is stored as a SQLite database (`.code-review-graph/graph.db`) in the repo root
- Binary files, generated files, and patterns in `.code-review-graphignore` are skipped
- If the graph tools are unavailable, fall back to `code-review-graph build` / `code-review-graph status` via the shell
@@ -0,0 +1,42 @@
---
description: Whole-project or single-feature code review (not diff-based) using graph-wide analysis and objective scoring.
agent: build
---
# Project Review
Review the entire codebase or a single feature/module, independent of the git diff. The scope is driven by your instruction.
$ARGUMENTS
**Token optimization:** Before starting, call `get_docs_section_tool(section_name="project-review")` for the optimized workflow.
## Steps
1. **Parse the scope** from the user instruction:
- "对项目代码进行全面审查" / "全面审查" / "整个项目" → `scope=whole-project`
- "审查 <功能/模块> 的代码" (e.g. payment, auth) → `scope=feature`, target=<keyword>
2. **Ensure the graph is current** by calling `build_or_update_graph_tool()`.
3. **Map the architecture** by calling `get_architecture_overview_tool(detail_level="minimal")` and `list_communities_tool(detail_level="minimal")`.
4. **Scan high-risk areas** (whole-project): `get_knowledge_gaps_tool()`, `get_hub_nodes_tool()`, `get_bridge_nodes_tool()`, `find_large_functions_tool()`, `get_surprising_connections_tool()`.
5. **Score objectively**:
- whole-project: `score_review_tool(all_files=True)` — every source file in the graph
- feature: `semantic_search_nodes_tool(query=<target>)` + `query_graph_tool(pattern="children_of", target=<target>)` to locate files, then `score_review_tool(changed_files=<files>)` + `get_impact_radius_tool(changed_files=<files>)`
6. **Review the code** (Layer 1 chain decomposition): eight categories + gstack CRITICAL sub-pass. Produce findings with severity (blocker/major/minor), confidence (1-10), file:line, and proposed fix.
7. **Merge findings** by calling `dedupe_findings_tool(findings=<your findings>)`.
8. **Generate the report** by calling `generate_report_tool(review_data=<verdict, scope, metrics, merged findings>)` — writes `code-review-report.html` and `code-review-report.md` (default `format="both"`).
9. **Report** the verdict (✅ PASS / ❌ FAIL), severity counts, each issue with confidence + fix, and manual-review items.
## Important Rules
- **READ-ONLY.** This workflow never modifies code, commits, or pushes. Every finding waits for a manual fix decision.
- **Any blocker → verdict ❌ FAIL**, regardless of other scores.
- This is **not** a diff review. For diff-based review use `/code-review-graph-unified-review`.
@@ -0,0 +1,47 @@
---
description: Review only changes since last commit using impact analysis and blast-radius detection.
agent: build
---
# Review Delta
Perform a focused, token-efficient code review of only the changed code and its blast radius.
$ARGUMENTS
**Token optimization:** Before starting, call `get_docs_section_tool(section_name="review-delta")` for the optimized workflow. Use ONLY changed nodes + 2-hop neighbors in context.
## Steps
1. **Ensure the graph is current** by calling `build_or_update_graph_tool()` (incremental update).
2. **Get review context** by calling `get_review_context_tool()`. This returns:
- Changed files (auto-detected from git diff)
- Impacted nodes and files (blast radius)
- Source code snippets for changed areas
- Review guidance (test coverage gaps, wide impact warnings, inheritance concerns)
3. **Analyze the blast radius** by reviewing the `impacted_nodes` and `impacted_files` in the context. Focus on:
- Functions whose callers changed (may need signature/behavior verification)
- Classes with inheritance changes (Liskov substitution concerns)
- Files with many dependents (high-risk changes)
4. **Perform the review** using the context. For each changed file:
- Review the source snippet for correctness, style, and potential bugs
- Check if impacted callers/dependents need updates
- Verify test coverage using `query_graph_tool(pattern="tests_for", target=<function_name>)`
- Flag any untested changed functions
5. **Report findings** in a structured format:
- **Summary**: One-line overview of the changes
- **Risk level**: Low / Medium / High (based on blast radius)
- **Issues found**: Bugs, style issues, missing tests
- **Blast radius**: List of impacted files/functions
- **Recommendations**: Actionable suggestions
## Advantages Over Full-Repo Review
- Only sends changed + impacted code to the model (5-10x fewer tokens)
- Automatically identifies blast radius without manual file searching
- Provides structural context (who calls what, inheritance chains)
- Flags untested functions automatically
@@ -0,0 +1,67 @@
---
description: Review a PR or branch diff using the knowledge graph for full structural context with blast-radius analysis.
agent: build
---
# Review PR
Perform a comprehensive code review of a pull request or branch diff using the knowledge graph.
$ARGUMENTS
**Token optimization:** Before starting, call `get_docs_section_tool(section_name="review-pr")` for the optimized workflow. Never include full files unless explicitly asked.
## Steps
1. **Identify the changes** for the PR:
- If a PR number or branch is provided in $ARGUMENTS, use `git diff main...<branch>` to get changed files
- Otherwise auto-detect from the current branch vs main/master
2. **Update the graph** by calling `build_or_update_graph_tool(base="main")` to ensure the graph reflects the current state.
3. **Get the full review context** by calling `get_review_context_tool(base="main")`:
- This uses `main` (or the specified base branch) as the diff base
- Returns all changed files across all commits in the PR
4. **Analyze impact** by calling `get_impact_radius_tool(base="main")`:
- Review the blast radius across the entire PR
- Identify high-risk areas (widely depended-upon code)
5. **Deep-dive each changed file**:
- Read the full source of files with significant changes
- Use `query_graph_tool(pattern="callers_of", target=<func>)` for high-risk functions
- Use `query_graph_tool(pattern="tests_for", target=<func>)` to verify test coverage
- Check for breaking changes in public APIs
6. **Generate structured review output**:
```
## PR Review: <title>
### Summary
<1-3 sentence overview>
### Risk Assessment
- **Overall risk**: Low / Medium / High
- **Blast radius**: X files, Y functions impacted
- **Test coverage**: N changed functions covered / M total
### File-by-File Review
#### <file_path>
- Changes: <description>
- Impact: <who depends on this>
- Issues: <bugs, style, concerns>
### Missing Tests
- <function_name> in <file> - no test coverage found
### Recommendations
1. <actionable suggestion>
2. <actionable suggestion>
```
## Tips
- For large PRs, focus on the highest-impact files first (most dependents)
- Use `semantic_search_nodes_tool` to find related code the PR might have missed
- Check if renamed/moved functions have updated all callers
@@ -0,0 +1,43 @@
---
description: Run the three-layer unified review (CRG graph context + score_review + dedupe_findings + generate_report).
agent: build
---
# Unified Review
Run the three-layer unified code review using the MCP prompt workflow.
$ARGUMENTS
**Token optimization:** Before starting, call `get_docs_section_tool(section_name="unified-review")` for the optimized workflow.
## Steps
1. **Load the workflow** by calling the `unified_review` MCP prompt (or the `unified-review` skill). This drives the full READ-ONLY review pipeline.
2. **Ensure the graph is current** by calling `build_or_update_graph_tool()`.
3. **Get the review context** by calling `get_review_context_tool()` — changed files, blast radius, source snippets.
4. **Detect changes** by calling `detect_changes_tool()` — risk score, changed functions, test gaps, affected flows.
5. **Score objectively** by calling `score_review_tool()` — SQL risk, exception coverage, redundancy, high-risk density, vulnerability heuristic (good/warn/fail grades). LLM-judged metrics are in `llm_judged`.
6. **Review the changed code** (Layer 1 chain decomposition): interface, business, data, utility, error handling, security, performance, observability. Produce findings with severity (blocker/major/minor), confidence (1-10), file:line, and proposed fix.
7. **Merge findings** by calling `dedupe_findings_tool(findings=<your findings>)` — fingerprint dedup, multi-source confidence boost, PR quality score.
8. **Generate the report** by calling `generate_report_tool(review_data=<verdict, tier, scope, metrics, merged findings>)` — writes `code-review-report.html` and `code-review-report.md` (default `format="both"`).
9. **Report** the verdict (✅ PASS / ❌ FAIL), severity counts, each issue with confidence + fix, and manual-review items.
## Important Rules
- **READ-ONLY.** This workflow never modifies code, commits, or pushes. Every finding waits for a manual fix decision.
- **Any blocker → verdict ❌ FAIL**, regardless of other scores.
- Tier (fast / standard / strict) comes from `.code-review.yaml` at the repo root, or the `tier` argument.
## Tips
- For large diffs (50+ lines), dispatch specialist subagents (testing, maintainability, security, performance, data-migration, api-contract) in parallel before dedupe.
- Security and data-migration are insurance specialists — always run even when silent.
+40
View File
@@ -0,0 +1,40 @@
---
description: 用自然语言为 code-checker 新增一条规则(语义/声明式/Python 兜底)并注册生效
agent: build
---
# rulegen
为 code-checker 新增一条代码规则。先加载 rulegen skill 并按其流程执行(若可用)。
用户要新增的规则描述:
$ARGUMENTS
## 前置
- 确认当前工作目录是 code-checker 项目(含 `tools/rulegen.py``docs/rulegen/prompt-template.md`)。若不是,先切换到该项目根目录或使用其绝对路径执行。
- Windows 下若无 `python` 命令,使用 `py`
## 执行步骤
1. 读取提示词模板 `docs/rulegen/prompt-template.md`,按其 schema 与全部约定执行。
2. 判断规则类型(三选一):
- **semantic(语义)**:需理解代码意图(如 catch 块日志、XSS 转义)→ 只生成 `rule.yaml`,必含 `check_prompt` + `message_zh`
- **declarative(声明式机械)**:行级正则可表达 → `rule.yaml``pattern`(**必须用 YAML 单引号**,如 `pattern: 'Thread\\.sleep\\s*\\('`
- **python(机械 Python 兜底)**:需跨行/AST 逻辑,且是 Java(JSP 不支持)→ `rule.yaml` + `checker.py`(继承 `BaseChecker``CATEGORIES` 用新类别)
3.`rules/_staging/<rule-slug>/` 下生成 `rule.yaml`(必要时含 `checker.py``sample.<java|jsp>``negative.<java|jsp>`)。
4. 依次执行(在项目根目录;退出码 0=OK / 1=失败,失败先修正再重试,**不要**强制跳过):
```
python tools/rulegen.py validate rules/_staging/<rule-slug>
python tools/rulegen.py smoke rules/_staging/<rule-slug> # 语义规则跳过
python tools/rulegen.py register rules/_staging/<rule-slug> --dry-run
python tools/rulegen.py register rules/_staging/<rule-slug>
```
5. 注册完成后向用户报告:规则 ID 与类型、落盘文件(`rules/custom/<lang>-rules.yaml`python 类另含 `check-engine/custom_checkers/`)、校验/冒烟摘要。
## 纪律
- **不修改** `rules/java-rules.yaml` / `rules/jsp-rules.yaml` / `rules/messages.yaml` 等交付文件;只写 `rules/custom/` 与 `check-engine/custom_checkers/`。
- ID 编号写 `-001` 即可,`register` 会自动重编号。
- 生成后不做 git 提交。
+31
View File
@@ -0,0 +1,31 @@
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"superpowers@git+https://github.com/obra/superpowers.git",
"D:/software/Clawd on Desk/resources/app.asar.unpacked/hooks/opencode-plugin"
],
"mcp": {
"playwright": {
"type": "local",
"command": [
"npx",
"-y",
"@playwright/mcp"
],
"enabled": true
},
"code-review-graph": {
"type": "local",
"command": [
"D:\\code-review-graph\\code-review-graph-main\\.venv\\Scripts\\code-review-graph.exe",
"serve",
"--repo",
"D:\\AuraSpace"
],
"timeout": 600000,
"env": {
"CRG_REPO_ROOT": "D:\\AuraSpace"
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
---
name: build-graph
description: Build or update the code review knowledge graph. Run this first to initialize, or let hooks keep it updated automatically.
argument-hint: "[full]"
---
# Build Graph
Build or incrementally update the persistent code knowledge graph for this repository.
## Steps
1. **Check graph status** by calling the `list_graph_stats_tool` MCP tool.
- If the graph has never been built (last_updated is null), proceed with a full build.
- If the graph exists, proceed with an incremental update.
2. **Build the graph** by calling the `build_or_update_graph_tool` MCP tool:
- For first-time setup: `build_or_update_graph_tool(full_rebuild=True)`
- For updates: `build_or_update_graph_tool()` (incremental by default)
3. **Verify** by calling `list_graph_stats_tool` again and report the results:
- Number of files parsed
- Number of nodes and edges created
- Languages detected
- Any errors encountered
## When to Use
- First time setting up the graph for a repository
- After major refactoring or branch switches
- If the graph seems stale or out of sync
- The graph auto-updates via hooks on edit/commit, so manual builds are rarely needed
## Notes
- The graph is stored as a SQLite database (`.code-review-graph/graph.db`) in the repo root
- Binary files, generated files, and patterns in `.code-review-graphignore` are skipped
- Supported languages: Python, TypeScript/JavaScript, Vue, Go, Rust, Java, Scala, C#, Ruby, Kotlin, Swift, PHP, Solidity, C/C++
+27
View File
@@ -0,0 +1,27 @@
---
name: debug-issue
description: Systematically debug issues using graph-powered code navigation
---
## Debug Issue
Use the knowledge graph to systematically trace and debug issues.
### Steps
1. Use `semantic_search_nodes_tool` to find code related to the issue.
2. Use `query_graph_tool` with `callers_of` and `callees_of` to trace call chains.
3. Use `get_flow` to see full execution paths through suspected areas.
4. Run `detect_changes_tool` to check if recent changes caused the issue.
5. Use `get_impact_radius_tool` on suspected files to see what else is affected.
### Tips
- Check both callers and callees to understand the full context.
- Look at affected flows to find the entry point that triggers the bug.
- Recent changes are the most common source of new issues.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.
@@ -0,0 +1,28 @@
---
name: explore-codebase
description: Navigate and understand codebase structure using the knowledge graph
---
## Explore Codebase
Use the code-review-graph MCP tools to explore and understand the codebase.
### Steps
1. Run `list_graph_stats` to see overall codebase metrics.
2. Run `get_architecture_overview_tool` for high-level community structure.
3. Use `list_communities_tool` to find major modules, then `get_community` for details.
4. Use `semantic_search_nodes_tool` to find specific functions or classes.
5. Use `query_graph_tool` with patterns like `callers_of`, `callees_of`, `imports_of` to trace relationships.
6. Use `list_flows` and `get_flow` to understand execution paths.
### Tips
- Start broad (stats, architecture) then narrow down to specific areas.
- Use `children_of` on a file to see all its functions and classes.
- Use `find_large_functions` to identify complex code.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.
@@ -0,0 +1,273 @@
---
name: project-review
description: Whole-project or single-feature code review (not diff-based) using graph-wide analysis and objective scoring
---
# Project Review
Review the entire codebase or a single feature/module, independent of the git diff. Two scopes, driven by the user's instruction:
- **whole-project**: "对项目代码进行全面审查", "全面审查", "整个项目" → review every source file in the graph.
- **feature**: "审查 <功能/模块> 的代码" (e.g. payment, auth) → review only the code related to the target.
**This skill is READ-ONLY.** Every finding is presented to the user for a manual fix decision. Never apply code changes, commit, or push.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="project review")`. Use `detail_level="minimal"` on all calls; escalate to `"standard"` only when a metric or finding needs evidence.
- **Whole-project deep-read is a sub-agent pipeline, not a main-context loop.** 主上下文不逐文件深读 500+ 文件;改为 Step 5.5 并行子代理分组深读。主上下文预算 ≤14 calls + N 子代理(见 Step 5.5)。
## Step 0 - Parse the scope
Read the user's instruction and set scope: whole-project (contains 全面/整个项目/所有/all) or feature + target (extract the feature/module keyword). Declare both in the report header.
> **快速参考**:审查 agent 的精简流程指引来自 `get_docs_section(section="project-review")`(引擎 `docs/LLM-OPTIMIZED-REFERENCE.md`),该文档已同步 review_data schema`reviewed_files` 数组、coverage 全字段透传含三件套字段、metrics 带 note、仅五客观指标)。完整 schema 以本 SKILL.md 与 `references/report-schema.md` 为准。
## Step 1 - Graph ready
1. Call `build_or_update_graph_tool()` to ensure the graph is current.
2. Call `get_minimal_context_tool(task="project review")` for stats and community overview.
## Step 2 - Architecture map
Call `get_architecture_overview_tool(detail_level="minimal")` and `list_communities_tool(detail_level="minimal")` to map the module structure.
## Step 3 - High-risk scan (whole-project)
Call `get_knowledge_gaps_tool()`, `get_hub_nodes_tool()`, `get_bridge_nodes_tool()`, `find_large_functions_tool()` and `get_surprising_connections_tool()` to locate hotspots, chokepoints, untested areas and odd coupling.
## Step 4 - Objective scoring
- whole-project: `score_review_tool(all_files=True)` scores every source file in the graph.
- feature: locate the target files with `semantic_search_nodes_tool(query=<target>)` and `query_graph_tool(pattern="children_of", target=<target>)`, then `score_review_tool(changed_files=<files>)` and `get_impact_radius_tool(changed_files=<files>)` for the blast radius.
- **指标集约束**:报告的 `metrics` 只透传 `score_review_tool` 返回的五个客观指标(`sql_risk` / `exception_coverage` / `redundancy_rate` / `high_risk_density` / `vulnerability_risk`)。**禁止**手工追加 `requirement_coverage` / `logic_alignment` / `llm_trust_boundary` / `shell_injection` / `enum_completeness` 等 LLM 判定指标;`review_data.llm_judged` 一律传空 `[]`
## Step 5 - Chain decomposition
Inspect the scored code across eight categories (interface, business, data, utility, error handling, security, performance, observability) and apply the gstack CRITICAL sub-pass (SQL & Data Safety, Race Conditions, LLM Output Trust Boundary, Shell Injection, Enum Completeness). Mark each ✅ / ⚠️ / —.
## Step 5.5 - 并行深读流水线(whole-project 强制,覆盖率达标的唯一可行路径)
> 引擎 v2.5.0 新增 `deep_read_plan_tool` / `save_coverage_index_tool``coverage_tool` 增加 `gate` / `include_prior`。主上下文无法逐文件深读 500+ 文件,必须用并行子代理分组深读,否则全库覆盖永远卡在 2-5%。覆盖率为**文件数口径**:已深读文件数 / 总文件数。
**目标**:全库覆盖 ≥85% **且** 高风险覆盖 ≥95%(双目标 `gate="both"`standard 档位)**且**每个深读文件通过三件套质量门禁(①单元完整性=100% ②行覆盖≥95% ③防伪抽验≤15次/轮,见 references/deep-read-pipeline.md §〇)。深读计划按风险权重贪心,85% 的全库计划会先选满全部高风险文件,天然同时逼近高风险 95%。
**流程**
```
a. 调 deep_read_plan_tool(gate="overall", target_coverage=85, include_prior=True)
→ 返回分组清单 groups[{name, weight, files[]}] + remaining_files
include_prior=True 自动排除上一轮已深读且未变更的文件,实现增量)
b. 按组并行派发 explore 子代理(每次 4-6 个并行,分多批)。
每个子代理深读该组**全部**文件,返回(**落盘到临时目录,勿回传大 JSON**):
- 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 证据的条目视为未深读**(该文件须重读)
prompt 模板见 references/deep-read-pipeline.md
⚠️ **禁止用 `DEEPREAD_CONFIRM: <路径|总行数|已读范围|单元数>` 单行摘要替代结构化 JSON**:
主代理必须拿到每文件的 `read_ranges`(分段数组)与 `semantic_units`(逐单元
{range,kind,name})并落盘,否则 coverage_tool 无法做行/单元门禁,报告会
"无行覆盖" 且引擎 fail-closed 会全部判缺口。子代理 prompt 必须直接复制
deep-read-pipeline.md §二 的模板,不得自创简化格式。
c. 每波子代理完成后跑三件套门禁(**B 阶段已落地,引擎原生支持**):
- 主代理首选:coverage_tool(deep_read_files=<verified_files>, gate="both+line",
file_read_ranges=<{rel:[[s,e]..]}>, file_semantic_units=<{rel:[{range,kind,name}]}>)
→ 引擎返回 line_coverage_pct / unit_coverage_pct / line_gap_files / unit_gap_files / unit_exempt_files
- 引擎不可用时的兜底:python skills/project-review/scripts/aggregate_deep_read.py <repo_root> <落盘目录>
- line_gap unit_gap → 补读队列;verified → 计入本轮 deep_read_files
d. 防伪抽验(**强制,每波必做,三件套③**):
- 每波子代理完成后立即执行,与 c 门禁同步;**每组抽 2 个文件、每文件抽 2-3 个语义单元**回读比对 note(每波 ≤40 次 read,全审查约 50-72 次分摊到各波)
- 回读:用 read 打开源文件对应 range,比对子代理上报的 semantic_units.note 是否与实际内容相符
- 结果落盘 `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}]
- 抽到假读 → 该组重读并升级抽验率(同组改抽 2 文件×3 单元)
- ⚠️ 各波 spot_check 必须聚合后在 Step 8 注入 `review_data.spot_check`(顶层字段),否则报告渲染"未执行 🔴"且 verify-spot-check.ps1 exit 1
e. 主代理汇总全部 verified_files(去重)→
coverage_tool(deep_read_files=<verified_files>, gate="both+line", include_prior=True) 复算
include_prior 自动复用跨轮索引中 SHA 未变的文件及其 read_ranges
f. 未达标(文件数 <85%/<95% 或 行覆盖 <95% 或 单元有缺口)→
对 coverage 返回的 priority_deep_read_files / line_gap_files / unit_gap_files 补一轮 → 循环直至全达标
⚠️ **禁止降级**unit_gap_files 或 line_gap_files 非空时,**不得**改回 `gate="both"` 静默跳过三件套;
必须补轮重读至空,或显式在报告中标注"三件套未达标 🔴"并列出缺口文件。
g. G2:对 silent_files 抽 15% 深读(见 Step 7.5
h. 报告生成后:save_coverage_index_tool(deep_read_files=<verified_files>, file_read_ranges=<{rel:[[s,e]..]}>)
→ 写 .code-review-graph/coverage-index.jsonv2file + per-file SHA + rangesSHA 来自 nodes.file_hash
```
**约束**
- 子代理数量:whole-project 508 文件 ≈ 14-16 组(后端 6 组 / 前端 8 组,见 references/deep-read-pipeline.md 分组表)。
- 质量控制(三件套,必须全部通过才算深读):
- ① 单元完整性:`semantic_units` 与图谱单元差集 = 空(one-to-one 匹配,宽 range 不能冒充;**严格模式**Props interface / Type / 小函数也必须逐一列出)。巨型文件(**仅当**最大单元行占比>80%)豁免,按行覆盖校验。
- ② 行覆盖:`union(read_ranges)` / 真实行数 ≥95%(分母为真实文件行数,非图节点 line_end——后者有 ±1 偏差)。
- ③ 防伪抽验:主代理**每波必做**,每组抽 2 文件 × 2-3 单元,每波 ≤40 次 read;结果落盘 spot_check 并注入报告。引擎无法防假读,抽样是唯一手段。
- 每条 finding 必须带 `file:line` 证据。
- 若引擎工具 `deep_read_plan_tool` 不可用(旧版本),退化为:先 `coverage_tool``priority_deep_read_files` 手动分组派发。
- 增量语义:`include_prior=True` 时,变更文件(SHA 不同)自动失效需重读,防止"上轮读过"掩盖新代码。
> 🔒 **诚实声明(三件套③的防伪边界)**:防伪抽验的**真实性**(主代理是否真读了文件)引擎与脚本**均无法验证**——`verify-spot-check.ps1` 只能验证**声明完整性**(抽了、单元数>0、range 落在 read_ranges 内)。真实回读依赖主代理行为准则。此为引擎防伪能力的已知边界(deep-read-pipeline.md §〇)。
## Step 6 - Manual adjudication (READ-ONLY)
Present every finding with severity (🔴 blocker / 🟡 major / 🔵 minor), confidence (1-10), file:line and a proposed fix. Group by severity and ask the user per batch: fix / skip / self-fix. 🔴 blockers cannot be batch-skipped. **Do not modify code.**
## Step 7 - Acceptance gate
Any 🔴 blocker → verdict `❌ FAIL`. Classify each finding as Ready / Needs Fix / Unusable.
## Step 7.5 - 覆盖度自检(必须执行,防"审完了"由感觉决定)
`CODE_REVIEW_GUIDE_ZH.md` §3.5 的覆盖度保障机制,报告前完成三件事:
1. **深读名单完整性(G1**:确认名单外文件是"被评估过"而非"被忽略";未被任何信号点名的文件记入 **"未深读文件清单"**,在报告中显式列出。
2. **静默抽检(G2**:调用 `coverage_tool` 取返回的 `silent_files`(未被任何信号点名的文件),随机抽 **15%** 深读;发现 ≥1 major → 该文件升级全量深读,并同社区/同类追加抽检一轮。抽检记录附入报告(抽了几份 / 几个 major / 有无升级)。
3. **三件套质量门禁复核(G1.5**:报告前复核 Step 5.5 的三件套聚合结果——`line_gap_files unit_gap_files` 必须为空(已补读至空);`verified_files``coverage_tool``deep_read_files` 一致;防伪抽验记录(抽了几组/几个单元/有无假读)附入报告。
4. **覆盖度计算(G3**
- **whole-project**:调用 `coverage_tool(deep_read_files=<本轮实际深读文件>, gate="both+line")`,引擎自动计算**三重口径**(文件数口径 + 三件套质量口径):
- **全库覆盖** = `coverage_pct`(已深读文件数 / 全部源文件数)
- **高风险覆盖** = `high_risk_coverage_pct`(已深读高风险文件数 / 信号点名文件数)
- **双口径门禁(G3**`gate="both+line"``target_reached` 要求**全库 ≥85% 且 高风险 ≥95% 且 行覆盖 ≥95% 且 单元完整性无缺口**(标准档位,三件套 AND)。`gate="both"` 保持原语义(仅前两项)。`target_reached=false` → 报告顶部标 🔴 覆盖不足。
- **缺口信息**:返回值含 `remaining_files_to_target`(还差多少文件数)与 `priority_deep_read_files`(按风险权重降序的待深读文件)——据此驱动 Step 5.5 的补轮深读。
- **增量**`include_prior=True` 合并跨轮覆盖索引(.code-review-graph/coverage-index.json)中 SHA 未变的已深读文件。
- **feature(单功能)**:调用 `coverage_tool(deep_read_files=<本轮实际深读文件>, gate="line+unit", file_read_ranges=<ranges>, file_semantic_units=<units>)`——**只做行覆盖 ≥95% + 单元完整性无缺口**,**不做全库/高风险文件数覆盖检查**(引擎返回 `coverage_pct`/`high_risk_coverage_pct``null`,报告只渲染行/单元覆盖区块,不渲染全库/高风险行)。`target_reached=false`(行/单元未达标)→ **禁止生成报告**,执行补读闭环(见下)直至达标。
- ⚠️ **三件套数据必传(行覆盖非 0 的前提)**:feature 主上下文深读每个文件时,必须**边读边累积** `file_read_ranges={<rel>:[[s,e],...]}`(已读行区间)与 `file_semantic_units={<rel>:[{"range":[s,e],"kind","name"},...]}`(文件内语义单元),再传给 `coverage_tool`。**不传则 fail-closed 行覆盖=0%**,报告标"行覆盖 0%"且 `verify-line-coverage.ps1` exit 1 拦截。
- 🔁 **补读闭环(硬性,未达标禁止出报告)**:若 `target_reached=false`**不得生成报告**。必须对 `line_gap_files`/`unit_gap_files`/`missing_data_files` 列出的文件补读缺失行区间/语义单元,重跑 `coverage_tool`,直至 `target_reached=true`。无法读全的文件**移出** `deep_read_files`(line+unit 不看文件数,只保留真正读满 ≥95% 的文件)。报告生成后跑 `verify-line-coverage.ps1`exit 1 → 补读重新生成。
将覆盖度结果**完整透传**到 `review_data.coverage`(直接把 `coverage_tool` 返回值全部字段传入: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/overall_target/high_risk_target/gate/**line_coverage_pct/unit_coverage_pct/line_gap_files/unit_gap_files/unit_exempt_files/missing_data_files**/remaining_files_to_target/remaining_weight_to_target/priority_deep_read_files/uncovered_files/silent_files/note),不要手挑子集,否则计数字段渲染为 0/0 或 N/A。报告会自动渲染 `## 覆盖度` 区块(含行/单元覆盖)。**若 `line_coverage_pct` 为 null(未跑 both+line 或未传三件套),报告会显式标"行覆盖:未执行 🔴",且 verify-line-coverage.ps1 会 exit 1 拦截。** **feature 走 `gate="line+unit"` 时 `coverage_pct`/`high_risk_coverage_pct` 为 `null` 属正常,勿因 null 误判为失败;`reviewed_files` 必须传本轮深读文件数组,报告顶部以可折叠列表(details/summary)展示审查文件。**
**前置健康检查**:调用 `community_health_tool`,若 `needs_postprocess=true`nodes.community_id 归属率 <90%),先 `code-review-graph postprocess` 重建社区归属再计算,否则覆盖度失真。
## Step 7.6 - 覆盖度自检验证(必须执行,防"优化没生效")
> 当 `coverage_tool` / `community_health_tool` / `score_review_tool` / `dedupe_findings_tool` / `generate_report_tool` / `deep_read_plan_tool` / `save_coverage_index_tool` **任一工具不可用**,或报告缺 `## 覆盖度` 区块时,按本步骤自检。机制未生效时禁止用 CLI 兜底继续审查,先修复环境。详见 `docs/plans/review-coverage-guarantee-plan.md` 第八章。
1. **环境自检(阻断)**:确认可用工具含上述 7 件套;缺失 → 检查本地源码 `D:\code-review-graph\code-review-graph-main` 版本为 2.5.0`opencode.json``.mcp.json` 均指向 `.venv\Scripts\code-review-graph.exe`(非 `uv run`/`uvx`)。
2. **进程自检**`Get-Process code-review-graph, uv` 应无残留 serve/uv 进程;残留进程会锁 `.venv\Scripts\code-review-graph.exe`,导致 `uv run``os error 32` → MCP 起不来。清理:`taskkill /PID <pid> /T /F`
3. **引擎自检**`community_health_tool()` 应返回 `needs_postprocess=false``attribution_pct>=90``coverage_tool(deep_read_files=[...], gate="both+line", file_read_ranges=<ranges>, file_semantic_units=<units>)` 应返回 `coverage_pct`/`high_risk_coverage_pct`/`line_coverage_pct`/`unit_coverage_pct`/`line_gap_files`/`unit_gap_files`/`unit_exempt_files`/`target_reached`/`remaining_weight_to_target`/`priority_deep_read_files`/`uncovered_files`/`silent_files` 全字段;`deep_read_plan_tool` 应返回 `groups`/`planned_files`
4. **恢复路径**:杀残留进程 → `uv sync``D:\code-review-graph\code-review-graph-main`)修复 venv → 重启 opencode 新会话 → 重新执行本步骤 1-3 直到 PASS。
## Step 8 - Report
Call `generate_report_tool(review_data=<REQUIRED SCHEMA>)` to write `code-review-report.html` and `code-review-report.md` (default `format="both"`).
> **报告生成后(Step 8.6 命名自检通过后)必须执行**:
> `save_coverage_index_tool(deep_read_files=<本轮全量深读清单>, file_read_ranges=<{rel:[[s,e]..]}>)` —— 写跨轮覆盖索引 `.code-review-graph/coverage-index.json`v2file + per-file SHA + rangesSHA 来自 nodes.file_hash)。下一轮审查 `include_prior=True` 时自动复用未变更文件及其行区间,实现增量覆盖,多轮后增量归零即全覆盖。
### ⚠️ review_data 精确 Schema(必须严格遵循,否则报告只剩类别/位置)
工具在 `scoring.py::build_report_data` 中**只读取以下键**。用错键名/字段名会静默丢内容。
顶层键(均为可选项,但 `findings` 缺了就没有问题清单):
| 顶层键 | 值类型 | 说明 |
|---|---|---|
| `verdict` | `"PASS"` / `"FAIL"` | 结论 |
| `scope` | str | **只允许** `"whole-project"` / `"feature"` / `"change-level"`。❌ 禁止传功能名(如 `"evm"`)——verify 脚本按此判定是否检查行覆盖/抽验,传错会绕过门禁 |
| `tier` | str | 如 `"standard"` |
| `timestamp` | str | 如 `"2026-08-06T15:24:47"` |
| `files` | str | 审查文件列表(逗号分隔,兼容字段) |
| **`reviewed_files`** | **list[str]** | **本轮审查的文件数组**(如 `["server/src/api/evm_api.rs", ...]`;**也可传逗号分隔字符串,引擎会自动拆分**)。whole-project/feature 必传;报告顶部以可折叠列表(details/summary)展示,缺省时回退 `files` 逗号拆分 |
| `baseline` | str | git SHA |
| `quality_score` | int/float | 0-10 |
| `counts` | dict | 如 `{"blocker":0,"major":3,"minor":5}`MD 只读 `critical`/`informational` 键) |
| `metrics` | dict | `{指标名: {grade, value, note}}`**值必须是 dict****`note` 必传**(透传 `score_review_tool` 返回的 note/evidence,否则指标"说明"列为空)。❌ 传扁平标量(如 `"sql_risk": 3`)会被静默丢弃。**仅含五个客观指标**sql_risk/exception_coverage/redundancy_rate/high_risk_density/vulnerability_risk,见 Step 4 指标集约束);引擎会过滤 `blast_radius`/`objective_grade` 等非五指标键 |
| **`findings`** | **list[dict]** | **问题清单(必须用 `findings`,不能用 `issues`** |
| `manual_review` | list[str] | 人工复核项 |
| `llm_judged` | list[str] | 保留兼容字段,**一律置空 `[]`**,不再新增 LLM 判定指标 |
| `spot_check` | dict | **防伪抽验记录(顶层字段,非嵌 coverage)**。whole-project/feature 必传,否则报告渲染"防伪抽验:未执行 🔴"且 verify-spot-check.ps1 exit 1。schema`{groups_sampled, files_sampled, units_sampled, fake_read_found, groups_rereread, samples:[{group,file,unit,range,note_match,in_read_ranges}]}` |
| `summary` | str | 审查摘要 |
`findings` 每条**必须**的字段:
| 字段 | 说明 | 反例(会导致丢失) |
|---|---|---|
| `path` | 文件路径(与 `line` 合成 `location` | ❌ 传合并字符串 `location` |
| `line` | 行号(int,与 `path` 合成 `location` | ❌ 省略 `line` 导致无位置 |
| `message` | 问题描述(工具读 `summary``message` | ❌ 用 `title`/`detail` |
| `fix` | 修复建议 | ❌ 用 `title`/`detail` |
| `severity` | `"blocker"`/`"major"`/`"minor"` | |
| `category` | 如 `"business"`/`"security"` | |
| `confidence` | int 1-10 | |
### 完整示例(可直接替换占位复制)
```json
{"verdict": "PASS", "scope": "feature", "tier": "standard", "timestamp": "2026-08-06T15:24:47",
"files": "server/src/api/evm_api.rs, server/src/domain/evm.rs",
"reviewed_files": ["server/src/api/evm_api.rs", "server/src/domain/evm.rs"],
"baseline": "<sha>", "quality_score": 7.5,
"counts": {"blocker": 0, "major": 1, "minor": 0},
"metrics": {"sql_risk": {"value": 0, "grade": "good", "note": "全部参数化,无注入风险。"}},
"findings": [
{"path": "server/src/services/workflow_service.rs", "line": 111,
"severity": "major", "category": "business", "confidence": 8,
"message": "workflow 更新 completion_percentage 后未失效 EVM 缓存,5 分钟 TTL 内显示过期数据。",
"fix": "在 update_state 中调用 EvmService::invalidate_evm_cache(project_id, None)。"}
],
"manual_review": ["确认 list_evm_cases 无 data_scope 是否为有意设计"],
"summary": "发现 1 个 major。SQL 全部参数化无注入风险,无 blocker,结论 PASS。"}
```
**常见错误(自查清单):**
- ❌ 用 `issues` 键 → 工具只读 `findings`,结果 `问题清单(0`
- ❌ finding 用 `title`/`detail` → 工具读 `message`/`fix`,结果只剩类别+位置
- ❌ finding 直接传 `location` 字符串 → 工具由 `path`+`line` 合成,结果位置为空
- ❌ metrics 传扁平标量(`"sql_risk": 0`)→ 工具要求 `{grade, value, note}` dict
### Step 8.5 - 生成后自检(必须执行,防止空报告回归)
生成报告后**必须**打开生成的 `.md` 文件验证,全部通过才算完成:
1. `## 问题清单(N` 中 N == findings 数量(不是 0
2. 每条 issue 同时含**问题描述**、**位置**、**修复建议** 三要素(位置形如 `` `server/...:111` ``
3. **`## 客观指标` 表格存在且非空**(若传了 metrics)——若缺失,多为 `metrics` 值不是 `{grade, value, note}` dict(扁平标量被静默丢弃),需修正后重新生成
4. **`## 客观指标` 表恰好 5 行**`sql_risk` / `exception_coverage` / `redundancy_rate` / `high_risk_density` / `vulnerability_risk`)——出现其他指标名说明手工注入了 LLM 判定指标,违反 Step 4 指标集约束,需移除后重新生成
5. 若发现缺描述/缺修复建议/问题数=0 → 用上述 schema 修正 `review_data` 后**重新调用** `generate_report_tool` 覆盖,直到通过
### Step 8.6 - 报告命名自检(必须执行,防止报告落错位置/缺时间戳)
生成完成后,对仓库运行命名自检脚本(脚本独立于 code-review-graph CLI,任何版本可用):
```powershell
powershell -File "C:\Users\Administrator\.config\opencode\skills\project-review\verify-report.ps1" -Repo <repo_root>
```
- 退出码 **0** → 通过:所有报告都在 `docs/reviews/` 且文件名带 `-YYYY-MM-DD-HHMMSS` 后缀。
- 退出码 **1** → 存在根目录残留 `code-review-report.*`(漏传 `output_path` 的强信号)。用 `-Fix` 自动归档,或直接以正确 `output_path="docs/reviews/{name}-review-{YYYY-MM-DD-HHMMSS}"` **重新调用** `generate_report_tool` 覆盖,然后重跑脚本确认退出码 0。
- 脚本列出的 historic naming warnings 无需处理(仅提示),但**本次生成的报告**必须满足规范。
### Step 8.7 - 行级覆盖自检(必须执行,防止"无行覆盖还全绿")
生成完成后,对最新报告运行行覆盖自检脚本(独立于 code-review-graph CLI,任何版本可用):
```powershell
powershell -File "C:\Users\Administrator\.config\opencode\skills\project-review\verify-line-coverage.ps1" -Repo <repo_root>
```
- 退出码 **0** → 通过:whole-project/feature 报告含**行覆盖**字段且 ≥95%(三件套数据完整)。
- 退出码 **1** → 阻塞:报告覆盖度区块**没有行覆盖**(漏跑 `gate="both+line"`/`line+unit` 或漏传三件套)或行覆盖 <95%。必须补读(Step 7.5 补读闭环)后重新生成报告,再重跑,直到退出码 0。
- ⚠️ **scope 判定**:脚本只对 `change-level` 跳过;自定义 scope 值(如 `"evm"``"issues"`)只要报告含 `## 覆盖度` 区块,同样按行覆盖 ≥95% 检查——`scope` 传功能名不会绕过门禁。
### Step 8.8 - 防伪抽验自检(必须执行,防止"漏抽验还全绿")
whole-project / feature 审查**必须**执行防伪抽验(三件套③),并校验报告含抽验记录。生成完成后对最新报告运行:
```powershell
powershell -File "C:\Users\Administrator\.config\opencode\skills\project-review\verify-spot-check.ps1" -Repo <repo_root>
```
- 退出码 **0** → 通过:报告覆盖度区块含**防伪抽验**字段且单元数 >0(Step 5.5 抽验已执行并注入 `review_data.spot_check`)。
- 退出码 **1** → 阻塞:报告无防伪抽验字段,或渲染"未执行 🔴"。必须补抽验(Step 5.5 d+ 注入 `spot_check` + 重新生成报告后重跑,直到退出码 0。
- ⚠️ **scope 判定**:同 Step 8.7——只对 `change-level` 跳过;自定义 scope 值(如 `"evm"`)含 `## 覆盖度` 区块时同样检查抽验。
- **诚实声明**:该脚本只能验证抽验**声明完整性**(抽了、单元数>0),无法验证主代理是否真读了文件——真实回读依赖行为准则,此为引擎防伪能力的已知边界。
- **Step 8.6(命名)、Step 8.7(行覆盖)、Step 8.8(防伪抽验)三脚本必须全过**才算审查完成。
**Archive naming:** output the report to `docs/reviews/` with `output_path=<base>/{scope}-review-{YYYY-MM-DD-HHMMSS}` (e.g. `docs/reviews/evm-feature-review-2026-08-06-151522`) so the filename carries an exact timestamp and avoids overwriting same-day reviews. Scope value examples: `full-project` (whole project), `{feature}-feature` (feature), `pr-{branch}` (change-level). **不传 `output_path` 时工具默认写到仓库根目录 `code-review-report.*`——这是违规命名,必须在 Step 8.6 用 `verify-report.ps1` 检出并修复。**
更详细的字段对照和完整模板见 `references/report-schema.md`
## Output Format
`Project Review: N issues (X blocker, Y major, Z minor) — verdict: ✅ PASS / ❌ FAIL`. List each issue with severity, confidence, file:line, problem, and proposed fix.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="project review")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to `"standard"` when minimal is insufficient.
- **whole-project 深读是子代理流水线(Step 5.5),不是主上下文循环。** 主上下文预算 ≤14 tool calls;深读通过并行 explore 子代理(每次 4-6 个)完成,每个子代理返回 `deep_read 确认清单 + findings[]`(带 file:line 证据)。目标:每轮全库覆盖 ≥80% 且高风险 ≥80%gate="both"),并通过跨轮索引增量累积。
@@ -0,0 +1,19 @@
# Unified Review — Common Mistakes
- **Skipping graph context** — always run `get_minimal_context` first; CRG
context is what makes the review token-efficient and blast-radius aware.
- **Rushing to fix** — this skill is READ-ONLY. Present findings, wait for
user decision. Never apply fixes, commit, or push.
- **Forgetting the fixed standard tier** — reviews always run at the
`standard` tier (all layers); don't assume fast/strict behaviour.
- **Judging metrics without evidence** — `score_review_tool` outputs are
heuristics. Cite the evidence, and let the LLM confirm SQL/exception/vuln
findings before presenting them as facts.
- **Missing manual-review modules** — payment, order, inventory, permission,
distributed-lock, data-migration always require a manual review checklist.
- **Forgetting enum completeness reads OUTSIDE the diff** — grep sibling
values, then read each consumer; in-diff review alone is insufficient.
- **Batch-skipping blockers** — 🔴 blockers cannot be batch-skipped; each
needs an explicit user decision.
- **Not producing the report** — always call `generate_report_tool` at the
end and present the text report inline.
@@ -0,0 +1,168 @@
# 并行深读流水线(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` 保留作独立校验兜底(引擎不可用时的替代),两者判定逻辑一致。
@@ -0,0 +1,12 @@
# Data Migration — Manual Review Checklist
High-risk module: schema/data migration requires human confirmation.
- [ ] Migration is idempotent and re-runnable
- [ ] Forward and rollback paths both defined and tested
- [ ] Backfill is batched / resumable for large tables
- [ ] Data type / precision changes do not silently truncate
- [ ] Nullability and default changes safe for existing rows
- [ ] Migration ordering across shards / replicas is consistent
- [ ] Application deploys compatibly with both old and new schema (expand/contract)
- [ ] Irreversible operations are flagged with a documented reason
@@ -0,0 +1,11 @@
# Distributed Lock — Manual Review Checklist
High-risk module: distributed-lock changes require architecture confirmation.
- [ ] Lock has a TTL / expiry — no permanent deadlock after crash
- [ ] Lock release is atomic and ownership-checked (compare-and-delete)
- [ ] Lock scope is correct (key includes the right entity identifiers)
- [ ] Renewal / watchdog exists for long critical sections
- [ ] Locking order is consistent across paths (no lock-ordering deadlock)
- [ ] Fencing tokens / version check prevents stale-holder writes
- [ ] Fail-open vs fail-closed behavior is intentional and documented
@@ -0,0 +1,11 @@
# Inventory Module — Manual Review Checklist
High-risk module: stock/inventory changes require human confirmation.
- [ ] Stock decrement is atomic (conditional UPDATE, not read-then-write)
- [ ] Oversell prevented: `UPDATE ... SET qty = qty - ? WHERE qty >= ?`
- [ ] Reservation vs. deduction semantics are consistent
- [ ] Concurrent orders cannot both reserve the last unit
- [ ] Restock/return increments handled correctly
- [ ] Inventory events are idempotent (retry-safe)
- [ ] Async stock updates propagate to downstream (warehouse, carts) safely
@@ -0,0 +1,12 @@
# Order Module — Manual Review Checklist
High-risk module: order lifecycle changes require human confirmation.
- [ ] State machine transitions are atomic (`WHERE status = ?` updates)
- [ ] Cancellation / timeout / expiry paths complete all side effects
- [ ] Order idempotency key prevents duplicate order creation
- [ ] Price/lock snapshot captured at order time, not at payment time
- [ ] Partial fulfillment / split-shipment handled
- [ ] Negative or inconsistent totals impossible
- [ ] Concurrent edits (cart + order) do not corrupt state
- [ ] Audit trail: every status change logged with reason
@@ -0,0 +1,14 @@
# Payment Module — Manual Review Checklist
High-risk module: payment changes require human confirmation for every
blocker/major fix.
- [ ] Callback idempotency: a duplicated webhook/callback does not double-charge
- [ ] Amounts stored as fixed-point (integers/cents), never floats
- [ ] Currency codes and precision handled correctly
- [ ] Provider signature / HMAC verification on callbacks
- [ ] Refund logic: correct reversal, no double-refund
- [ ] Failure path: payment timeout, declined, retry semantics
- [ ] Transaction boundary spans charge + order-state update
- [ ] Sensitive data (PAN, tokens) never logged or masked on output
- [ ] Ledger/journal entries are append-only and auditable
@@ -0,0 +1,11 @@
# Permission Module — Manual Review Checklist
High-risk module: authorization changes require product/human confirmation.
- [ ] Every endpoint/action enforces the intended permission — no default-allow
- [ ] Role hierarchy / scoping (tenant, org, user) is consistent
- [ ] Object-level permissions checked on read AND write
- [ ] Deny-before-allow ordering is safe
- [ ] Permission checks cannot be bypassed via IDs, query params, or bulk ops
- [ ] New permission/role values handled by all consumers (enum completeness)
- [ ] Sensitive actions audited with actor + target
@@ -0,0 +1,174 @@
# generate_report_tool 入参 Schema(权威参考)
> 依据 `code-review-graph` 源码 `code_review_graph/scoring.py::build_report_data`
> 与 `render_markdown_report` 反推的精确约定。**任何偏离都会导致报告静默丢内容。**
## 1. 顶层键
工具从 `review_data` dict 中只读取以下键(`scoring.py:620-633`):
| 键 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| `scope` | str | `"change-level"` | 审查范围 |
| `tier` | str | `"standard"` | 审查档位 |
| `timestamp` | str | `""` | 生成时间 |
| `files` | str | `""` | 审查文件(逗号分隔字符串,兼容字段) |
| `reviewed_files` | list[str] | `[]` | 本轮审查文件数组;报告顶部以可折叠列表(details/summary)展示,缺省回退 `files` |
| `baseline` | str | `"generic"` | git 基线 SHA |
| `verdict` | str | `"❌ FAIL"` | 结论,`"PASS"``"FAIL"` |
| `quality_score` | int/float | `None` | PR 质量分 |
| `counts` | dict | `{}` | 严重度统计 |
| `metrics` | dict | `{}` | 客观指标(值必须是 dict) |
| **`findings`** | **list[dict]** | `[]` | **问题清单** |
| `manual_review` | list[str] | `[]` | 人工复核项 |
| `llm_judged` | list[str] | `[]` | 保留兼容字段,**一律置空**;不再新增 LLM 判定指标 |
| `summary` | str | `""` | 摘要(仅 HTML 渲染) |
## 2. findings 条目字段(scoring.py:645-658
工具对每条 finding 做如下映射:
```python
data["issues"].append({
"severity": f.get("severity", "minor"),
"category": f.get("category", ""),
"message": f.get("summary", f.get("message", "")), # summary 优先,其次 message
"location": f"{f.get('path','')}:{f.get('line','')}" # 仅当 line 非空
if f.get("line") else str(f.get("path","")),
"confidence": f.get("confidence"),
"fix": f.get("fix", ""),
})
```
| 字段 | 说明 | 若不传会怎样 |
|---|---|---|
| `path` | 文件路径 | 与 `line` 合成 `location`;仅传 path 也可显示路径 |
| `line` | 行号 | **line 为空则 location 只有 path**(无 `:行号` |
| `message` | 问题描述 | 缺失 → 报告只剩类别/位置 |
| `summary` | 问题描述(优先级高于 message) | 同上 |
| `fix` | 修复建议 | 缺失 → 无修复建议段 |
| `severity` | `blocker`/`major`/`minor`(也接受 `critical`/`warn`/`informational` | 默认 `minor` |
| `category` | 如 `business`/`security`/`data` 等 | 默认空 |
| `confidence` | int 1-10 | 缺失则不显示置信度 |
## 3. metrics 结构(scoring.py:636-643
`metrics` 的值必须是 **dict**,每项支持 `grade`/`value`/`note`/`evidence`。**`note` 必传**(透传 `score_review_tool` 返回的 note/evidence),否则报告"说明"列为空。
```json
"metrics": {
"sql_risk": {"value": 0, "grade": "good", "note": "全部参数化查询,无注入风险。"},
"exception_coverage": {"value": 0, "grade": "fail", "note": "Rust Result 误报。"}
}
```
MD 报告显示标签来自硬编码映射:`sql_risk`→SQL 注入风险、`exception_coverage`→异常分支覆盖、`redundancy_rate`→代码冗余率、`high_risk_density`→高风险场景密度、`vulnerability_risk`→漏洞风险。其他指标名直接显示原名。**指标表应仅含上述五个客观指标**(指标集约束见 SKILL.md Step 4),禁止手工注入 `requirement_coverage`/`logic_alignment`/`llm_trust_boundary`/`shell_injection`/`enum_completeness` 等 LLM 判定指标;**也不要混入 `blast_radius`/`objective_grade` 等键——`build_report_data` 会按五指标白名单过滤,非五指标键一律丢弃。**
## 4. counts 说明(scoring.py:727-732
MD 报告的问题统计行只读 `counts.critical``counts.informational`
```python
f"- **问题统计**{counts.get('critical', 0)} 严重 · {counts.get('informational', 0)} 次要"
```
因此若想统计正确,`counts` 需用 `critical`/`informational` 键(或将 major/minor 数量合入)。
## 4b. coverage 字段(Step 7.5 G3,引擎 v2.5.0
`review_data.coverage` 直接透传 `coverage_tool` 返回值**全部字段**(不要手挑子集,否则计数字段渲染 0/0 或 N/A):
| 字段 | 说明 |
|---|---|
| `coverage_pct` | 全库覆盖(已深读文件数 / 全部源文件数,**文件数口径**);`gate="line+unit"`feature)时为 `None` |
| `high_risk_coverage_pct` | 高风险覆盖(已深读高风险文件数 / 信号点名文件数,文件数口径);`gate="line+unit"`feature)时为 `None` |
| `grade` | good/warn/fail |
| `deep_read_count` / `total_files` | 已深读数 / 总源文件数 |
| `high_risk_total_files` / `high_risk_deep_count` | 高风险文件数 / 已深读高风险数 |
| `deep_read_weight` / `total_weight` | 已深读权重 / 总权重(兼容保留,仅用于排序参考) |
| `target_reached` / `target` | 门禁结果 / 高风险目标值(gate="both" 时需全库 ≥85% 且 高风险 ≥95% 才 Truestandard=85/95 |
| `overall_target` / `high_risk_target` | 双目标值:全库 85% / 高风险 95%(报告据此分别显示) |
| `gate` | 门禁口径(high_risk/overall/both/both+line/line+unit |
| `remaining_files_to_target` | 距全库目标还差多少文件数(**驱动补轮的主字段**) |
| `remaining_weight_to_target` | 距目标还差多少权重(兼容保留) |
| `priority_deep_read_files` | 按风险权重降序的待深读文件(`[{path, weight}]` |
| `uncovered_files` | 未深读文件清单(G1 |
| `silent_files` | 静默文件清单(G2 抽检源) |
| `note` | 引擎口径说明 |
> 增量:`coverage_tool(include_prior=True)` 合并跨轮索引 `.code-review-graph/coverage-index.json`
> 中 SHA 未变的已深读文件。报告后必须 `save_coverage_index_tool` 写索引供下轮复用。
### 4b-1. feature(单功能)审查:`gate="line+unit"`
feature 覆盖度门禁**只保留行级覆盖 + 单元覆盖**,不做全库/高风险文件数覆盖检查:
```json
"coverage": {
"gate": "line+unit",
"coverage_pct": null,
"high_risk_coverage_pct": null,
"line_coverage_pct": 100.0,
"unit_coverage_pct": 100.0,
"line_gap_files": [],
"unit_gap_files": [],
"target_reached": true
}
```
- `coverage_pct` / `high_risk_coverage_pct``null` 是**预期行为**(不做文件数覆盖检查),勿误判失败。
- `target_reached` 只由行覆盖 ≥95% + 单元完整性无缺口决定。
- 报告 `## 覆盖度` 区块只渲染行/单元覆盖与状态行,不渲染全库/高风险行。
- 必须同时传 `reviewed_files`(本轮深读文件数组),报告顶部以可折叠列表展示。
## 5. 完整可复制模板
```json
{
"verdict": "PASS",
"scope": "feature",
"target": "EVM",
"tier": "standard",
"timestamp": "2026-08-06T15:24:47",
"files": "server/src/api/evm_api.rs, server/src/services/evm_service.rs",
"baseline": "f4235d00008a0de651ad8988adc1bca27ede1fb2",
"quality_score": 7.5,
"counts": {"blocker": 0, "major": 3, "minor": 5},
"metrics": {
"sql_risk": {"value": 0, "grade": "good", "note": "全部参数化查询,无注入风险。"}
},
"findings": [
{
"path": "server/src/services/workflow_service.rs",
"line": 111,
"severity": "major",
"category": "business",
"confidence": 8,
"message": "progressive 模式下 EV 计算依赖 workflow_states.completion_percentage,更新后未失效 EVM 缓存,5 分钟 TTL 内显示过期数据。",
"fix": "在 update_state / create_state / delete_state 中调用 EvmService::invalidate_evm_cache(project_id, None)。"
}
],
"manual_review": [
"list_evm_cases 无 data_scope 是否为有意设计(需产品/权限负责人确认)"
],
"summary": "发现 3 个 major 与 5 个 minor。SQL 全部参数化无注入风险,无 blocker,结论 PASS。"
}
```
## 6. 错误用法 → 现象对照表
| 错误用法 | 代码示例 | 现象 |
|---|---|---|
| 用 `issues` 键 | `{"issues": [...]}` | `问题清单(0`,条目全丢 |
| finding 用 `title`/`detail` | `{"title": "...", "detail": "..."}` | 只剩类别+位置,描述/修复为空 |
| 传合并 `location` | `{"location": "a.rs:5"}` | 位置为空(工具只认 path+line |
| metrics 传扁平标量 | `"sql_risk": 0` | 指标表空(要求 dict |
| `counts` 用 major/minor | `{"counts":{"major":3}}` | MD 统计行显示 0 严重·0 次要 |
## 7. 生成后自检清单(Step 8.5
生成报告后必须打开 `.md` 验证:
- [ ] `## 问题清单(N`N == findings 条数,且 > 0
- [ ] 每条 issue 同时含描述 + 位置(`` `path:line` ``+ 修复建议
- [ ] 若任一缺失 → 修正 `review_data` 字段后重新调用 `generate_report_tool` 覆盖
@@ -0,0 +1,238 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>代码审查报告</title>
<style>
:root { --border:#d0d7de; --bg:#f6f8fa; --fg:#1f2328; --muted:#57606a;
--good:#1a7f37; --warn:#9a6700; --fail:#cf222e; --na:#57606a;
--blocker:#cf222e; --major:#9a6700; --minor:#57606a;
--critical:#cf222e; --informational:#0969da; }
* { box-sizing: border-box; }
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
margin:0; padding:2rem 1rem; color:var(--fg); background:#fff; line-height:1.55; }
.wrap { max-width:960px; margin:0 auto; }
h1 { font-size:1.5rem; margin:0 0 .25rem; }
h2 { font-size:1.1rem; margin:1.5rem 0 .5rem; padding-bottom:.25rem;
border-bottom:1px solid var(--border); }
.meta { color:var(--muted); font-size:.9rem; margin-bottom:1.5rem; }
.verdict { display:inline-block; padding:.25rem .75rem; border-radius:20px;
font-weight:700; font-size:.95rem; }
.verdict.pass { background:#dafbe1; color:var(--good); }
.verdict.fail { background:#ffebe9; color:var(--fail); }
table { border-collapse:collapse; width:100%; margin:.75rem 0; }
th,td { border:1px solid var(--border); padding:.45rem .6rem; text-align:left;
font-size:.9rem; vertical-align:top; }
th { background:var(--bg); }
.grade.good { color:var(--good); font-weight:600; }
.grade.warn { color:var(--warn); font-weight:600; }
.grade.fail { color:var(--fail); font-weight:600; }
.grade.na { color:var(--na); }
.issue { margin:.6rem 0; padding:.65rem .8rem; border:1px solid var(--border);
border-radius:6px; background:#fff; }
.issue .tag { display:inline-block; padding:.1rem .5rem; border-radius:10px;
font-size:.75rem; font-weight:700; color:#fff; margin-right:.5rem; }
.tag.blocker, .tag.critical { background:var(--blocker); }
.tag.major, .tag.warn { background:var(--major); }
.tag.minor, .tag.informational { background:var(--minor); }
.issue .cat { font-weight:600; }
.issue .loc { color:var(--muted); font-size:.85rem; margin-top:.2rem; }
.issue .fix { margin-top:.35rem; font-size:.88rem; background:var(--bg);
padding:.4rem .6rem; border-radius:4px; }
.muted { color:var(--muted); font-size:.85rem; }
code { background:var(--bg); padding:.1rem .3rem; border-radius:4px;
font-size:.88em; }
details.reviewed { margin:.5rem 0; border:1px solid var(--border);
border-radius:6px; padding:.4rem .8rem; }
details.reviewed summary { cursor:pointer; font-weight:600; }
details.reviewed ul { margin:.4rem 0 0; padding-left:1.2rem; }
details.reviewed li { margin:.15rem 0; }
</style>
</head>
<body>
<div class="wrap" id="app"></div>
<script>
const data = {{REPORT_DATA}};
function esc(s) {
return String(s ?? "").replace(/[&<>"']/g, c => ({
"&":"&amp;", "<":"&lt;", ">":"&gt;", '"':"&quot;", "'":"&#39;"
})[c]);
}
function verdictClass(v) {
v = String(v || "").toUpperCase();
return v.includes("PASS") ? "pass" : "fail";
}
// Collapsible list of the reviewed files (native <details>/<summary>, no JS).
// Accepts an array OR a comma-separated string (agents pass both). Falls
// back to the flat ``files`` string when nothing structured is given.
function renderReviewedFiles(data) {
let arr = data.reviewed_files;
if (typeof arr === "string") {
arr = arr.split(",").map(s => s.trim()).filter(Boolean);
}
if (!Array.isArray(arr) || !arr.length) {
return data.files ? `<p><b>文件:</b> ${esc(data.files)}</p>` : "";
}
return `<details class="reviewed">
<summary>审查文件 (${arr.length}) <span class="muted">点击展开/收起</span></summary>
<ul>${arr.map(f => `<li><code>${esc(f)}</code></li>`).join("")}</ul>
</details>`;
}
const metricLabels = {
sql_risk: "SQL 注入风险",
exception_coverage: "异常分支覆盖",
redundancy_rate: "代码冗余率",
high_risk_density: "高风险场景密度",
vulnerability_risk: "漏洞风险",
};
const gradeLabels = { good: "良好", warn: "警告", fail: "不合格", na: "不适用" };
const sevLabels = {
blocker: "🔴 阻塞", critical: "🔴 严重",
major: "🟡 主要", warn: "🟡 主要",
minor: "🔵 次要", informational: "🔵 次要",
};
let html = `<h1>代码审查报告</h1>
<div class="meta">
<span class="verdict ${verdictClass(data.verdict)}">${esc(data.verdict || "无结论")}</span>
&nbsp; 档位: <code>${esc(data.tier || "standard")}</code>
&nbsp; 范围: <code>${esc(data.scope || "change-level")}</code>
${data.baseline ? `&nbsp; 基线: <code>${esc(data.baseline)}</code>` : ""}
</div>`;
if (data.quality_score != null) html += `<p><b>PR 质量分:</b> ${esc(data.quality_score)}/10</p>`;
if (data.timestamp) html += `<p class="muted">生成时间: ${esc(data.timestamp)}</p>`;
html += renderReviewedFiles(data);
if (data.summary) html += `<p>${esc(data.summary)}</p>`;
const cov = data.coverage || {};
// The coverage section renders whenever file-count coverage OR line/unit
// coverage was computed. gate="line+unit" (feature reviews) returns
// coverage_pct=null, so the 全库/高风险 rows are skipped and only the
// line/unit rows + spot-check render.
if (cov && (cov.coverage_pct != null || cov.line_coverage_pct != null)) {
const isLineOnly = cov.coverage_pct == null;
const covOk = cov.target_reached;
const covCls = covOk ? "good" : "fail";
const covStatus = covOk ? "✅ 达标" : "🔴 覆盖不足";
const oTarget = cov.overall_target ?? cov.target ?? "N/A";
const hTarget = cov.high_risk_target ?? cov.target ?? "N/A";
html += `<h2>覆盖度</h2>
<p><span class="verdict ${covCls}">${covStatus}</span></p>`;
if (!isLineOnly) {
html += `<p><b>全库覆盖:</b> ${esc(cov.coverage_pct ?? "N/A")}% — 已深读 ${esc(cov.deep_read_count ?? "N/A")}/${esc(cov.total_files ?? "N/A")}(目标 ${esc(oTarget)}%</p>
<p><b>高风险覆盖:</b> ${esc(cov.high_risk_coverage_pct ?? "N/A")}% — 已深读 ${esc(cov.high_risk_deep_count ?? "N/A")}/${esc(cov.high_risk_total_files ?? "N/A")}(目标 ${esc(hTarget)}%</p>`;
}
html += renderLineUnit(cov);
html += renderSpotCheck(data.spot_check);
if (!isLineOnly && (cov.uncovered_files || []).length) {
html += `<p class="muted"><b>未深读文件:</b> ${esc(cov.uncovered_files.length)} 个(静默文件 ${esc((cov.silent_files || []).length)} 个)</p>`;
}
}
// Line / unit coverage (three-piece suite items 1-2). Fail-closed: a
// missing line/unit coverage renders "未执行 🔴" so reviews that skipped
// gate="both+line" or the three-piece data are visible, never silent green.
function renderLineUnit(cov) {
const linePct = cov.line_coverage_pct;
const unitPct = cov.unit_coverage_pct;
const lineTarget = cov.line_target ?? 95.0;
const unitTarget = cov.unit_target ?? 100.0;
const lineGap = (cov.line_gap_files || []).length;
const unitGap = (cov.unit_gap_files || []).length;
const missing = (cov.missing_data_files || []).length;
let s = "";
if (linePct == null || unitPct == null) {
s += `<p><b>行覆盖:</b> 未执行 🔴 <span class="muted">(coverage_tool 未用 gate="both+line" 或未传三件套数据)</span></p>`;
} else {
const lineOk = linePct >= lineTarget && lineGap === 0;
const unitOk = unitPct >= unitTarget && unitGap === 0;
s += `<p><b>行覆盖:</b> ${esc(linePct)}% — 目标 ${esc(lineTarget)}%(缺口 ${esc(lineGap)} 文件)${lineOk ? "✅" : "🔴"}</p>`;
s += `<p><b>单元覆盖:</b> ${esc(unitPct)}% — 目标 ${esc(unitTarget)}%(缺口 ${esc(unitGap)} 文件)${unitOk ? "✅" : "🔴"}</p>`;
}
if (missing) {
s += `<p class="muted"><b>三件套数据缺失:</b> ${esc(missing)} 个文件(缺 read_ranges/语义单元,已按 fail-closed 计为缺口)</p>`;
}
return s;
}
// Anti-fake spot check (three-piece suite item 3). Fail-closed: a
// missing/incomplete spot_check renders "未执行 🔴" so reviews that
// skipped the sampled re-read are visible instead of silently green.
function renderSpotCheck(spot) {
if (!spot) {
return `<p><b>防伪抽验:</b> 未执行 🔴 <span class="muted">(主代理未回读任何语义单元;Step 5.5 应执行每组抽 2 文件 × 2-3 单元并落盘 spot_check)</span></p>`;
}
const groups = spot.groups_sampled;
const files = spot.files_sampled;
const units = spot.units_sampled;
const fake = spot.fake_read_found || 0;
const rereread = spot.groups_rereread || [];
if (!units) {
return `<p><b>防伪抽验:</b> 未执行 🔴 <span class="muted">(spot_check 已上报但单元数为 0)</span></p>`;
}
const mark = (fake || rereread.length) ? "🔴 发现假读" : "✅";
let s = `<p><b>防伪抽验:</b> 抽样 ${esc(files)} 文件 / ${esc(units)} 单元 / ${esc(groups)} 组,假读 ${esc(fake)} ${mark}</p>`;
if (rereread.length) {
s += `<p class="muted">因假读重读组:${esc(rereread.join(", "))}</p>`;
}
return s;
}
const metrics = data.metrics || {};
const mkeys = Object.keys(metrics);
if (mkeys.length) {
html += `<h2>客观指标</h2><table><tr><th>指标</th><th>数值</th><th>评级</th><th>说明</th></tr>`;
for (const k of mkeys) {
const m = metrics[k] || {};
const g = m.grade || "na";
const label = metricLabels[k] || k;
const gradeText = gradeLabels[g] || g;
html += `<tr>
<td>${esc(label)}</td>
<td>${esc(m.value ?? "N/A")}</td>
<td class="grade ${esc(g)}">${esc(gradeText)}</td>
<td class="muted">${esc(m.note || "")}</td>
</tr>`;
}
html += `</table>`;
}
const issues = data.issues || [];
html += `<h2>问题清单 (${issues.length})</h2>`;
if (!issues.length) {
html += `<p class="muted">未发现问题。</p>`;
}
for (const i of issues) {
const sev = (i.severity || "minor").toLowerCase();
const sevLabel = sevLabels[sev] || i.severity;
html += `<div class="issue">
<span class="tag ${esc(sev)}">${esc(sevLabel)}</span>
<span class="cat">${esc(i.category)}</span>
${esc(i.message || "")}
${i.confidence != null ? `<span class="muted">(置信度 ${esc(i.confidence)}/10)</span>` : ""}
${i.location ? `<div class="loc">→ ${esc(i.location)}</div>` : ""}
${i.fix ? `<div class="fix"><b>修复建议:</b> ${esc(i.fix)}</div>` : ""}
</div>`;
}
const manual = data.manual_review || [];
if (manual.length) {
html += `<h2>需要人工审查</h2><ul>`;
for (const m of manual) html += `<li>${esc(m)}</li>`;
html += `</ul>`;
}
const judged = data.llm_judged || [];
if (judged.length) {
html += `<p class="muted"><b>需 LLM 判断的指标:</b> ${judged.map(esc).join(", ")}</p>`;
}
document.getElementById("app").innerHTML = html;
</script>
</body>
</html>
@@ -0,0 +1,82 @@
# Unified Review — Generic Checklist
Reference for the Layer-1 chain decomposition and gstack CRITICAL sub-pass.
Load the language-specific checklist when available (`java-spring.md`,
`python-django.md`, `python-fastapi.md`, `node-express.md`, `go-gin.md`,
`csharp-dotnet.md`, `rust.md`, `php-laravel.md`, `ruby-rails.md`); otherwise
use this generic list.
## Layer 1 — eight categories
For each changed area mark ✅ Clean / ⚠️ Issues Found / — N/A.
1. **Interface** — parameter validation, response conventions, HTTP status
codes, rate limiting, API versioning, protocol correctness
2. **Business** — logic aligns with requirements, state machine correctness,
idempotency design, distributed locks
3. **Data** — SQL injection, query performance, index usage, transaction
boundaries, cache invalidation
4. **Utility** — input validity, no side effects, error return values,
date/time timezone handling
5. **Error handling** — exception classification, fallback logic, error
message sanitization, retry with backoff
6. **Security** — AuthN/AuthZ, sensitive data masking, permission control,
CSRF/XSS prevention
7. **Performance** — N+1 queries, caching strategy, connection pooling,
batch operations, blocking in async paths
8. **Observability** — structured logging with correlation IDs, metrics,
health checks
## gstack CRITICAL sub-pass (highest severity)
### SQL & Data Safety
- String interpolation in SQL — use parameterized queries
- TOCTOU check-then-set — use atomic `WHERE` + update
- Bypassing model validations for direct DB writes
- N+1 queries — missing eager loading
### Race Conditions & Concurrency
- Read-check-write without uniqueness constraint / duplicate-key retry
- find-or-create without a unique DB index
- Status transitions not atomic (`WHERE old_status = ? UPDATE ...`)
- Unsafe HTML rendering on user-controlled data
### LLM Output Trust Boundary
- LLM-generated values (emails, URLs, names) written to DB without format
validation
- Structured tool output accepted without type/shape checks
- LLM-generated URLs fetched without an allowlist (SSRF)
- LLM output stored in knowledge bases without sanitization (stored prompt
injection)
### Shell Injection
- `subprocess` with `shell=True` AND interpolated command strings
- `os.system()` with variable interpolation
- `eval()`/`exec()` on LLM-generated code without sandboxing
### Enum & Value Completeness
- New enum/status/tier values: read (not just grep) every consumer that
switches/filters/displays the value
- Check allowlists and `case`/`if-elsif` chains for fall-through
## Suppressions — do NOT flag
- Harmless redundancy that aids readability
- "Add a comment explaining a threshold" — thresholds drift
- Consistency-only changes
- Anything already addressed in the diff
## Severity
- 🔴 **blocker** — must fix before merge (injection, secrets, missing
transaction, auth bypass) → verdict FAIL
- 🟡 **major** — should fix before merge (missing validation, missing
fallback, N+1, unmasked data)
- 🔵 **minor** — can optimize later (naming, duplicate code, comments)
## Confidence calibration
- 9-10 verified by reading specific code
- 7-8 high-confidence pattern match
- 5-6 medium — show with caveat
- 3-4 low — move to appendix
- 1-2 speculation — suppress unless severity would be P0
Every finding: `[SEVERITY] (confidence: N/10) file:line — problem → fix`.
@@ -0,0 +1,13 @@
# API Contract Specialist
Focus: API and interface contract changes in the diff.
- [ ] Breaking changes to public endpoints (paths, params, response shape)
- [ ] Versioning compatibility (deprecations, fallbacks)
- [ ] Request/response validation matches the schema
- [ ] Error response shape is consistent
- [ ] Authentication/authorization behavior unchanged for existing consumers
- [ ] Renamed/moved functions: all callers updated
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"api-contract","summary":"...","fix":"...","source":"api-contract"}`
@@ -0,0 +1,15 @@
# Data Migration Specialist
Focus: database schema and data migrations in the diff.
- [ ] Migration idempotent and re-runnable
- [ ] Forward + rollback paths defined
- [ ] Backfill batched / resumable
- [ ] Type/precision changes do not truncate data
- [ ] Nullability/default changes safe for existing rows
- [ ] Application deploy compatible with old + new schema (expand/contract)
Insurance specialist — always runs, even when silent.
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"data-migration","summary":"...","fix":"...","source":"data-migration"}`
@@ -0,0 +1,16 @@
# Maintainability Specialist
Focus: code quality and maintainability issues.
- [ ] Dead code / unreachable branches / unused variables
- [ ] Magic numbers → named constants
- [ ] Overcomplicated abstractions (indirection without payoff)
- [ ] Copy-paste blocks that should be shared (only when it aids clarity)
- [ ] Functions too large / doing too much
- [ ] Stale comments contradicting the code
Suppress: harmless redundancy that aids readability, comment-on-threshold
requests, consistency-only changes.
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"maintainability","summary":"...","fix":"...","source":"maintainability"}`
@@ -0,0 +1,14 @@
# Performance Specialist
Focus: performance and resource efficiency in the diff.
- [ ] N+1 queries — missing eager loading
- [ ] Unindexed lookups in hot loops
- [ ] O(n×m) lookups in views/loops
- [ ] Blocking calls in async paths (sync subprocess, requests, sleep)
- [ ] Connection pool exhaustion, unbounded retries
- [ ] Bundle/asset size regressions (frontend)
- [ ] Redundant recomputation / missing caching
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"performance","summary":"...","fix":"...","source":"performance"}`
@@ -0,0 +1,18 @@
# Red Team Specialist (conditional)
Focus: find what the primary and specialist reviewers MISSED. Only dispatched
when the diff is large (>200 lines) or a specialist found a critical issue.
Think like an attacker and a chaos engineer:
- [ ] Cross-cutting concerns the specialist checklists do not cover
- [ ] Integration boundary failures (service-to-service, module-to-module)
- [ ] Failure modes: what breaks in production under load, restart, partial
failure
- [ ] Silent data corruption paths (wrong results without errors)
- [ ] Error handling that swallows failures
- [ ] Trust boundary violations
- [ ] Race conditions and edge cases the primary review missed
Be adversarial. No compliments — just the problems. Tag findings with
`"source":"red-team"`. Output `NO FINDINGS` when nothing new is found.
@@ -0,0 +1,17 @@
# Security Specialist
Focus: security vulnerabilities in the diff.
- [ ] SQL injection (string interpolation, parameterized queries)
- [ ] AuthN/AuthZ bypasses, missing permission checks
- [ ] XSS (unsafe HTML rendering on user data)
- [ ] Sensitive data exposure / missing masking in logs and responses
- [ ] SSRF (fetching user/LLM-controlled URLs without allowlist)
- [ ] Command injection (`shell=True` + interpolation)
- [ ] Hardcoded secrets / credentials
- [ ] CSRF / missing rate limiting on auth endpoints
Insurance specialist — always runs, even when silent.
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"security","summary":"...","fix":"...","source":"security"}`
@@ -0,0 +1,14 @@
# Testing Specialist
Focus: test coverage gaps and tests that would catch the issues found.
- [ ] Every changed function has at least a happy-path test
- [ ] Negative/error paths tested (invalid input, failure branches)
- [ ] Edge cases mirror the happy-path structure
- [ ] If the fix for a finding can be caught by a test, propose a minimal
`test_stub` (framework-detected: jest/vitest/rspec/pytest/go-test)
- [ ] Integration coverage for critical flows (DB, external calls)
- [ ] No assertion-only tests that pass trivially
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"testing","summary":"...","fix":"...","test_stub":"...","source":"testing"}`
@@ -0,0 +1,287 @@
#!/usr/bin/env python3
"""Aggregate per-file deep-read quality (V2.1 three-part gate).
A-stage implementation run by the main agent after each sub-agent wave.
Reads sub-agent output JSON files that were written to disk (not returned
into the main context), computes the three-part gate per file:
① unit completeness : sub-agent semantic_units must cover every graph unit
(Function/Class/Test node) of the file. Hard, 100%.
Giant files (units<3 or max unit >80% of lines) are
exempt from this gate (unit_exempt_files).
② line coverage : |union(read_ranges)| / real line count >= 95%.
Denominator is the REAL file line count read once
(cached), NOT graph node line_end (verified +-1 skew).
③ anti-fake budget : reported to the main agent; only a sampled re-read
can detect fake reads (engine cannot).
Usage:
python aggregate_deep_read.py <repo_root> <out_dir>
<repo_root> repository root (for graph + real file reads)
<out_dir> directory containing sub-agent result JSON files
(each: {"path": rel, "total_lines": int,
"read_ranges": [[s,e],...],
"semantic_units": [{"range":[s,e],"kind","name"}],
"findings": [...]})
Outputs to stdout:
{status, line_gap_files[], unit_gap_files[], unit_exempt_files[],
verified_files[], summary}
"""
import json
import sys
from pathlib import Path
def _abs(root: Path, rel: str) -> Path:
p = Path(rel)
return p if p.is_absolute() else root / p
def _real_line_count(root: Path, rel: str, cache: dict) -> int:
"""Real file line count, cached. Independent of graph node line_end."""
if rel in cache:
return cache[rel]
p = _abs(root, rel)
try:
n = len(p.read_text(encoding="utf-8", errors="replace").splitlines())
except OSError:
n = 0
cache[rel] = n
return n
def _union_len(ranges: list) -> int:
"""Covered line count of a list of inclusive [s,e] ranges."""
if not ranges:
return 0
merged: list[list[int]] = []
for s, e in sorted((int(a), int(b)) for a, b in ranges):
if s < 1:
s = 1
if e < s:
continue
if merged and s <= merged[-1][1] + 1:
merged[-1][1] = max(merged[-1][1], e)
else:
merged.append([s, e])
return sum(e - s + 1 for s, e in merged)
def _graph_units(store, root: Path, rel: str) -> list[dict]:
"""Graph semantic-unit nodes (Function/Class/Test) with line ranges."""
abs_path = _abs(root, rel).as_posix().replace("/", "\\")
q = abs_path.replace("\\", "/")
try:
rows = store._conn.execute(
"SELECT kind, name, line_start, line_end FROM nodes "
"WHERE file_path = ? AND kind IN ('Function','Class','Test') "
"ORDER BY line_start",
(q,),
).fetchall()
except Exception:
return []
out = []
for r in rows:
try:
out.append(
{
"kind": r["kind"],
"name": r["name"],
"line_start": int(r["line_start"]),
"line_end": int(r["line_end"]),
}
)
except (KeyError, TypeError):
continue
return out
def _overlap(a: list, b: list) -> int:
lo, hi = max(a[0], b[0]), min(a[1], b[1])
return max(0, hi - lo + 1)
def _unit_covered(
graph_unit: dict,
read_ranges: list,
unit_ranges: list,
matched_uids: set,
) -> bool:
"""A graph unit is covered iff one reported unit range (a) overlaps its
range by >=80% of the graph unit's span, AND (b) is not already claimed by
a higher-overlap graph unit (one-to-one matching), AND (c) >=80% of the
graph unit's lines fall inside union(read_ranges).
The one-to-one rule stops a single wide range (e.g. the whole file) from
covering every unit by pretending to be all of them."""
gs, ge = graph_unit["line_start"], graph_unit["line_end"]
gspan = max(1, ge - gs + 1)
# exact-range match wins (handles nested/overlapping graph units)
exact = [i for i, (s, e) in enumerate(unit_ranges) if s == gs and e == ge]
if exact:
best_idx = exact[0]
if best_idx in matched_uids:
return False
matched_uids.add(best_idx)
else:
best_idx, best_overlap = None, 0
for i, (s, e) in enumerate(unit_ranges):
ov = _overlap([gs, ge], [s, e])
if ov > best_overlap:
best_overlap, best_idx = ov, i
if best_idx is None or best_idx in matched_uids:
return False
if best_overlap / gspan < 0.8:
return False
matched_uids.add(best_idx)
in_union = 0
for s, e in _merge_ranges(read_ranges):
lo, hi = max(gs, s), min(ge, e)
if lo <= hi:
in_union += hi - lo + 1
if (in_union / gspan) < 0.8:
return False
matched_uids.add(best_idx)
return True
def _merge_ranges(ranges: list) -> list[list[int]]:
merged: list[list[int]] = []
for s, e in sorted((int(a), int(b)) for a, b in ranges or []):
if s < 1:
s = 1
if e < s:
continue
if merged and s <= merged[-1][1] + 1:
merged[-1][1] = max(merged[-1][1], e)
else:
merged.append([s, e])
return merged
def _is_giant_file(graph_units: list, real_lines: int) -> bool:
"""unit-exempt when the largest unit spans >80% of the file's lines.
Aligned with the engine (scoring.py _is_giant_file): small files with a
few ordinary units are NOT exempt - they must cover every unit. A single
huge function (e.g. migrations.rs run_migrations = 98%) makes unit
completeness meaningless, so such files are checked on line coverage only.
"""
if not graph_units or real_lines <= 0:
return False
largest = max(u["line_end"] - u["line_start"] + 1 for u in graph_units)
return (largest / real_lines) > 0.8
def _load_subagent_files(out_dir: Path) -> list[dict]:
payloads = []
for f in sorted(out_dir.glob("*.json")):
try:
payloads.append(json.loads(f.read_text(encoding="utf-8")))
except (OSError, ValueError) as exc:
print(json.dumps({"status": "error", "file": str(f), "error": str(exc)}))
return payloads
def aggregate(repo_root: str, out_dir: str):
root = Path(repo_root)
out = Path(out_dir)
# engine import path: prefer CRG_ENGINE env, else known local checkout
engine = (
__import__("os").environ.get("CRG_ENGINE")
or r"D:\code-review-graph\code-review-graph-main"
)
sys.path.insert(0, engine)
from code_review_graph.tools._common import _get_store
store, root2 = _get_store(repo_root)
cache: dict = {}
verified: list[str] = []
line_gap: list[dict] = []
unit_gap: list[dict] = []
exempt: list[dict] = []
seen: set = set()
for payload in _load_subagent_files(out):
rel = (payload.get("path") or "").replace("\\", "/")
if not rel or rel in seen:
continue
seen.add(rel)
ranges = payload.get("read_ranges") or []
units = payload.get("semantic_units") or []
reported_total = payload.get("total_lines")
real = _real_line_count(root, rel, cache)
if real == 0:
continue
line_cov = _union_len(ranges) / real
g_units = _graph_units(store, root, rel)
giant = _is_giant_file(g_units, real)
unit_ranges = [tuple(map(int, (u.get("range") or [0, 0])[:2])) for u in units]
# ① unit completeness
if g_units and not giant:
matched_uids: set = set()
uncovered = [
{"name": u["name"], "range": [u["line_start"], u["line_end"]]}
for u in g_units
if not _unit_covered(u, ranges, unit_ranges, matched_uids)
]
if uncovered:
unit_gap.append(
{
"path": rel,
"total_units": len(g_units),
"covered_units": len(g_units) - len(uncovered),
"uncovered": uncovered,
}
)
# a unit gap also fails the file
continue
elif g_units and giant:
exempt.append(
{
"path": rel,
"reason": "giant-file: units=%d largest_span=%.0f%%"
% (len(g_units), 100 * max(u["line_end"] - u["line_start"] + 1 for u in g_units) / real),
}
)
# ② line coverage (applies to every file)
if line_cov < 0.95:
line_gap.append(
{
"path": rel,
"coverage_pct": round(line_cov * 100, 1),
"total_lines": real,
"covered_lines": _union_len(ranges),
"reported_total": reported_total,
}
)
continue
verified.append(rel)
store.close()
result = {
"status": "ok",
"verified_files": sorted(verified),
"line_gap_files": sorted(line_gap, key=lambda x: x["coverage_pct"]),
"unit_gap_files": sorted(unit_gap, key=lambda x: x["total_units"]),
"unit_exempt_files": sorted(exempt, key=lambda x: x["path"]),
"summary": (
f"verified={len(verified)} line_gap={len(line_gap)} "
f"unit_gap={len(unit_gap)} unit_exempt={len(exempt)} "
f"(unit_exempt are checked on line coverage only)"
),
}
print(json.dumps(result, ensure_ascii=False, indent=1))
if __name__ == "__main__":
if len(sys.argv) != 3:
print(__doc__)
sys.exit(2)
aggregate(sys.argv[1], sys.argv[2])
@@ -0,0 +1,151 @@
<#
.SYNOPSIS
Verify that a code-review report carries a line-coverage gate (fail-closed).
.DESCRIPTION
Enforces the "three-piece suite" guarantee defined in project-review/SKILL.md
Step 8.7: whole-project / feature review reports MUST contain a line coverage
(xingfugai) field in their coverage section, and its value MUST be >= the
configured target (default 95%). A missing field means the review never ran
coverage_tool(gate="both+line") (or forgot the three-piece data) and the
report would otherwise look green with no line coverage.
Scope handling: only whole-project and feature reviews are gated; other
scopes (e.g. change-level) are skipped.
Exit codes: 0 = line coverage present and >= target (or scope not gated);
1 = blocking violation (missing line coverage or below target).
.PARAMETER Repo
Repository root. Defaults to the current directory.
.PARAMETER Report
Optional relative path (under docs/reviews/) to a specific report file.
Defaults to the newest *.md report in docs/reviews/.
.PARAMETER Target
Minimum line-coverage percentage. Default 95.
.EXAMPLE
powershell -File verify-line-coverage.ps1 -Repo D:\AuraSpace
powershell -File verify-line-coverage.ps1 -Repo D:\AuraSpace -Report full-project-review-2026-08-17-101500.md
#>
param(
[string]$Repo = "",
[string]$Report = "",
[double]$Target = 95.0
)
$ErrorActionPreference = "Stop"
function Write-Step([string]$msg) { Write-Host " $msg" -ForegroundColor DarkGray }
function Write-Ok([string]$msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
function Write-Bad([string]$msg) { Write-Host " [!!] $msg" -ForegroundColor Red }
function Write-Warn([string]$msg) { Write-Host " [!] $msg" -ForegroundColor Yellow }
# Resolve repo root ---------------------------------------------------------
if ([string]::IsNullOrWhiteSpace($Repo)) { $Repo = (Get-Location).Path }
$root = (Resolve-Path -LiteralPath $Repo).Path
$reviewsDir = Join-Path $root "docs\reviews"
Write-Host "verify-line-coverage: repo = $root (target $Target%)"
Write-Host ""
# Locate the report --------------------------------------------------------
$reportPath = ""
if (-not [string]::IsNullOrWhiteSpace($Report)) {
$reportPath = Join-Path $reviewsDir $Report
if (-not (Test-Path -LiteralPath $reportPath)) {
Write-Bad "report not found: $reportPath"
exit 1
}
} else {
if (-not (Test-Path -LiteralPath $reviewsDir)) {
Write-Warn "docs/reviews/ does not exist - no report to verify"
Write-Host "RESULT: PASS (no report)" -ForegroundColor Green
exit 0
}
$reportPath = Get-ChildItem -LiteralPath $reviewsDir -File |
Where-Object { $_.Extension -eq ".md" } |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 -ExpandProperty FullName
if (-not $reportPath) {
Write-Warn "no .md report in docs/reviews/ - nothing to verify"
Write-Host "RESULT: PASS (no report)" -ForegroundColor Green
exit 0
}
}
Write-Step "checking report: $reportPath"
# Parse scope from header (line like: 档位:standard · 范围:whole-project · 基线:...)
$content = [System.IO.File]::ReadAllText($reportPath, [System.Text.Encoding]::UTF8)
$scope = ""
$scopeLine = ($content -split "`n" | Where-Object { $_ -match "范围" } | Select-Object -First 1)
if ($scopeLine -and $scopeLine -match "范围\**[:]\s*([^\s·*]+)") {
$scope = $Matches[1].Trim()
}
# Scope gate: only change-level reviews are skipped. whole-project / feature
# AND any custom scope value (e.g. scope="evm") that still produced a coverage
# section must be checked for line coverage.
if ($scope -eq "change-level") {
Write-Warn "scope = '$scope' is not gated on line coverage (change-level only)"
Write-Host "RESULT: PASS (scope not gated)" -ForegroundColor Green
exit 0
}
Write-Step "scope = '$scope' (gated)"
# Parse ## 覆盖度 section
$covSection = ""
$lines = $content -split "`r?`n"
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match "^##\s*\**覆盖度\**") {
$j = $i + 1
$buf = @()
while ($j -lt $lines.Count -and -not ($lines[$j] -match "^##\s")) {
$buf += $lines[$j]; $j++
}
$covSection = $buf -join "`n"
break
}
}
if (-not $covSection) {
Write-Bad "no coverage section found - line coverage cannot be verified"
exit 1
}
# Line-coverage field present?
$lineLine = ($covSection -split "`n" | Where-Object { $_ -match "行覆盖" } | Select-Object -First 1)
if (-not $lineLine) {
Write-Bad "coverage section has NO line-coverage field - gate='both+line' was not run (or three-piece data missing)"
Write-Step "coverage section:"
foreach ($l in ($covSection -split "`n" | Where-Object { $_.Trim() })) { Write-Step " $($l.Trim())" }
exit 1
}
# "未执行" marker means gate never ran
if ($lineLine -match "未执行") {
Write-Bad "line coverage = NOT RUN (gate='both+line' not run or three-piece data missing)"
Write-Step $lineLine.Trim()
exit 1
}
# Extract percentage (行覆盖:99.8% — 目标 95.0%(缺口 0 文件)✅)
if ($lineLine -match "行覆盖\**[:]\s*([0-9.]+)\s*%") {
$pct = [double]$Matches[1]
if ($pct -lt $Target) {
Write-Bad "line coverage $pct% < target $Target%"
Write-Step $lineLine.Trim()
exit 1
}
Write-Ok "line coverage = $pct% (>= $Target%)"
if ($lineLine -match "缺口\s*([0-9]+)\s*文件" -and [int]$Matches[1] -gt 0) {
Write-Warn "line-coverage gap files present: $($Matches[1])"
}
Write-Host "RESULT: PASS - line coverage verified" -ForegroundColor Green
exit 0
}
Write-Bad "unable to parse line-coverage percentage from: $($lineLine.Trim())"
exit 1
@@ -0,0 +1,124 @@
<#
.SYNOPSIS
Verify / repair code-review report file naming compliance.
.DESCRIPTION
Enforces the archive convention defined in project-review/SKILL.md:
every review report must live under <repo>/docs/reviews/ and be named
<name>-review-YYYY-MM-DD-HHMMSS.{md,html} (exact timestamp suffix).
Two modes:
(default) verify - scan and report non-compliance; exit 0 = clean, 1 = violations.
-Fix - move stray root-level code-review-report.* files into
docs/reviews/ with a timestamp suffix, then verify.
Violations (blocking, exit 1): stray root-level code-review-report.* files.
Warnings (non-blocking): docs/reviews/ files not carrying a -YYYY-MM-DD-HHMMSS
suffix. Historic files are only reported, never renamed automatically.
.PARAMETER Repo
Repository root. Defaults to the current directory.
.PARAMETER Fix
Repair blocking violations automatically (archive stray root reports).
.EXAMPLE
powershell -File verify-report.ps1 -Repo D:\AuraSpace
powershell -File verify-report.ps1 -Repo D:\AuraSpace -Fix
#>
param(
[string]$Repo = "",
[switch]$Fix
)
$ErrorActionPreference = "Stop"
function Write-Step([string]$msg) { Write-Host " $msg" -ForegroundColor DarkGray }
function Write-Ok([string]$msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
function Write-Bad([string]$msg) { Write-Host " [!!] $msg" -ForegroundColor Red }
function Write-Warn([string]$msg) { Write-Host " [!] $msg" -ForegroundColor Yellow }
# Resolve repo root ---------------------------------------------------------
if ([string]::IsNullOrWhiteSpace($Repo)) { $Repo = (Get-Location).Path }
$root = (Resolve-Path -LiteralPath $Repo).Path
$reviewsDir = Join-Path $root "docs\reviews"
$timestampRe = '^(.+?-review)-\d{4}-\d{2}-\d{2}-\d{6}\.(md|html)$'
Write-Host "verify-report: repo = $root"
Write-Host ""
# Phase 0 - optional repair -------------------------------------------------
if ($Fix) {
Write-Host "Fix mode: archiving stray root-level code-review-report.*"
$stray = Get-ChildItem -LiteralPath $root -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '^code-review-report\.(md|html)$' }
if ($stray) {
New-Item -ItemType Directory -Path $reviewsDir -Force | Out-Null
foreach ($f in $stray) {
$ts = $f.LastWriteTime.ToString("yyyy-MM-dd-HHmmss")
$target = Join-Path $reviewsDir "report-review-$ts$($f.Extension)"
if (Test-Path -LiteralPath $target) {
$ts2 = $f.LastWriteTime.ToString("yyyy-MM-dd-HHmmss-fff")
$target = Join-Path $reviewsDir "report-review-$ts2$($f.Extension)"
}
Move-Item -LiteralPath $f.FullName -Destination $target -Force
Write-Ok "archived $($f.Name) -> $target"
}
} else {
Write-Ok "no stray root-level reports to archive"
}
Write-Host ""
}
# Phase 1 - verify root has no stray reports (BLOCKING) ---------------------
$violations = @()
$warnings = @()
Write-Host "1. Root-level report files (blocking):"
$rootStray = Get-ChildItem -LiteralPath $root -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '^code-review-report\.(md|html)$' }
if ($rootStray) {
foreach ($f in $rootStray) {
Write-Bad "stray root report: $($f.FullName)"
$violations += $f.FullName
}
} else {
Write-Ok "no stray root-level code-review-report.* files"
}
Write-Host ""
# Phase 2 - verify docs/reviews naming (WARNING only) -----------------------
Write-Host "2. docs/reviews/*.{md,html} naming (should carry -YYYY-MM-DD-HHMMSS):"
if (-not (Test-Path -LiteralPath $reviewsDir)) {
Write-Ok "docs/reviews/ does not exist yet - nothing to verify"
} else {
$reports = Get-ChildItem -LiteralPath $reviewsDir -File |
Where-Object { $_.Extension -in @(".md", ".html") }
if (-not $reports) {
Write-Ok "no report files present"
} else {
foreach ($f in $reports) {
if ($f.Name -match $timestampRe) {
Write-Ok $f.Name
} else {
Write-Warn "non-conforming name (historic, not auto-fixed): $($f.Name)"
$warnings += $f.FullName
}
}
}
}
Write-Host ""
# Verdict -------------------------------------------------------------------
if ($violations.Count -gt 0) {
Write-Host "RESULT: FAIL ($($violations.Count) blocking violation(s))" -ForegroundColor Red
exit 1
} else {
if ($warnings.Count -gt 0) {
Write-Host "RESULT: PASS ($($warnings.Count) non-blocking historic naming warning(s) - rename manually if desired)" -ForegroundColor Green
} else {
Write-Host "RESULT: PASS - all review reports are archived under docs/reviews/ with timestamp suffixes" -ForegroundColor Green
}
exit 0
}
@@ -0,0 +1,152 @@
<#
.SYNOPSIS
Verify that a code-review report carries an anti-fake spot-check (fail-closed).
.DESCRIPTION
Enforces the "three-piece suite" item 3 (anti-fake spot check) defined in
project-review/SKILL.md Step 8.8: whole-project / feature review reports
MUST contain a "防伪抽验" field in their coverage section, and it must NOT
be "未执行" (i.e. the main agent must have sampled re-read semantic units
from the sub-agent batches). A missing field means the review never ran
the anti-fake spot check and the report would otherwise look green with no
anti-fake guarantee.
Scope handling: only whole-project and feature reviews are gated (they use
the sub-agent deep-read pipeline); change-level reviews skip.
Exit codes: 0 = spot check present and non-zero; 1 = blocking violation
(missing spot check or reported as 未执行 / zero units).
.PARAMETER Repo
Repository root. Defaults to the current directory.
.PARAMETER Report
Optional relative path (under docs/reviews/) to a specific report file.
Defaults to the newest *.md report in docs/reviews/.
.EXAMPLE
powershell -File verify-spot-check.ps1 -Repo D:\AuraSpace
powershell -File verify-spot-check.ps1 -Repo D:\AuraSpace -Report full-project-review-2026-08-18-144500.md
#>
param(
[string]$Repo = "",
[string]$Report = ""
)
$ErrorActionPreference = "Stop"
function Write-Step([string]$msg) { Write-Host " $msg" -ForegroundColor DarkGray }
function Write-Ok([string]$msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
function Write-Bad([string]$msg) { Write-Host " [!!] $msg" -ForegroundColor Red }
function Write-Warn([string]$msg) { Write-Host " [!] $msg" -ForegroundColor Yellow }
# Resolve repo root ---------------------------------------------------------
if ([string]::IsNullOrWhiteSpace($Repo)) { $Repo = (Get-Location).Path }
$root = (Resolve-Path -LiteralPath $Repo).Path
$reviewsDir = Join-Path $root "docs\reviews"
Write-Host "verify-spot-check: repo = $root"
Write-Host ""
# Locate the report --------------------------------------------------------
$reportPath = ""
if (-not [string]::IsNullOrWhiteSpace($Report)) {
$reportPath = Join-Path $reviewsDir $Report
if (-not (Test-Path -LiteralPath $reportPath)) {
Write-Bad "report not found: $reportPath"
exit 1
}
} else {
if (-not (Test-Path -LiteralPath $reviewsDir)) {
Write-Warn "docs/reviews/ does not exist - no report to verify"
Write-Host "RESULT: PASS (no report)" -ForegroundColor Green
exit 0
}
$reportPath = Get-ChildItem -LiteralPath $reviewsDir -File |
Where-Object { $_.Extension -eq ".md" } |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 -ExpandProperty FullName
if (-not $reportPath) {
Write-Warn "no .md report in docs/reviews/ - nothing to verify"
Write-Host "RESULT: PASS (no report)" -ForegroundColor Green
exit 0
}
}
Write-Step "checking report: $reportPath"
# Parse scope from header (line like: 档位:standard · 范围:whole-project · 基线:...)
$content = [System.IO.File]::ReadAllText($reportPath, [System.Text.Encoding]::UTF8)
$scope = ""
$scopeLine = ($content -split "`n" | Where-Object { $_ -match "范围" } | Select-Object -First 1)
if ($scopeLine -and $scopeLine -match "范围\**[:]\s*([^\s·*]+)") {
$scope = $Matches[1].Trim()
}
# Scope gate: only change-level reviews are skipped. whole-project / feature
# AND any custom scope value (e.g. scope="evm") that still produced a coverage
# section must be checked for the anti-fake spot check.
if ($scope -eq "change-level") {
Write-Warn "scope = '$scope' is not gated on anti-fake spot check (change-level only)"
Write-Host "RESULT: PASS (scope not gated)" -ForegroundColor Green
exit 0
}
Write-Step "scope = '$scope' (gated)"
# Parse ## 覆盖度 section
$covSection = ""
$lines = $content -split "`r?`n"
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match "^##\s*\**覆盖度\**") {
$j = $i + 1
$buf = @()
while ($j -lt $lines.Count -and -not ($lines[$j] -match "^##\s")) {
$buf += $lines[$j]; $j++
}
$covSection = $buf -join "`n"
break
}
}
if (-not $covSection) {
Write-Bad "no coverage section found - anti-fake spot check cannot be verified"
exit 1
}
# Anti-fake spot check field present?
$spotLine = ($covSection -split "`n" | Where-Object { $_ -match "防伪抽验" } | Select-Object -First 1)
if (-not $spotLine) {
Write-Bad "coverage section has NO anti-fake spot-check field - Step 5.5 spot check was not run (or spot_check not passed to generate_report_tool)"
Write-Step "coverage section:"
foreach ($l in ($covSection -split "`n" | Where-Object { $_.Trim() })) { Write-Step " $($l.Trim())" }
exit 1
}
# "未执行" marker means spot check never ran
if ($spotLine -match "未执行") {
Write-Bad "anti-fake spot check = NOT RUN (no semantic-unit re-read sampled)"
Write-Step $spotLine.Trim()
exit 1
}
# Present and non-zero: extract sampled counts (防伪抽验:抽样 X 文件 / Y 单元 / Z 组,假读 N ✅)
if ($spotLine -match "抽样\s*(\d+)\s*文件\s*/\s*(\d+)\s*单元") {
$filesSampled = [int]$Matches[1]
$unitsSampled = [int]$Matches[2]
if ($unitsSampled -le 0) {
Write-Bad "anti-fake spot check reported but 0 units sampled"
Write-Step $spotLine.Trim()
exit 1
}
Write-Ok "anti-fake spot check = $filesSampled files / $unitsSampled units sampled"
if ($spotLine -match "假读\s*(\d+)") {
$fake = [int]$Matches[1]
if ($fake -gt 0) {
Write-Warn "fake reads detected: $fake - confirm affected groups were re-read"
}
}
Write-Host "RESULT: PASS - anti-fake spot check verified" -ForegroundColor Green
exit 0
}
Write-Bad "unable to parse anti-fake spot-check counts from: $($spotLine.Trim())"
exit 1
@@ -0,0 +1,28 @@
---
name: refactor-safely
description: Plan and execute safe refactoring using dependency analysis
---
## Refactor Safely
Use the knowledge graph to plan and execute refactoring with confidence.
### Steps
1. Use `refactor_tool` with mode="suggest" for community-driven refactoring suggestions.
2. Use `refactor_tool` with mode="dead_code" to find unreferenced code.
3. For renames, use `refactor_tool` with mode="rename" to preview all affected locations.
4. Use `apply_refactor_tool` with the refactor_id to apply renames.
5. After changes, run `detect_changes_tool` to verify the refactoring impact.
### Safety Checks
- Always preview before applying (rename mode gives you an edit list).
- Check `get_impact_radius_tool` before major refactors.
- Use `get_affected_flows_tool` to ensure no critical paths are broken.
- Run `find_large_functions` to identify decomposition targets.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.
@@ -0,0 +1,29 @@
---
name: review-changes
description: Perform a structured code review using change detection and impact
---
## Review Changes
Perform a thorough, risk-aware code review using the knowledge graph.
### Steps
1. Run `detect_changes_tool` to get risk-scored change analysis.
2. Run `get_affected_flows_tool` to find impacted execution paths.
3. For each high-risk function, run `query_graph_tool` with pattern="tests_for" to check test coverage.
4. Run `get_impact_radius_tool` to understand the blast radius.
5. For any untested changes, suggest specific test cases.
### Output Format
Provide findings grouped by risk level (high/medium/low) with:
- What changed and why it matters
- Test coverage status
- Suggested improvements
- Overall merge recommendation
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.
@@ -0,0 +1,46 @@
---
name: review-delta
description: Review only changes since last commit using impact analysis. Token-efficient delta review with automatic blast-radius detection.
argument-hint: "[file or function name]"
---
# Review Delta
Perform a focused, token-efficient code review of only the changed code and its blast radius.
**Token optimization:** Before starting, call `get_docs_section_tool(section_name="review-delta")` for the optimized workflow. Use ONLY changed nodes + 2-hop neighbors in context.
## Steps
1. **Ensure the graph is current** by calling `build_or_update_graph_tool()` (incremental update).
2. **Get review context** by calling `get_review_context_tool()`. This returns:
- Changed files (auto-detected from git diff)
- Impacted nodes and files (blast radius)
- Source code snippets for changed areas
- Review guidance (test coverage gaps, wide impact warnings, inheritance concerns)
3. **Analyze the blast radius** by reviewing the `impacted_nodes` and `impacted_files` in the context. Focus on:
- Functions whose callers changed (may need signature/behavior verification)
- Classes with inheritance changes (Liskov substitution concerns)
- Files with many dependents (high-risk changes)
4. **Perform the review** using the context. For each changed file:
- Review the source snippet for correctness, style, and potential bugs
- Check if impacted callers/dependents need updates
- Verify test coverage using `query_graph_tool(pattern="tests_for", target=<function_name>)`
- Flag any untested changed functions
5. **Report findings** in a structured format:
- **Summary**: One-line overview of the changes
- **Risk level**: Low / Medium / High (based on blast radius)
- **Issues found**: Bugs, style issues, missing tests
- **Blast radius**: List of impacted files/functions
- **Recommendations**: Actionable suggestions
## Advantages Over Full-Repo Review
- Only sends changed + impacted code to the model (5-10x fewer tokens)
- Automatically identifies blast radius without manual file searching
- Provides structural context (who calls what, inheritance chains)
- Flags untested functions automatically
+66
View File
@@ -0,0 +1,66 @@
---
name: review-pr
description: Review a PR or branch diff using the knowledge graph for full structural context. Outputs a structured review with blast-radius analysis.
argument-hint: "[PR number or branch name]"
---
# Review PR
Perform a comprehensive code review of a pull request or branch diff using the knowledge graph.
**Token optimization:** Before starting, call `get_docs_section_tool(section_name="review-pr")` for the optimized workflow. Never include full files unless explicitly asked.
## Steps
1. **Identify the changes** for the PR:
- If a PR number or branch is provided, use `git diff main...<branch>` to get changed files
- Otherwise auto-detect from the current branch vs main/master
2. **Update the graph** by calling `build_or_update_graph_tool(base="main")` to ensure the graph reflects the current state.
3. **Get the full review context** by calling `get_review_context_tool(base="main")`:
- This uses `main` (or the specified base branch) as the diff base
- Returns all changed files across all commits in the PR
4. **Analyze impact** by calling `get_impact_radius_tool(base="main")`:
- Review the blast radius across the entire PR
- Identify high-risk areas (widely depended-upon code)
5. **Deep-dive each changed file**:
- Read the full source of files with significant changes
- Use `query_graph_tool(pattern="callers_of", target=<func>)` for high-risk functions
- Use `query_graph_tool(pattern="tests_for", target=<func>)` to verify test coverage
- Check for breaking changes in public APIs
6. **Generate structured review output**:
```
## PR Review: <title>
### Summary
<1-3 sentence overview>
### Risk Assessment
- **Overall risk**: Low / Medium / High
- **Blast radius**: X files, Y functions impacted
- **Test coverage**: N changed functions covered / M total
### File-by-File Review
#### <file_path>
- Changes: <description>
- Impact: <who depends on this>
- Issues: <bugs, style, concerns>
### Missing Tests
- <function_name> in <file> - no test coverage found
### Recommendations
1. <actionable suggestion>
2. <actionable suggestion>
```
## Tips
- For large PRs, focus on the highest-impact files first (most dependents)
- Use `semantic_search_nodes_tool` to find related code the PR might have missed
- Check if renamed/moved functions have updated all callers
@@ -0,0 +1,69 @@
---
name: unified-review
description: Three-layer unified code review fusing CRG graph context with ai-code-review scoring methodology and gstack-review fix-first workflow
---
# Unified Review
Perform a three-layer, read-only code review that fuses:
- **CRG graph context** (blast radius, test gaps, affected flows)
- **ai-code-review methodology** (Layer-1 chain decomposition, Layer-2 quantitative scoring, Layer-3 acceptance)
- **gstack-review workflow** (confidence calibration, fix-first, specialist subagents, review-log persistence)
**This skill is READ-ONLY.** Every finding is presented to the user for a manual fix decision. Never apply code changes, commit, or push.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="unified review")`. Use `detail_level="minimal"` on all calls; escalate to `"standard"` only when a metric or finding needs evidence.
## Step 0 - Scope
Review always runs at the fixed `standard` tier (all layers).
Detect the project language/framework and the review scope (change/file/service/chain level). Declare both in the report header.
## Step 1 - Graph context (CRG)
1. Call `build_or_update_graph_tool()` to ensure the graph is current.
2. Call `get_review_context_tool()` for changed files, blast radius, source snippets and review guidance.
3. Call `detect_changes_tool()` for risk-scored change analysis, test gaps and affected flows.
## Step 2 - Layer 1: Chain decomposition (ai-code-review)
Inspect the changed code across eight categories: interface, business, data, utility, error handling, security, performance, observability. Mark each `✅ Clean / ⚠️ Issues Found / — N/A`. Apply the gstack CRITICAL categories as a sub-pass: SQL & Data Safety, Race Conditions & Concurrency, LLM Output Trust Boundary, Shell Injection, and Enum & Value Completeness. Enum completeness requires reading code OUTSIDE the diff (Grep for sibling values, then Read each consumer).
## Step 3 - Layer 2: Quantitative scoring
Call `score_review_tool()` for the objective metrics (SQL risk, exception coverage, redundancy, high-risk density, vulnerability heuristic). The remaining metrics (requirement coverage, logic alignment, trust boundaries) are judged by you from the requirements doc or a generic baseline; without a requirements doc halve their weight in the verdict.
## Step 4 - Specialist dispatch (gstack, diff >= 50 lines)
When the diff has 50+ changed lines, dispatch specialist subagents in parallel via the Agent/task tool, each with a fresh context and its own checklist: testing, maintainability, security, performance, data-migration, api-contract. Security and data-migration always run (insurance). Collect each specialist's JSON findings.
## Step 5 - Merge and dedupe
Call `dedupe_findings_tool(findings=<all raw findings>)` to merge by fingerprint (`path:line:category`), boost multi-source confidence (+1, cap 10), route low-confidence findings to the appendix, and compute the PR quality score.
## Step 6 - Manual adjudication (READ-ONLY)
Present every merged finding with its severity (🔴 blocker / 🟡 major / 🔵 minor), confidence (1-10), file:line and a proposed fix. Group by severity and ask the user per batch: fix / skip / self-fix. 🔴 blockers cannot be batch-skipped. Record skipped findings for prior-review suppression on the next run. **Do not modify code.**
## Step 7 - Acceptance gate (ai-code-review)
Any 🔴 blocker → verdict `❌ FAIL` regardless of other scores. Classify each finding as Ready / Needs Fix / Unusable. Verify the change does not deviate from requirements or architecture conventions.
## Step 8 - Report
Call `generate_report_tool(review_data=<collected verdict, metrics, findings, tier, scope>)` to write `code-review-report.html` and `code-review-report.md` (default `format="both"`). Also present the text report inline.
## Step 9 - Persistence (optional)
If the `gstack-review-log` binary is available, record the review outcome (status, counts, quality score, per-finding actions). If it is unavailable, skip silently.
## Output Format
`Unified Review: N issues (X blocker, Y major, Z minor) — verdict: ✅ PASS / ❌ FAIL`. List each issue with severity, confidence, file:line, problem, and proposed fix. List manual-review items (payment, order, inventory, permission, distributed-lock, data-migration) explicitly.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="unified review")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to `"standard"` when minimal is insufficient.
- Target: complete a unified review in ≤8 tool calls and ≤1200 total output tokens.
@@ -0,0 +1,19 @@
# Unified Review — Common Mistakes
- **Skipping graph context** — always run `get_minimal_context` first; CRG
context is what makes the review token-efficient and blast-radius aware.
- **Rushing to fix** — this skill is READ-ONLY. Present findings, wait for
user decision. Never apply fixes, commit, or push.
- **Ignoring tier** — read `.code-review.yaml`. `fast` skips Layer 2/3;
`strict` requires per-item confirmation for every blocker/major.
- **Judging metrics without evidence** — `score_review_tool` outputs are
heuristics. Cite the evidence, and let the LLM confirm SQL/exception/vuln
findings before presenting them as facts.
- **Missing manual-review modules** — payment, order, inventory, permission,
distributed-lock, data-migration always require a manual review checklist.
- **Forgetting enum completeness reads OUTSIDE the diff** — grep sibling
values, then read each consumer; in-diff review alone is insufficient.
- **Batch-skipping blockers** — 🔴 blockers cannot be batch-skipped; each
needs an explicit user decision.
- **Not producing the report** — always call `generate_report_tool` at the
end and present the text report inline.
@@ -0,0 +1,12 @@
# Data Migration — Manual Review Checklist
High-risk module: schema/data migration requires human confirmation.
- [ ] Migration is idempotent and re-runnable
- [ ] Forward and rollback paths both defined and tested
- [ ] Backfill is batched / resumable for large tables
- [ ] Data type / precision changes do not silently truncate
- [ ] Nullability and default changes safe for existing rows
- [ ] Migration ordering across shards / replicas is consistent
- [ ] Application deploys compatibly with both old and new schema (expand/contract)
- [ ] Irreversible operations are flagged with a documented reason
@@ -0,0 +1,11 @@
# Distributed Lock — Manual Review Checklist
High-risk module: distributed-lock changes require architecture confirmation.
- [ ] Lock has a TTL / expiry — no permanent deadlock after crash
- [ ] Lock release is atomic and ownership-checked (compare-and-delete)
- [ ] Lock scope is correct (key includes the right entity identifiers)
- [ ] Renewal / watchdog exists for long critical sections
- [ ] Locking order is consistent across paths (no lock-ordering deadlock)
- [ ] Fencing tokens / version check prevents stale-holder writes
- [ ] Fail-open vs fail-closed behavior is intentional and documented
@@ -0,0 +1,11 @@
# Inventory Module — Manual Review Checklist
High-risk module: stock/inventory changes require human confirmation.
- [ ] Stock decrement is atomic (conditional UPDATE, not read-then-write)
- [ ] Oversell prevented: `UPDATE ... SET qty = qty - ? WHERE qty >= ?`
- [ ] Reservation vs. deduction semantics are consistent
- [ ] Concurrent orders cannot both reserve the last unit
- [ ] Restock/return increments handled correctly
- [ ] Inventory events are idempotent (retry-safe)
- [ ] Async stock updates propagate to downstream (warehouse, carts) safely
@@ -0,0 +1,12 @@
# Order Module — Manual Review Checklist
High-risk module: order lifecycle changes require human confirmation.
- [ ] State machine transitions are atomic (`WHERE status = ?` updates)
- [ ] Cancellation / timeout / expiry paths complete all side effects
- [ ] Order idempotency key prevents duplicate order creation
- [ ] Price/lock snapshot captured at order time, not at payment time
- [ ] Partial fulfillment / split-shipment handled
- [ ] Negative or inconsistent totals impossible
- [ ] Concurrent edits (cart + order) do not corrupt state
- [ ] Audit trail: every status change logged with reason
@@ -0,0 +1,14 @@
# Payment Module — Manual Review Checklist
High-risk module: payment changes require human confirmation for every
blocker/major fix.
- [ ] Callback idempotency: a duplicated webhook/callback does not double-charge
- [ ] Amounts stored as fixed-point (integers/cents), never floats
- [ ] Currency codes and precision handled correctly
- [ ] Provider signature / HMAC verification on callbacks
- [ ] Refund logic: correct reversal, no double-refund
- [ ] Failure path: payment timeout, declined, retry semantics
- [ ] Transaction boundary spans charge + order-state update
- [ ] Sensitive data (PAN, tokens) never logged or masked on output
- [ ] Ledger/journal entries are append-only and auditable
@@ -0,0 +1,11 @@
# Permission Module — Manual Review Checklist
High-risk module: authorization changes require product/human confirmation.
- [ ] Every endpoint/action enforces the intended permission — no default-allow
- [ ] Role hierarchy / scoping (tenant, org, user) is consistent
- [ ] Object-level permissions checked on read AND write
- [ ] Deny-before-allow ordering is safe
- [ ] Permission checks cannot be bypassed via IDs, query params, or bulk ops
- [ ] New permission/role values handled by all consumers (enum completeness)
- [ ] Sensitive actions audited with actor + target
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>代码审查报告</title>
<style>
:root { --border:#d0d7de; --bg:#f6f8fa; --fg:#1f2328; --muted:#57606a;
--good:#1a7f37; --warn:#9a6700; --fail:#cf222e; --na:#57606a;
--blocker:#cf222e; --major:#9a6700; --minor:#57606a;
--critical:#cf222e; --informational:#0969da; }
* { box-sizing: border-box; }
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
margin:0; padding:2rem 1rem; color:var(--fg); background:#fff; line-height:1.55; }
.wrap { max-width:960px; margin:0 auto; }
h1 { font-size:1.5rem; margin:0 0 .25rem; }
h2 { font-size:1.1rem; margin:1.5rem 0 .5rem; padding-bottom:.25rem;
border-bottom:1px solid var(--border); }
.meta { color:var(--muted); font-size:.9rem; margin-bottom:1.5rem; }
.verdict { display:inline-block; padding:.25rem .75rem; border-radius:20px;
font-weight:700; font-size:.95rem; }
.verdict.pass { background:#dafbe1; color:var(--good); }
.verdict.fail { background:#ffebe9; color:var(--fail); }
table { border-collapse:collapse; width:100%; margin:.75rem 0; }
th,td { border:1px solid var(--border); padding:.45rem .6rem; text-align:left;
font-size:.9rem; vertical-align:top; }
th { background:var(--bg); }
.grade.good { color:var(--good); font-weight:600; }
.grade.warn { color:var(--warn); font-weight:600; }
.grade.fail { color:var(--fail); font-weight:600; }
.grade.na { color:var(--na); }
.issue { margin:.6rem 0; padding:.65rem .8rem; border:1px solid var(--border);
border-radius:6px; background:#fff; }
.issue .tag { display:inline-block; padding:.1rem .5rem; border-radius:10px;
font-size:.75rem; font-weight:700; color:#fff; margin-right:.5rem; }
.tag.blocker, .tag.critical { background:var(--blocker); }
.tag.major, .tag.warn { background:var(--major); }
.tag.minor, .tag.informational { background:var(--minor); }
.issue .cat { font-weight:600; }
.issue .loc { color:var(--muted); font-size:.85rem; margin-top:.2rem; }
.issue .fix { margin-top:.35rem; font-size:.88rem; background:var(--bg);
padding:.4rem .6rem; border-radius:4px; }
.muted { color:var(--muted); font-size:.85rem; }
code { background:var(--bg); padding:.1rem .3rem; border-radius:4px;
font-size:.88em; }
</style>
</head>
<body>
<div class="wrap" id="app"></div>
<script>
const data = {{REPORT_DATA}};
function esc(s) {
return String(s ?? "").replace(/[&<>"']/g, c => ({
"&":"&amp;", "<":"&lt;", ">":"&gt;", '"':"&quot;", "'":"&#39;"
})[c]);
}
function verdictClass(v) {
v = String(v || "").toUpperCase();
return v.includes("PASS") ? "pass" : "fail";
}
const metricLabels = {
sql_risk: "SQL 注入风险",
exception_coverage: "异常分支覆盖",
redundancy_rate: "代码冗余率",
high_risk_density: "高风险场景密度",
vulnerability_risk: "漏洞风险",
};
const gradeLabels = { good: "良好", warn: "警告", fail: "不合格", na: "不适用" };
const sevLabels = {
blocker: "🔴 阻塞", critical: "🔴 严重",
major: "🟡 主要", warn: "🟡 主要",
minor: "🔵 次要", informational: "🔵 次要",
};
let html = `<h1>代码审查报告</h1>
<div class="meta">
<span class="verdict ${verdictClass(data.verdict)}">${esc(data.verdict || "无结论")}</span>
&nbsp; 档位: <code>${esc(data.tier || "standard")}</code>
&nbsp; 范围: <code>${esc(data.scope || "change-level")}</code>
${data.baseline ? `&nbsp; 基线: <code>${esc(data.baseline)}</code>` : ""}
</div>`;
if (data.quality_score != null) html += `<p><b>PR 质量分:</b> ${esc(data.quality_score)}/10</p>`;
if (data.timestamp) html += `<p class="muted">生成时间: ${esc(data.timestamp)}</p>`;
if (data.files) html += `<p><b>文件:</b> ${esc(data.files)}</p>`;
if (data.summary) html += `<p>${esc(data.summary)}</p>`;
const metrics = data.metrics || {};
const mkeys = Object.keys(metrics);
if (mkeys.length) {
html += `<h2>客观指标</h2><table><tr><th>指标</th><th>数值</th><th>评级</th><th>说明</th></tr>`;
for (const k of mkeys) {
const m = metrics[k] || {};
const g = m.grade || "na";
const label = metricLabels[k] || k;
const gradeText = gradeLabels[g] || g;
html += `<tr>
<td>${esc(label)}</td>
<td>${esc(m.value ?? "N/A")}</td>
<td class="grade ${esc(g)}">${esc(gradeText)}</td>
<td class="muted">${esc(m.note || "")}</td>
</tr>`;
}
html += `</table>`;
}
const issues = data.issues || [];
html += `<h2>问题清单 (${issues.length})</h2>`;
if (!issues.length) {
html += `<p class="muted">未发现问题。</p>`;
}
for (const i of issues) {
const sev = (i.severity || "minor").toLowerCase();
const sevLabel = sevLabels[sev] || i.severity;
html += `<div class="issue">
<span class="tag ${esc(sev)}">${esc(sevLabel)}</span>
<span class="cat">${esc(i.category)}</span>
${esc(i.message || "")}
${i.confidence != null ? `<span class="muted">(置信度 ${esc(i.confidence)}/10)</span>` : ""}
${i.location ? `<div class="loc">→ ${esc(i.location)}</div>` : ""}
${i.fix ? `<div class="fix"><b>修复建议:</b> ${esc(i.fix)}</div>` : ""}
</div>`;
}
const manual = data.manual_review || [];
if (manual.length) {
html += `<h2>需要人工审查</h2><ul>`;
for (const m of manual) html += `<li>${esc(m)}</li>`;
html += `</ul>`;
}
const judged = data.llm_judged || [];
if (judged.length) {
html += `<p class="muted"><b>需 LLM 判断的指标:</b> ${judged.map(esc).join(", ")}</p>`;
}
document.getElementById("app").innerHTML = html;
</script>
</body>
</html>
@@ -0,0 +1,82 @@
# Unified Review — Generic Checklist
Reference for the Layer-1 chain decomposition and gstack CRITICAL sub-pass.
Load the language-specific checklist when available (`java-spring.md`,
`python-django.md`, `python-fastapi.md`, `node-express.md`, `go-gin.md`,
`csharp-dotnet.md`, `rust.md`, `php-laravel.md`, `ruby-rails.md`); otherwise
use this generic list.
## Layer 1 — eight categories
For each changed area mark ✅ Clean / ⚠️ Issues Found / — N/A.
1. **Interface** — parameter validation, response conventions, HTTP status
codes, rate limiting, API versioning, protocol correctness
2. **Business** — logic aligns with requirements, state machine correctness,
idempotency design, distributed locks
3. **Data** — SQL injection, query performance, index usage, transaction
boundaries, cache invalidation
4. **Utility** — input validity, no side effects, error return values,
date/time timezone handling
5. **Error handling** — exception classification, fallback logic, error
message sanitization, retry with backoff
6. **Security** — AuthN/AuthZ, sensitive data masking, permission control,
CSRF/XSS prevention
7. **Performance** — N+1 queries, caching strategy, connection pooling,
batch operations, blocking in async paths
8. **Observability** — structured logging with correlation IDs, metrics,
health checks
## gstack CRITICAL sub-pass (highest severity)
### SQL & Data Safety
- String interpolation in SQL — use parameterized queries
- TOCTOU check-then-set — use atomic `WHERE` + update
- Bypassing model validations for direct DB writes
- N+1 queries — missing eager loading
### Race Conditions & Concurrency
- Read-check-write without uniqueness constraint / duplicate-key retry
- find-or-create without a unique DB index
- Status transitions not atomic (`WHERE old_status = ? UPDATE ...`)
- Unsafe HTML rendering on user-controlled data
### LLM Output Trust Boundary
- LLM-generated values (emails, URLs, names) written to DB without format
validation
- Structured tool output accepted without type/shape checks
- LLM-generated URLs fetched without an allowlist (SSRF)
- LLM output stored in knowledge bases without sanitization (stored prompt
injection)
### Shell Injection
- `subprocess` with `shell=True` AND interpolated command strings
- `os.system()` with variable interpolation
- `eval()`/`exec()` on LLM-generated code without sandboxing
### Enum & Value Completeness
- New enum/status/tier values: read (not just grep) every consumer that
switches/filters/displays the value
- Check allowlists and `case`/`if-elsif` chains for fall-through
## Suppressions — do NOT flag
- Harmless redundancy that aids readability
- "Add a comment explaining a threshold" — thresholds drift
- Consistency-only changes
- Anything already addressed in the diff
## Severity
- 🔴 **blocker** — must fix before merge (injection, secrets, missing
transaction, auth bypass) → verdict FAIL
- 🟡 **major** — should fix before merge (missing validation, missing
fallback, N+1, unmasked data)
- 🔵 **minor** — can optimize later (naming, duplicate code, comments)
## Confidence calibration
- 9-10 verified by reading specific code
- 7-8 high-confidence pattern match
- 5-6 medium — show with caveat
- 3-4 low — move to appendix
- 1-2 speculation — suppress unless severity would be P0
Every finding: `[SEVERITY] (confidence: N/10) file:line — problem → fix`.
@@ -0,0 +1,13 @@
# API Contract Specialist
Focus: API and interface contract changes in the diff.
- [ ] Breaking changes to public endpoints (paths, params, response shape)
- [ ] Versioning compatibility (deprecations, fallbacks)
- [ ] Request/response validation matches the schema
- [ ] Error response shape is consistent
- [ ] Authentication/authorization behavior unchanged for existing consumers
- [ ] Renamed/moved functions: all callers updated
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"api-contract","summary":"...","fix":"...","source":"api-contract"}`
@@ -0,0 +1,15 @@
# Data Migration Specialist
Focus: database schema and data migrations in the diff.
- [ ] Migration idempotent and re-runnable
- [ ] Forward + rollback paths defined
- [ ] Backfill batched / resumable
- [ ] Type/precision changes do not truncate data
- [ ] Nullability/default changes safe for existing rows
- [ ] Application deploy compatible with old + new schema (expand/contract)
Insurance specialist — always runs, even when silent.
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"data-migration","summary":"...","fix":"...","source":"data-migration"}`
@@ -0,0 +1,16 @@
# Maintainability Specialist
Focus: code quality and maintainability issues.
- [ ] Dead code / unreachable branches / unused variables
- [ ] Magic numbers → named constants
- [ ] Overcomplicated abstractions (indirection without payoff)
- [ ] Copy-paste blocks that should be shared (only when it aids clarity)
- [ ] Functions too large / doing too much
- [ ] Stale comments contradicting the code
Suppress: harmless redundancy that aids readability, comment-on-threshold
requests, consistency-only changes.
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"maintainability","summary":"...","fix":"...","source":"maintainability"}`
@@ -0,0 +1,14 @@
# Performance Specialist
Focus: performance and resource efficiency in the diff.
- [ ] N+1 queries — missing eager loading
- [ ] Unindexed lookups in hot loops
- [ ] O(n×m) lookups in views/loops
- [ ] Blocking calls in async paths (sync subprocess, requests, sleep)
- [ ] Connection pool exhaustion, unbounded retries
- [ ] Bundle/asset size regressions (frontend)
- [ ] Redundant recomputation / missing caching
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"performance","summary":"...","fix":"...","source":"performance"}`
@@ -0,0 +1,18 @@
# Red Team Specialist (conditional)
Focus: find what the primary and specialist reviewers MISSED. Only dispatched
when the diff is large (>200 lines) or a specialist found a critical issue.
Think like an attacker and a chaos engineer:
- [ ] Cross-cutting concerns the specialist checklists do not cover
- [ ] Integration boundary failures (service-to-service, module-to-module)
- [ ] Failure modes: what breaks in production under load, restart, partial
failure
- [ ] Silent data corruption paths (wrong results without errors)
- [ ] Error handling that swallows failures
- [ ] Trust boundary violations
- [ ] Race conditions and edge cases the primary review missed
Be adversarial. No compliments — just the problems. Tag findings with
`"source":"red-team"`. Output `NO FINDINGS` when nothing new is found.
@@ -0,0 +1,17 @@
# Security Specialist
Focus: security vulnerabilities in the diff.
- [ ] SQL injection (string interpolation, parameterized queries)
- [ ] AuthN/AuthZ bypasses, missing permission checks
- [ ] XSS (unsafe HTML rendering on user data)
- [ ] Sensitive data exposure / missing masking in logs and responses
- [ ] SSRF (fetching user/LLM-controlled URLs without allowlist)
- [ ] Command injection (`shell=True` + interpolation)
- [ ] Hardcoded secrets / credentials
- [ ] CSRF / missing rate limiting on auth endpoints
Insurance specialist — always runs, even when silent.
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"security","summary":"...","fix":"...","source":"security"}`
@@ -0,0 +1,14 @@
# Testing Specialist
Focus: test coverage gaps and tests that would catch the issues found.
- [ ] Every changed function has at least a happy-path test
- [ ] Negative/error paths tested (invalid input, failure branches)
- [ ] Edge cases mirror the happy-path structure
- [ ] If the fix for a finding can be caught by a test, propose a minimal
`test_stub` (framework-detected: jest/vitest/rspec/pytest/go-test)
- [ ] Integration coverage for critical flows (DB, external calls)
- [ ] No assertion-only tests that pass trivially
Output JSON lines:
`{"severity":"CRITICAL|INFORMATIONAL","confidence":N,"path":"file","line":N,"category":"testing","summary":"...","fix":"...","test_stub":"...","source":"testing"}`