# AI Review 评论结构规范

> 触发：`scripts/ops/ai-review-runner.sh` 调 `claude` CLI 输出 PR 评审评论时的结构约束。
> 关联：[`docs/standards/13-pr-description-spec.md`](../../../../docs/standards/13-pr-description-spec.md)（PR 描述规范）

## 为什么有这个规范

AI Review 评论的早期实现把所有评审结论塞进一段散文 `summary`（≤1000 字）。reviewer 拿到评论后得自己提炼"AI 看了什么、发现什么、要不要做事"——评论没结构 = 评论没用。

借鉴 [`13-pr-description-spec.md`](../../../../docs/standards/13-pr-description-spec.md) 的「双目的」思路，AI Review 评论也要同时服务：

1. **当下 reviewer 决策**：30 秒看完知道"能不能 merge / 要不要回应"
2. **未来 retro / AI Review 准确率评估**：半年后回看能知道当时 AI 看了哪些维度、判断依据、有没有漏看

## 评论必备结构

| 区块 | 字段 | 服务目的 |
|---|---|---|
| **元信息 banner** | dry-run 标识 / 跟踪 issue / 评审模式 | 当下：识别是不是阻断；未来：哪个阶段的产物 |
| **Verdict** | `pass` / `pass_with_risk` / `should_fix` / `needs_fix` / `block` | 当下：决策结论（五级语义见 `scripts/ops/ai-review-schema.json` + runner prompt） |
| **一句话** | `summary`（≤200 字，强约束） | 当下：30 秒看完结论 |
| **评审维度矩阵** | `dimensions` 数组（每项含 name / status / note） | 当下：透明度；未来：准确率评估（漏看哪些维度） |
| **发现** | `findings`（按 severity 分组：hard_block / risk / suggestion） | 当下：可 actionable 的具体问题 |
| **给作者的 action** | `recommended_action`（≤200 字） | 当下：作者明确知道要不要回应、做什么 |
| **footer 元数据** | 生成器 / 模型 / 时间戳 / 跟踪 issue | 未来：哪条 review 来自哪个模型 |

## JSON Schema（runner 强校验）

`scripts/ops/ai-review-runner.sh` 用 `--json-schema` 把以下 schema 推给 Claude API 做硬校验：

```json
{
  "type": "object",
  "additionalProperties": false,
  "required": ["verdict", "summary", "dimensions", "findings", "recommended_action"],
  "properties": {
    "verdict": { "enum": ["pass", "pass_with_risk", "should_fix", "needs_fix", "block"] },
    "summary": { "type": "string", "maxLength": 200 },
    "dimensions": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "status"],
        "properties": {
          "name": { "type": "string" },
          "status": { "enum": ["ok", "warn", "block", "n/a"] },
          "note": { "type": "string", "maxLength": 200 }
        }
      }
    },
    "findings": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["severity", "category", "message", "stable_id"],
        "properties": {
          "stable_id": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{2,63}$",
            "description": "稳定语义 ID，kebab-case 3-64 字符；同 PR 跨多轮 review 指同一问题必须复用同一 ID（#259 state 去重）"
          },
          "severity": { "enum": ["hard_block", "risk", "suggestion"] },
          "category": { "type": "string" },
          "message": { "type": "string" },
          "file": { "type": "string" },
          "line": { "type": "integer", "minimum": 0 }
        }
      }
    },
    "recommended_action": { "type": "string", "maxLength": 200 }
  }
}
```

## PR description body 作为 finding 判定输入（#373）

`scripts/ops/ai-review-runner.sh` 会在 prompt 里把 **PR description body 原文**注入到「PR 元信息」之后，并附使用规则。LLM 在 emit findings 前必须先读 body。

判定规则（按严重性）：

| 作者在 PR body 中的处理 | LLM 应该 |
|---|---|
| 合理解释、自洽、与 diff 一致、引用 PRD/issue/标准条款 | **不列为 finding**（视作 ack）；如必要降级为 `suggestion`，message 以"PR body 已 ack：<原话>"开头 |
| 牵强解释（含糊、循环论证、与 diff 矛盾、强行合理化高风险路径） | 仍列为 finding，message 必须引用 body 原话并说明 rationale 为何不成立 |
| 未提及 | 按常规判定 |

特别注意：**高风险路径合规维度**最容易撞重复指控盲点——作者多次解释为何合并高风险改动 / 与 PRD 拆分粒度不同。注入 body 后，这些已 ack 项不应在后续轮次重复出现。

body 是**作者声明**，不是契约面事实源；与 docs/ 或代码冲突时仍以后者为准，但 finding 要明确点出"PR body 声称 X，但与 <文档/代码> 不一致"。

## 维度清单（dimensions 字段填什么）

AI Review 至少覆盖以下 7 个维度。每个维度返回 `ok` / `warn` / `block` / `n/a`：

| 维度 name | 说明 |
|---|---|
| `契约面` | API/数据模型/状态机/权限/UI/事件是否动了；动了是否同步文档 |
| `数据迁移` | prisma/migrations 是否合规、能否回滚、锁表风险 |
| `代码逻辑` | 业务规则正确性、状态转换、边界条件 |
| `测试覆盖` | 改动是否有对应测试、契约校验 L0a/b/c、集成 L1 |
| `安全/权限` | 鉴权、授权、数据范围、SQL 注入、XSS |
| `文档一致性` | docs/modules/ 是否跟代码同步；docs/standards 改动是否单独 PR |
| `高风险路径合规` | 是否触碰 prisma/standards/.gitea/workflows/API 契约面，触碰是否单独 PR |

任何模式都至少返回这 7 个维度。模式特定维度（如 `release-risk` 加 "回滚预案"）追加在后面。

## 渲染模板（runner 输出的 markdown）

```markdown
> 🔬 **DRY RUN（观察期）** · 不阻断 · 跟踪 #171
> 评估准确率达标后会去 dry-run 进 required check。

## $EMOJI AI Review ($MODE) — $VERDICT

**一句话**：$summary

### 评审维度

| 维度 | 结论 | 备注 |
|---|---|---|
| 契约面 | $emoji $status | $note |
| 数据迁移 | $emoji $status | $note |
| ...

### 发现

按严重度分级。零发现写"无"。

- 🚫 **硬阻断**：$count 项
  - **[$category]** ($file:$line) $message
- ⚠️ **风险**：$count 项
- 💡 **建议**：$count 项

### 给作者的 action

$recommended_action

---
*由 ai-review-runner 生成 · 模型 claude-opus-4-7 · 跟踪 [#171](http://43.130.59.228/FFAIWorkspace/workspace/issues/171)*
```

### status emoji 映射

| status | emoji | 含义 |
|---|---|---|
| `ok` | ✓ | 通过 |
| `warn` | ⚠️ | 关注但不阻断 |
| `block` | 🚫 | 触发硬阻断 |
| `n/a` | — | 不涉及该维度 |

## 反例 vs 正例

### 反例（v1，散文化）

```markdown
## ✅ AI Review (hard-rules-block) — pass

纯文档 PR：升级 PR 模板到 v2（上半部分核心叙事 5 段必填 + 下半部分回顾段条件填），段标题统一中文化，新增「为什么现在做 / 破坏性变更 / 权衡与备选 / 度量指标 / 后续跟进」五段...（再两段）。无契约面变更、无迁移、无代码、无测试影响；.agents/ 与 .claude/ 分发副本保持一致（符合 CLAUDE.md 规则）；spec 中段名引用与模板段名一致。无硬规则违反。
```

问题：reviewer 得自己从散文里挑"看了什么、发现什么"。

### 正例（v2，结构化）

```markdown
> 🔬 DRY RUN · 不阻断 · 跟踪 #171

## ✅ AI Review (hard-rules-block) — pass

**一句话**：纯文档 PR，模板段名一致 + skill 引用同步 + 无契约面变更。

### 评审维度

| 维度 | 结论 | 备注 |
|---|---|---|
| 契约面 | — | 不涉及 |
| 数据迁移 | — | 不涉及 |
| 代码逻辑 | — | 纯文档 |
| 测试覆盖 | — | 不涉及 |
| 安全/权限 | — | 不涉及 |
| 文档一致性 | ✓ | 模板段名 vs spec 段落判定表一一对应 |
| 高风险路径合规 | ⚠️ | 触碰 docs/standards/** + .gitea/PR template，PR body 已声明 + 单 PR scope 合理 |

### 发现
- 🚫 硬阻断：0 项
- ⚠️ 风险：0 项
- 💡 建议：0 项

### 给作者的 action

无需修改，已可合并。

---
*由 ai-review-runner 生成 · 模型 claude-opus-4-7 · 跟踪 #171*
```

差异：reviewer 30 秒能读完 verdict + 维度 + action，每段密度高。

## 给未来 AI Review 准确率评估

评论的结构化也服务 issue [#259](http://43.130.59.228/FFAIWorkspace/workspace/issues/259)（AI Review 历史盘点）：

- **dimensions 字段**可被脚本统计："AI 在过去 N 次 review 里平均覆盖几个维度？哪个维度漏看最多？"
- **verdict + findings 数量**可对照 PR merge 后是否真出问题，算准确率 / 召回率
- **recommended_action 文本**可分类（"无需改动" / "改 X" / "block"），分析 AI 建议的有效率

散文 summary 没法做这些统计。结构化后才有 retro 数据基础。
