| name | review-loop |
| description | 提交前的自动 review 迭代环——委派独立 context 的子 agent 编队 review、修、跑验证、复审,迭代到「运行验证通过 + 无高置信 correctness 问题」才放行;2 轮不收敛留痕放行,全程无人在环。由 /commit 自动调用,也可手动跑 |
| disable-model-invocation | false |
对当前工作树的改动跑一轮自动 review 迭代,直到 clean 才放行提交。本 skill 是 review 机制的单一真源 —— 宪法与 /commit 只留触发点与一句判据,细节都在这里。
为什么存在(结论)
写代码的 context 自带先入之见:同一个对话里自审,reviewer「知道」代码想干什么,最容易漏掉的恰是「实际写的和想的不一样」。本 skill 把「独立 context review → 修 → 验证 → 复审 → 迭代到干净」固化成 commit 前的自动环,全程无人在环。
收敛靠「运行验证 + 高置信过滤」,不是靠 reviewer 挑不出为止。
已知局限(诚实声明):reviewer 与写这段 diff 的同为 Claude 模型家族,属同模型自审,对并发 / 多线程 / 难复现改动有已知盲区;独立的是 context 而非模型,升重档只是缓解、不等于消除。需要跨模型第二意见时人工手动引入。
⚠ 要改本 skill 的收敛判据 / 档位表 / 降级链门槛 / 「明确不做」清单之前,先读 references/rationale.md(CC 端绝对路径 $HOME/.claude/skills/review-loop/references/rationale.md)—— 下面每条规则对应哪次翻车、为什么不能再松一格(病根清单、为什么不依赖 CC 内置 /code-review、同模型自审的硬实证),全在那里。不读就改,等于把防线连同它的来历一起改掉。日常跑一轮 loop 不必读。
收敛判据(三要素并闸)
一轮收敛当且仅当三者同时成立。判据是「基础功能没坏 + 有没有高置信真会出错的问题」,不是 P 级数字、更不是「reviewer 还能不能再挑一个」。
| 闸 | 内容 | 备注 |
|---|
| A · 运行验证 | 受影响测试全绿 + happy-path 主流程跑通 | 硬前置,排在 B 之前;纯文档 / 指令规则文件判 N/A(见 6.3) |
| B · 无高置信 correctness finding | 无「附 file:line 证据 + 高置信真会在生产触发」的正确性 / 逻辑 / 安全问题 | 含被标 P2 的;低置信 / 无证据 / pre-existing / pedantic / linter 域一律不阻断 |
| C · 已定前提未被重复质疑 | reviewer 质疑一个人类已拍板的决策不算 bug | 把该前提补进下轮 prompt 继续;不要为了让它闭嘴而推翻人类已定的决策 |
必须是循环而非单次:修复本身可能引入新问题,首轮 review 也未必看全。
Step 1 · 确认有变更
git status。干净无变更 → 打印「无待 review 变更」退出(clean)。
review 的对象是整个工作树的全部改动 —— /commit 调本 skill 时还没人分批 git add,故不区分 staged / unstaged。
Step 2 · 琐碎改动跳过判定
只有真正的用户文档 / 纯机械改动才自动跳过:仅改 docs/ 下文件或 README 的非流程说明段、仅改代码注释 / docstring、单行或极小的机械 fix(笔误、格式、排版)。
这些绝不自动跳过(哪怕是纯文字):
- 指令 / 规则文件 ——
skills/*.md、GLOBAL_AGENTS.md、playbooks/*.md、agents/*.md 与 .claude/ 下的 agent 配置。它们就是开发流程与安全边界本身,跳过 review 等于让门禁在修改自身时失效。agents/*.md 尤其如此:改一个 model 或 effort 就改了整道门禁的强度。
- 配置变更 —— CI 权限、部署目标、认证 / CORS、依赖版本、构建 / 运行时开关,一行就可能改变安全态或线上行为。
有疑则不跳。
Step 3 · 选档
三条成本硬规则
- 成本四维,各有各的钉死处:成本 = 数量 × 模型 × 思考档 × 范围。
- 模型与思考档由
agents/*.md 的 frontmatter 钉死(model + effort),不继承主会话 —— 主会话可以照常跑 xhigh,编队不跟。
- 数量由下面的档位表钉死。
- 范围由委派 prompt 钉死:「只审本次 diff 及其接壤代码(调用点、被调方、紧邻上下文),禁止全库扫描」。
- 永远在独立 context 的子 agent 里跑(唯一例外是 Step 5 的降级)。独立 context 是本机制的首要属性;且主会话直跑会把整轮文件阅读永久写进主对话历史、之后每轮重发。实测:两轮 review 在子 agent 内烧 ~32 万 token,主会话只增加了两份 finding 列表。
- 编队只有两档,不自行加码:不多起 reviewer、不追加角度、不传
model 入参去盖 agent 定义。要调编队规格就改 agents/*.md(那是单一真源),不要在委派时临时加码。
| 档 | 编队(subagent_type × 数量 → 角度) | 触发 |
|---|
| 默认 | review-orchestrator ×1;code-reviewer ×3 → 角度 ①②③ | 一切需要 review 的改动 |
| 重 | review-orchestrator ×1;code-reviewer ×4 → 角度 ①②③④;code-reviewer-deep ×1 → 角度 ⑤ | 命中下列任一复杂特征 |
档位之间的差别是角度数 + 深审模型,不是思考深度 —— 三个类型统一跑 medium,档位表里看不到 effort,是因为它已经被 agent 定义钉死了。
升重档的复杂特征(正是「审浅了会漏真 bug」、也是同模型自审盲区最大的地方):并发 / 多线程 / 异步生命周期(锁、跨线程队列、join / cancel / 优雅停、事件循环迁移);跨进程 / 网络 / 容错(重试 / 幂等 / 部分失败 / 回滚 / 超时 / 降级);状态机 / 竞态(排序假设、陈旧状态、重入、资源开关配对);难以用测试复现,或横跨 3+ 模块的编排装配。拿不准偏向升重档 —— 漏判一个并发 diff 的代价大于多花两个 reviewer。
没有更轻档,也不再往下调思考档:真正琐碎的已在 Step 2 跳过;没跳过的(配置、指令规则文件)每行都重。角度数是检出率的主驱动,不能砍;medium 已是编码场景的底。要动这一条先读 references/rationale.md §4。
Step 4 · 委派独立 review orchestrator
起 1 个 orchestrator 子 agent(subagent_type: review-orchestrator),同步等它返回一份 finding 列表。按你环境里 Agent 工具的实际 schema 填参 —— 该 schema 随 CC 版本漂移,别照抄记忆里的字段清单。
走本档编队时不要传 model 入参。 模型解析顺序是「环境变量 > 单次调用的 model 参数 > agent 定义的 frontmatter」—— 传了就会盖掉定义里钉死的那个(code-reviewer-deep 会被从 opus 打回去)。思考档没有单次调用入参,只认 frontmatter,所以编队档位的唯一真源是 agents/*.md。唯一例外是 Step 5 的第 ② 档 —— 那里用的通用类型没有 frontmatter 可继承,model 反而必须传。
本段是 CC 端路径。 Codex 端没有 Agent 工具、也没有 agents/ 这个概念(install.sh 只把 agents/ 链到 CC 端),故在 Codex 上本步必然走 Step 5 的降级链,且直接落到第 ③ 档(没有 Agent 工具,第 ② 档同样起不来)—— 那是能力缺失,属正当降级,照 Step 5 留痕。
先钉死工作目录(这条排在任务书之前,因为它决定了后面六条审的是不是同一棵树):主会话自己跑 git rev-parse --show-toplevel 取绝对路径,写进委派 prompt,并要求整个编队一切操作都锚定这个根(git -C <根> + 绝对路径读文件)、把它原样逐层转给每个 reviewer。
压缩版理由:不传根,reviewer 会在主 checkout 上审另一个分支的改动并报 clean,失败完全静默;agent 线程的 cwd 每次 bash 调用都会重置,故 cd 只会把这个静默失败换个更隐蔽的形态放回来。这两条的完整推导(含实测)见 references/rationale.md §5 —— 想放宽这条约束前必须先读它。
orchestrator 任务书(六条缺一不可):
- 对象与范围:用
git -C <根> status / git -C <根> diff 拿全部改动;只审 diff 及其接壤代码,禁止全库扫描。
- 编队:按档位表并行起 reviewer 子 agent,每份委派 prompt 都带上那个仓库根;各自独立审、互不通信,各返回 finding 列表(
file:line + 严重度 + 理由 + 证据)。起不了子 agent 时自己按同一角度清单逐角度顺序审,并在结果顶部注明「reviewer 未并行」——不许因此少审一个角度。
- 角度分工:清单在本 skill 目录下的
references/angles.md(CC 端绝对路径 $HOME/.claude/skills/review-loop/references/angles.md)。orchestrator 自己去读那个文件,把对应角度那一节逐字原文转给该 reviewer —— 不改写、不压缩、不合并。清单是「低思考档也不漏审」的机制本身,压缩它等于抵消降档的前提。
- 汇总:跨 reviewer 去重;逐条按 0–100 置信打分 —— 0 = 伪报 / pre-existing;25 = 可能真但未验证;50 = 真但属 nit / 低频;75 = 双查过、很可能实际触发、直接影响功能;100 = 确证且高频。< 80 直接丢弃。75 分上下的存疑项,能用可执行探针(边界值、调用点核对、最小复现)验证的先验证再定分。
- 返回:单一结构化 finding 列表(
file:line、置信分、证据、来源角度);无 finding 则明确说 clean。不修改任何文件。
- 已定设计前提:把清单转传各 reviewer;对这些前提的质疑不算 finding。
必须拿到 finding 才往下走:环境提供同步开关就选同步;默认后台异步的环境等完成通知再继续,不要在结果返回前推进 loop。
「已定设计前提」清单怎么来:子 agent 没有本轮对话的上下文,不告诉它哪些是人类已拍板的决策,它就会去质疑,产出一堆假 finding。首轮委派前先收集:本轮对话里人类明确拍板过的决策,加上 docs/<N>-*/PLAN.md 的「关键设计决策」段与 PROMPT.md 的「已决」段;清单为空就省略那一段。迭代中追加:reviewer 又质疑了某个已定决策,先核对它确属已拍板的(拿不准问用户),确认后追加进下轮 prompt。不要自己替用户否决 reviewer 的意见。
Step 5 · 降级链
优先级:① orchestrator 编队 > ② 主会话当 orchestrator + 通用 agent 编队 > ③ 主会话结构化自审 > 不 review(禁止)。
| 档 | 编队形态 | 相对 ① 丢了什么 | 何时用 |
|---|
| ① | Step 4 的 review-orchestrator + code-reviewer | —— | 默认 |
| ② | 主会话自己当 orchestrator:按档位表并行起 N 个当前可用的通用 agent 类型(general-purpose 等)各审一个角度,主会话只做跨 reviewer 去重、置信打分、探针验证 | 丢由 agent 定义兜住的两条:effort 钉死(Agent 工具无 effort 入参)、以及结构性只读(general-purpose 拿的是全量工具,没有 code-reviewer 那行 disallowedTools)。独立 context 与 model 保住 | agents/*.md 的类型不可用,但 Agent 工具本身能用 |
| ③ | 无编队,主会话逐角度自审 | 独立 context —— 本机制的首要属性 | Agent 工具整个不可用(Codex 端 / 受限环境) |
② 的委派 prompt 必须自带这两条(①档由 agent 定义兜住,通用类型没有):
- 传
model 入参,值照抄 agents/code-reviewer.md 的 frontmatter —— 不传就跟着主会话跑。这是 Step 4「不要传 model」的唯一例外。
- 写死「只读不写:不修改任何文件」 —— 通用类型没有
disallowedTools 兜底,漏了这句 reviewer 真的能改工作树,而 review 阶段改动是静默的。
② 不是将就,在无人值守会话里接近无损:agents/*.md 存在的唯一理由是钉死 model 与 effort,而 effort 那一半防的是「主会话跑 xhigh 时全编队跟着烧」—— 那是本机交互会话的风险,云端 routine 没有 xhigh 主会话。只要上面两条约束真写进了 prompt,② 与 ① 差距很小;③ 丢的却是首要属性,量级完全不同。
云端 agents/ 不可用是常态、不是偶发:CC 在会话启动时快照 agent 类型,而云端 routine 的 agents/ 由会话内的 install.sh 才软链上,来不及。实测(CC 2.1.247):会话中途新建的 agent 定义不会被拾取,调一次 Skill 工具(skills 靠它整体刷新)也不刷新 agents。撞上 Agent type 'review-orchestrator' not found 就直接走 ②,不必每次重判。
降级门槛(三条硬规则,任一不满足就不许降):
- 先真核验一次。 要么实际发起过一次 Agent 调用并失败,要么核对确认 Agent 工具不在当前工具列表里(受限环境下无从发起,核对工具列表就是那次实际核验)—— 两者都是可复述的实际观察。纯推断(「我判断它起不来 / 不该调」)不构成理由。
- 只有能力缺失才算失败。 穷举:Agent 工具不在当前工具列表里、调用直接报错、子 agent 起不来、返回的不是 finding 列表。策略类指令一律不算 —— 「除非用户要求否则别调 Agent / 别用 workflow」这类平台通用系统提示,在用户走
/commit / /review-loop 时条件已被满足(宪法要求 commit 前委派独立 context reviewer,这就是那个 user request),它是策略约束、不是能力缺失。真拿不准 → 停机问人,不许自己挑降级路径:在代价不同、计划未预先授权的方案间替人类选择是方向性决策。
- 一次只降一档,门槛逐档适用。 核验到 ① 失败只授权你走 ②;要再落到 ③,必须对 ② 另做一次核验(通用 agent 类型也起不来)。「① 起不来」永远不构成走 ③ 的理由 —— 两个自动 PR(#141 / #143)正是这么丢掉独立 context 的,而同一根因下的 #125 / #136 / #137 都在 ② 上跑完了。
无人值守例外:云端 routine 等无人在环的会话没有「停机问人」这个选项 —— 判不准时按降级处理,并在留痕里写明「门槛判定存疑」及实际观察到的表现。挂起等于整次运行报废,比一次留痕充分的降级更糟。
降级后角度覆盖不打折:② 由主会话读 references/angles.md、把对应小节逐字原文转给每个 reviewer;③ 由主会话自己逐角度过一遍 diff。两者都用同一套置信 rubric 过滤,并在结果顶部显著标注 —— 两档的标注不通用,别混用:
② ⚠ 本次编队降级为通用 agent 类型 —— 独立 context 未失,model 已按 agents/code-reviewer.md 钉死、只读已由委派 prompt 约束;损失是 effort 未由 frontmatter 钉死,reviewer 继承了主会话思考档。
降级原因:<那次调用失败的实际表现:报错原文>
③ ⚠ 本次为主会话结构化自审(未经独立 context 把关)—— 开发对话的先入之见在场,难复现问题极易漏判。另需告知用户「主 context 会因此增大」。
降级原因:<① 与 ② 各自失败的实际表现:报错原文 / 工具确实不在列表>
留痕必须带证据:降级标注只写结论不算数,要附那次核验的实际表现。落点:commit message(由 /commit 第 7 步写入)与 REVIEW.md;无 docs 目录的轮(如 /quick)写进对话输出。写不出证据,本身就说明不该降级 —— 此时的正解是回去把委派做完,绝不是编一次没发生过的调用来填这一栏。
绝不静默跳过。
Step 6 · 分诊 + 运行验证 + 迭代收敛
先跑闸 A 再看闸 B —— 先确认基础功能没被上一轮修废。
6.1 分诊 finding
orchestrator 已过滤过一层,主会话仍复核证据,按置信 + 是否真会出错走三条出口,不看 P 级数字:
- 高置信 correctness finding → 未收敛,进 6.2 修复。
- 命中「已定设计前提」 → 不算 bug、不阻断、不计入迭代轮数(闸 C)。把该前提补进下轮委派 prompt。
- 低置信 / 无证据 / pre-existing / pedantic / 纯风格 / 可选优化 → 不阻断(顺手能改的可改,不强制、不计入迭代)。
这是防「无限挑刺」的最后一道闸:finding 若无 file:line 证据、或明显属推测式,直接判为不阻断丢弃 —— 不修、不因它继续迭代。
6.2 自动修复(不停下逐条等用户确认)
按问题性质分流:
- 有清晰输入输出契约的代码类问题(业务逻辑 / 纯函数 / 算法 / 并发)→ TDD 正序:先写一个能复现该 bug 的最小测试、跑它、确认它在未修实现下失败(红) → 只改相关代码让它变绿 → 跑确认绿。写不出会红的测试,说明还没真正理解这个 bug,先别动实现。
⚠ 防假绿硬规则:补写的测试必须先在旧(未修)实现上验证为红。旧实现上就绿的测试是假绿,证明不了它抓得住 bug。
- 纯风格 / 机械修复,或 bug 本质无法用测试复现(纯 UI / 视觉,或改的就是指令 / 文档本身)→ 无红测试可写,直接改。
- 修复纪律:只改与本次改动相关的代码,绝不顺手动无关文件。
6.3 运行验证子步(闸 A)—— 每轮修完必跑
修完后、复审前必须真正运行代码确认基础功能没坏(reviewer 只读不跑、发现不了这层):
- 有对应测试 → 必跑、失败阻断:按项目类型跑受影响测试(Python+uv
uv run pytest、Node npm test、Rust cargo test、Go go test ./...;跑受影响子集即可,无法精准定位则跑全量)。失败 → 未收敛,回 6.2(失败本身就是一条高置信 correctness 问题)。
- 被改代码是编排器 / facade 却无 happy-path integration test → 先补一条再放行(判据见
playbooks/python.md §3.7)。补一条最小 fixture 端到端跑主入口、只验「跑通不报错」 —— 抓 missing import / 参数顺序 / self.X 没初始化等装配错误,正是「审废基础功能」的典型形态。
- 无运行时面 → 判 N/A 跳过:纯文档 / 指令规则文件没有可运行的代码单元(但这类仍走闸 B)。项目无任何测试框架且改动非编排器时同样 N/A,但改动含业务逻辑时应按 TDD 补最小测试而非直接跳过。
闸 A 与 lint 的分工:lint 只证明静态无错、证明不了行为未回归;闸 A 真正跑起来验证基础功能。二者不可互替。
6.4 复审收敛
修复 + 运行验证通过后回到 Step 1 重跑(以最新工作树复审,档位沿用本轮选择、不降档;只把委派 prompt 收窄为「核对这几处 finding 是否已消除、修复是否引入新问题」)。三要素并闸全过 → 打印「review clean ✅」放行。
终止保护 —— 2 轮自动上限 + 留痕放行(硬规则):自动修复每跑满 2 轮仍未收敛,或提前出现振荡(同类问题反复)/ 发散(每轮全返新问题)→ 停环、留痕、放行,不停下等用户:
- 剩余未修 finding 全量写入
docs/<N>-*/REVIEW.md 的「未收敛遗留」段(每条:内容、为什么没修完 / 怎么权衡的);无 docs 目录的轮(如 /quick)写进对话输出;
- 告知
/commit 在 message body 追加标注行:Review: 2 轮未收敛,遗留 N 条 finding,见 docs/<N>-*/REVIEW.md;
- 照常放行 commit。
为什么留痕放行而非停下问人:边际取舍终究是人的判断,而「停下问人」会让后台 / 云端会话永久挂起 —— 故把它连同证据前移到 /finish(推导见 references/rationale.md §6)。
留痕:每轮结论追加到 docs/<N>-*/REVIEW.md(报了什么 → 怎么修 → 复审结果)。非 /start 轮(无 docs 目录)跳过留痕。
明确不做
- 不做提交动作:只把 diff review 到 clean,
git commit 由 /commit 完成。
- 不调 CC 内置
/code-review(缘由见 references/rationale.md §2)。人工手输仍可自行使用。
- 不自动引入跨模型第二意见:判定链长、触发率近零、维护面外溢;需要时人工手动引入(硬实证见
references/rationale.md §3)。
- 不做敏感文件隔离:委派的子 agent 与主会话处在同一信任边界 —— 同为本机 CC 进程、能读的文件完全一样、受同一套权限约束,故不加「禁读
.env*」之类的指令(那只在把 diff 交给外部模型进程时才有意义)。真正的保证是「绝密内容不落工作树明文」。
- 不做「每次 stop 都触发」:由「commit 前触发」界定边界,收敛即停。