chore: sync local changes, add Chinese docs and opencode config
This commit is contained in:
@@ -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.
|
||||
@@ -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 提交。
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++
|
||||
@@ -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`(schema:groups_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.json(v2:file + per-file SHA + ranges,SHA 来自 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`(v2:file + per-file SHA + ranges,SHA 来自 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`(schema:groups_sampled / files_sampled / units_sampled /
|
||||
fake_read_found / groups_rereread / samples[{group,file,unit,range,note_match,in_read_ranges}])。
|
||||
4. 全部达标后,三件套数据传入引擎 coverage_tool(B 阶段引擎原生支持):
|
||||
coverage_tool(deep_read_files=<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.ps1(Step 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% 才 True;standard=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 => ({
|
||||
"&":"&", "<":"<", ">":">", '"':""", "'":"'"
|
||||
})[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>
|
||||
档位: <code>${esc(data.tier || "standard")}</code>
|
||||
范围: <code>${esc(data.scope || "change-level")}</code>
|
||||
${data.baseline ? ` 基线: <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
|
||||
@@ -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 => ({
|
||||
"&":"&", "<":"<", ">":">", '"':""", "'":"'"
|
||||
})[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>
|
||||
档位: <code>${esc(data.tier || "standard")}</code>
|
||||
范围: <code>${esc(data.scope || "change-level")}</code>
|
||||
${data.baseline ? ` 基线: <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"}`
|
||||
Reference in New Issue
Block a user