feat: add project-review workflow (whole-project / single-feature review)
Adds the project-review workflow for code review independent of the git diff. The scope is parsed from the user instruction: 全面/整个项目 -> whole-project (score every source file), otherwise feature + target keyword (locate the code with semantic search + graph queries). - scoring_tools.py: score_review_func gains all_files=True to score every source file in the graph via store.get_all_files() - main.py: score_review_tool gains all_files param; registers the project_review MCP prompt (prompts 6->7) - prompts.py: project_review_prompt(scope, target) with whole-project and feature branches (fixed a precedence bug that truncated the feature text) - skills.py + skills/project-review/: new read-only project-review skill with shared checklists - .opencode/command/code-review-graph-project-review.md: slash command - tests: test_project_review.py (prompt rendering), TestProjectReviewPrompt, skill count assertions 5->6, all_files wiring checks - docs: prompts (6->7) + project-review entries across COMMANDS, CLAUDE, README (+localized), INDEX, architecture, LLM-OPTIMIZED-REFERENCE, CHANGELOG
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
---
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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 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 8 - Report
|
||||
|
||||
Call `generate_report_tool(review_data=<verdict, scope, metrics, findings>)` to write `code-review-report.html` and `code-review-report.md` (default `format="both"`).
|
||||
|
||||
## 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.
|
||||
- Target: complete a project review in ≤12 tool calls and ≤1800 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"}`
|
||||
@@ -53,7 +53,7 @@ Any 🔴 blocker → verdict `❌ FAIL` regardless of other scores. Classify eac
|
||||
|
||||
## Step 8 - Report
|
||||
|
||||
Call `generate_report_tool(review_data=<collected verdict, metrics, findings, tier, scope>)` to write `code-review-report.html`. Also present the text report inline.
|
||||
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)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Code Review Report</title>
|
||||
<title>代码审查报告</title>
|
||||
<style>
|
||||
:root { --border:#d0d7de; --bg:#f6f8fa; --fg:#1f2328; --muted:#57606a;
|
||||
--good:#1a7f37; --warn:#9a6700; --fail:#cf222e; --na:#57606a;
|
||||
@@ -61,28 +61,45 @@ function verdictClass(v) {
|
||||
return v.includes("PASS") ? "pass" : "fail";
|
||||
}
|
||||
|
||||
let html = `<h1>Code Review Report</h1>
|
||||
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 || "NO VERDICT")}</span>
|
||||
Tier: <code>${esc(data.tier || "standard")}</code>
|
||||
Scope: <code>${esc(data.scope || "change-level")}</code>
|
||||
${data.baseline ? ` Baseline: <code>${esc(data.baseline)}</code>` : ""}
|
||||
<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.timestamp) html += `<p class="muted">Generated ${esc(data.timestamp)}</p>`;
|
||||
if (data.files) html += `<p><b>Files:</b> ${esc(data.files)}</p>`;
|
||||
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>Objective Metrics</h2><table><tr><th>Metric</th><th>Value</th><th>Grade</th><th>Notes</th></tr>`;
|
||||
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(k)}</td>
|
||||
<td>${esc(label)}</td>
|
||||
<td>${esc(m.value ?? "N/A")}</td>
|
||||
<td class="grade ${esc(g)}">${esc(g)}</td>
|
||||
<td class="grade ${esc(g)}">${esc(gradeText)}</td>
|
||||
<td class="muted">${esc(m.note || "")}</td>
|
||||
</tr>`;
|
||||
}
|
||||
@@ -90,32 +107,33 @@ if (mkeys.length) {
|
||||
}
|
||||
|
||||
const issues = data.issues || [];
|
||||
html += `<h2>Issues (${issues.length})</h2>`;
|
||||
html += `<h2>问题清单 (${issues.length})</h2>`;
|
||||
if (!issues.length) {
|
||||
html += `<p class="muted">No issues found.</p>`;
|
||||
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(i.severity)}</span>
|
||||
<span class="tag ${esc(sev)}">${esc(sevLabel)}</span>
|
||||
<span class="cat">${esc(i.category)}</span>
|
||||
${esc(i.message || "")}
|
||||
${i.confidence ? `<span class="muted">(confidence ${esc(i.confidence)})</span>` : ""}
|
||||
${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>Fix:</b> ${esc(i.fix)}</div>` : ""}
|
||||
${i.fix ? `<div class="fix"><b>修复建议:</b> ${esc(i.fix)}</div>` : ""}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const manual = data.manual_review || [];
|
||||
if (manual.length) {
|
||||
html += `<h2>Manual Review Required</h2><ul>`;
|
||||
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-judged:</b> ${judged.map(esc).join(", ")}</p>`;
|
||||
html += `<p class="muted"><b>需 LLM 判断的指标:</b> ${judged.map(esc).join(", ")}</p>`;
|
||||
}
|
||||
|
||||
document.getElementById("app").innerHTML = html;
|
||||
|
||||
Reference in New Issue
Block a user