chore: sync local changes, add Chinese docs and opencode config
This commit is contained in:
@@ -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"}`
|
||||
Reference in New Issue
Block a user