---
name: loop-review
description: >-
  循环 Code Review（AI 交叉验证循环）——写完代码提交 PR 后，反复「拉取 AI review（Claude Opus 4.6 + GPT-5.5 交叉验证）→ 逐条判断哪些建议值得改 → 改 → push 触发新一轮 review → 直到某轮不再有新问题」。触发场景包括但不限于：
  「循环review」「循环审查」「循环评审」「循环 Review」「loop review」「Loop Review」「review循环」「review 循环」。
  「循环review流程」「走一下循环review」「走循环review」「循环review一下」「循环处理review」「循环处理一下review」。
  「循环到没问题」「review到没问题」「review到干净」「反复review」「重复review」「一直review到干净」「review迭代」「交叉验证循环」。
  「再来一轮review」「下一轮review」「review下一轮」「再review一轮」。
  当用户写完代码提交 PR 后，主动问「要不要走循环 review」时也可触发。
  创建完 PR 后按 PR 闭环流程（AGENTS.base.md「PR 核心要求」章节）**自动触发**，无需用户再开口。
---

# 循环 Code Review（AI 交叉验证循环）

⚠️ **本 Skill 已触发。第一句话必须输出：「🔧 已触发 `loop-review`，开始循环 review 流程（拉取 AI 交叉验证 → 逐条判断 → 修改 → 再 review → 直到不再有新问题）」然后严格按照以下步骤执行，不得跳过。**

让 AI review bot（Claude Opus 4.6 + GPT-5.5 交叉验证，由 `github-actions[bot]` 发布）循环审查，把**每一轮新出现且值得修的真问题都修掉**，直到某一轮不再冒出值得修的新问题即结束。

## 为什么 bot 修完一轮还会冒出"新问题"（必须理解）

bot **每一轮都把整份 PR diff 从头到尾全新读一遍**，不是只看这次改了什么。所以"新问题"永远是三个来源：

1. **上一轮漏报的旧问题（最主要）**：同一批代码，第一轮注意力集中在最明显的 bug 上，修完之后它重新通读，才注意到之前没看的角落。不是问题"新增"了，是上一轮没看到。人审代码也一样——第一遍看主流程，第二遍才抠边界。
2. **修复本身引入的回归**：为修问题 A 改动共享逻辑，波及问题 B。这是真实新问题，该修。
3. **新代码自带新问题**：为新需求写的函数/逻辑，bot 会 review 新代码，新代码自然有新毛病。

**关键认识**：bot 的"问题挖掘能力"是无限的，总能往下挖出新层次。**所以「追着严重度清零」是永远停不下来的循环**（实测 PR #26 走了 10 轮、第 7-8 轮曾短暂干净、第 9 轮又冒出新 Critical）。循环要收敛，靠的不是"问题清空了"，而是——**每一轮冒出的新问题我们都逐条判断过：值得修的修掉、不值得修/口味/过度设计的 Won't fix 切断**。剩下全是判断过"不值得修"的，bot 再提就用 Won't fix 挡住。

## 工作原理

AI review bot 在**每次 push 到 PR 时自动触发新一轮 review**。因此循环流程是：

```
拉取最新 review → 解析两个模型的建议 → 每条读真实代码判断（值得修 / 不值得修）
→ 值得修的改 → 不值得修的 Won't fix 回复 → push（触发新 review）→ 等待新 review 到达
→ 若本轮没有任何值得修的新问题 → 结束；否则进入下一轮
```

## 当前 PR

!`gh pr view --json number,title,state -q '"PR #\(.number): \(.title) (\(.state))"' 2>/dev/null`

## 核心流程

### 0. 记录起始状态

```bash
REPO=$(gh repo view --json nameWithOwner -q '.nameWithOwner')
PR=$(gh pr view --json number -q '.number')
HEAD=$(git rev-parse HEAD)
```

记录本轮起始 `HEAD`，用于判断「新 review 是否已针对最新 push」。

### 1. 拉取最新一轮 AI review

```bash
gh api repos/$REPO/pulls/$PR/reviews?per_page=100 --jq '
  [.[] | select(.user.login == "github-actions[bot]") |
   {id, commit_id, submitted_at, body}]
'
```

review body 结构（已实测确认）：

- 开头标注「审查 commit: \`xxx\`」——该轮 review 对应的提交
- `## 🤖 Claude Opus 4.6 Code Review`：Opus 的审查结论
- `## 🤖 GPT-5.5 Code Review（交叉验证）`：GPT-5.5 的交叉验证结论
- 每个模型内部按严重度分节：`## 🔴 Critical` / `## 🟡 Warning` / `## 🔵 Suggestion`

**确认「最新一轮 review 是否针对当前 HEAD」**：调用独立脚本 `check-review.sh` 获取最新 review 的完整 commit_id（脚本处理完整 SHA、按 submitted_at 排序取末条、分页合并），再与当前 HEAD 完整 SHA 比较：

```bash
# 取最新一条对应当前 HEAD 的 review（命中则输出 YES，否则为空）
CUR=$(git rev-parse HEAD)
LATEST=$(bash "$(git rev-parse --show-toplevel)/.claude/skills/loop-review/scripts/check-review.sh" "$REPO" "$PR")
if [ -n "$LATEST" ] && [ "$LATEST" = "$CUR" ]; then echo "YES"; else echo "NO"; fi
```

⚠️ `git rev-parse HEAD` 必须用完整 40 位 SHA：`review.commit_id` 是完整 SHA，短 SHA 比较永远不匹配，会误判「review 未到」而空等。

### 2. 汇总建议并逐条判断（核心）

解析两个模型的所有问题，**两模型报同一问题时合并为一条**（不要重复改两遍）。

⚠️ **严重度只是 bot 的标注，不是「该不该修」的依据**。真正决定改不改的是你读完代码后的判断。逐条按下面的决策框架：

| 判断 | 说明 | 处理 |
|------|------|------|
| **值得修** | 读代码确认是真实 bug / 真实风险，或明显值得改 | ✅ 改 |
| **不值得修** | 过度设计 / 口味偏好 / 重构建议 / 收益小于风险 | ⛔ 不改 + Won't fix 回复 |
| **已处理过** | 上一轮已修过但 bot 重提 | ⛔ 不改 + 回复「已修复于 <hash>」 |
| **误报** | 读代码确认 review 描述不属实 | ⛔ 不改 + 回复「误报：<为什么>」 |

**判断依据（每条必须读真实代码，禁止凭 review 文字盲改）**：

- 这是否是真实 bug / 真实风险？（读代码验证，不轻信 review 描述，也不轻信严重度标签）
- 该建议是否符合本项目规则（AGENTS.base.md / AGENTS.private.md）？
- 改动风险多大？会不会超出该建议本身的范围？（为了修一条问题去大改共享逻辑，本身可能引入新风险）
- 是"必须修"还是"有则更好"？后者更接近口味，可拒绝。
- 两模型是否一致指出？一致 → 优先级更高、基本可信。

**每轮必须追问一句**：这条问题是不是上一轮已经判断过/处理过的？是 → 直接 Won't fix / 已修复，不重复改。**这一步是循环能收敛的关键。**

输出判断表：

```
| # | 来源 | 严重度 | 建议摘要 | 判断 | 理由 |
|---|------|--------|---------|------|------|
| 1 | Opus+GPT | 🔴 | 并行部署失败被静默忽略 | 改 | 后台进程不受 set -e 管控，属实 |
| 2 | GPT | 🟡 | test/prod 共用缓存 TAG | 改 | 有缓存污染风险 |
| 3 | Opus | 🔵 | 提取重复代码 | 不改 | 仅出现两次，封装过度 |
| 4 | Opus | 🔴 | 某竞态条件 | 不改 | 上一轮已修复于 3fa21c0，bot 重提 |
```

### 3. 应用修复

- 判定「值得修」的（无论严重度）→ 每个修复单独 commit，commit 信息用中文，正文引用对应 review 来源
- 纯建议级且判定值得改的 → 合并为一个 commit
- 判定「不值得修 / 已处理 / 误报」的 → 按步骤 4 回复，切断 bot 下轮重提
- ⚠️ **所有修复必须直接 commit 在当前 PR 对应的 feature 分支上**（`git push origin <feature 分支>`），并推送到远程。禁止把 review 修复只放在其他地方（例如直接堆到 test 分支、或修改后不 commit）。**宗旨：feature 分支（PR）与 test 分支两条线保持同步，修复代码同时存在于 feature 分支和 test 分支，缺一不可。**

### 4. 对「不改」的建议回复（防重复循环，关键）

```bash
# 值得改的已修复
gh pr comment $PR --body "Fixed in <hash>。针对「<建议摘要>」。"

# 不值得修 / 口味
gh pr comment $PR --body "Won't fix: <理由>。针对「<建议摘要>」。"

# 误报
gh pr comment $PR --body "误报：<读代码后为什么它不是问题>。针对「<建议摘要>」。"
```

已修复的问题若 review 附了 inline comment，用 `gh api .../pulls/$PR/comments` 的 `in_reply_to` 回复。

⚠️ **改动的每条 review 建议都必须有落点**：要么修复 commit，要么 Won't fix/误报回复。**不允许静默跳过**——不回复的话 bot 下一轮还会原样重提，循环就真的无限了。

### 5. push 并等待下一轮 review

```bash
git push origin <当前分支>
```

- ⚠️ **禁止使用 `[skip ci]` / `[skip review]` 等关键词**——会跳过 bot 审查，循环就断了。
- push 后 bot 需要一段时间才出新一轮 review（实测几分钟到几十分钟不等），用轮询等待，不要干等：

```bash
CUR=$(git rev-parse HEAD)
for i in $(seq 1 45); do
  R=$(bash "$(git rev-parse --show-toplevel)/.claude/skills/loop-review/scripts/check-review.sh" "$REPO" "$PR")
  [ -n "$R" ] && [ "$R" = "$CUR" ] && echo "NEW_REVIEW_READY" && break
  sleep 20
done
```

⚠️ 等待判断也用完整 SHA + `check-review.sh`（步骤 1 的同一套逻辑），避免 `gh api --jq --arg` 写法失败或短 SHA 误判导致空等 15 分钟。

- ⚠️ **遵守心跳约定**：等待期间超过 1 分钟无输出，主动告知「正在等 bot 审查」。
- 轮询约 15 分钟（45 次 × 20s）仍没出新 review → 停止等待，向用户报告当前状态并询问是否继续等。

### 6. 判定本轮结果

拉取针对当前 `HEAD` 的最新 review，逐条判断（回到步骤 2 的决策框架）后：

- **本轮没有任何「值得修」的新问题**（要么 bot 没提新的，要么新问题全是已处理/口味/误报，全部有回复落点）→ 循环结束 ✅，进入步骤 7 收尾
- **本轮有「值得修」的新问题** → 已修复并回复，回到步骤 5 进入下一轮
- **达到最大轮数（默认 5 轮；用户明确要求深度循环时最多 8 轮）仍有值得修的新问题** → 停止循环，把剩余问题逐条列给用户，由用户决定（继续 / 放过 / 人工处理），禁止无限循环

⚠️ 每轮结束向用户汇报：本轮发现什么、改了什么、拒绝了什么、剩什么。

### 7. 收尾汇报

循环结束或达到轮数上限后，输出汇总报告：

```
循环 review 完成，共 X 轮：
- 第 1 轮：发现 4 个问题（1 Critical / 2 Warning / 1 Suggestion），修复 3，Won't fix 1
- 第 2 轮：发现 2 个新问题（1 Critical / 1 Warning），修复 2，另收到 2 条重复重提 → 已回复已修复
- 第 3 轮：无值得修的新问题，循环结束 ✅

最终状态：真实问题已全部修复。bot 仍挂着 N 条建议（口味/过度设计），均已 Won't fix 回复并说明理由。
```

## 循环终止条件（重要）

| 条件 | 行为 |
|------|------|
| 本轮没有任何「值得修」的新问题 | ✅ 结束（注意：**不是**「严重度清零」——bot 总会挂着几条我们判断过不值得修的） |
| 达到最大轮数（默认 5 轮；用户明确要求深度循环时最多 8 轮） | ⛔ 停止，剩余问题交用户决定 |
| 等待新 review 超时（默认 15 分钟） | ⏸ 停止等待，询问用户 |
| 连续两轮 review 内容完全相同（bot 疑似卡死） | ⛔ 停止，向用户报告 |

## ⚠️ 铁律

- ⚠️ **每条建议必须读真实代码再判断，禁止盲从 review 文字、也禁止盲从严重度标签**。review 是 LLM 生成的，可能误报，严重度只是它的标注。
- ⚠️ **循环收敛靠「值得修的都修完 + 不值得修的 Won't fix 切断」**，不是靠「严重度清零」。禁止用「无 Critical/Warning」当结束标准。
- ⚠️ **每条 review 建议必须有落点**（修复 commit / Won't fix / 误报回复），禁止静默跳过——不回复 bot 下轮必重提，循环无限。
- ⚠️ **每轮先确认哪些是上一轮已处理过的**，一律不重复改，直接回复「已修复于 <hash>」。
- ⚠️ **禁止使用 `[skip ci]` / `[skip review]`**——循环依赖「每次 push 触发 review」，跳过就断了。
- ⚠️ **必须设最大轮数（默认 5；用户明确要求深度循环时最多 8）和等待超时（默认 15 分钟）**，禁止无限循环 / 无限等待。
- ⚠️ **已修复的功能性问题单独 commit**，禁止与大量建议级改动混在一起，保证每轮 push 的 diff 可读。
- ⚠️ **每条 review 修复都必须同 commit 回 PR 对应的 feature 分支并推送远程**，不得只推到 test 分支或只放在工作区不 commit。feature 分支与 test 分支两条线必须同步，缺一不可（详见步骤 3）。
- ⚠️ **等待 bot 审查期间遵守心跳约定**：超过 1 分钟无输出主动说明在等什么。
- ⚠️ **commit 用中文、禁止 `git push --force`、commit 信息引用对应 review 来源**。
- ⚠️ **两模型报同一问题合并为一条处理**，不要重复改两遍。
- ⚠️ **涉及生产部署、大范围改动等高风险修改，即使判定「要改」也应先向用户说明再执行**（与全自动模式互补，不冲突）。

## 参考

- 拉取评论 / 分类 / 回复 thread 的细节复用 `github-pr-review` skill（`skills/github-pr-review/SKILL.md`）
- 严重度识别补充规则：`skills/github-pr-review/references/severity_guide.md`
