agentleFS
Luo-rootLuo-root/deep-code-review/SKILL.md

deep-code-review

Skill2 starsChanged 14 days agoMITView on GitHub

Evidence-driven code review for diffs, branches, PRs/MRs, files, modules, functions or configs. 快模式 (merge decision): report only defects this change introduces, each with a trigger and impact, compact output. 全量模式 (design review): a "可以合 / 改完再合 / 方向先定" verdict over seven dimensions (design, API contracts, implementation, docs, hardcoding, duplication, over-engineering) plus error paths, concurrency, security, compatibility, observability, performance, test blind spots; a refutation pass (assume it's wrong), a scale/complexity scan, falsification gates; then what must be fixed, what is worth fixing, what needs a decision, what was verified. Use when the user says 代码审查 / code review / "review 一下" / "帮我看看这个 PR 有没有问题" / "这个 PR 能不能合" / "合并前自审" / "按我的标准审", or asks for the design to be questioned: "按七维审" / "审设计" / "审契约" / "审重复" / "审过度设计" / "给这个模块做体检" / "看看这套东西还有哪些坑" / "这个算法有没有问题" / "扛不扛得住这个数据量" / "这套设计是不是不合理". Do NOT use for explaining code or debugging a failure with a known cause.

13 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Files
SKILL.md

Deep Code Review

审查的目标不是证明会挑错,而是在合并前让作者看到「他原本看不到的那一件事」。

三条底层原则:

  1. 先理解意图,再判断实现。 不知道意图时先声明假设,让用户能纠正范围。
  2. 每条结论落到一个具体触发路径。 落不到的按闸门表降级或删掉(多数类型直接删);证据强度决定措辞强度。
  3. 只报作者会因此改代码的问题。 风格偏好、接口口味、「我会换个写法」不占篇幅。

本次审查要读哪些文件

文件什么时候读读它是为了
SKILL.md必读流程、分级、输出契约
references/dimensions.md必读七维细则与每个维度的「不算」——误报边界只在这里,跳过它就会把「不算」当「算」;快模式至少读第 2 维与第 4 维的成立条件与「不算」、第 5 维的「先分清是哪一类」(协议固定值 vs 业务政策值)、第 6 维的「成立条件(三条)」、第 7 维的「成立条件」与「规模与复杂度」一节——快模式要跑的 playbook 必查项里有归这几维的条目,不读它们的「不算」就会把「不算」当「算」;快模式若要报第 1 维(设计)或第 3 维(实现)的结论,另读这两维的成立条件与「不算」——playbook 里有 6 条必查项标着「实现」(第 3 维),不读它的「不算」就会把「不算」当「算」
references/playbooks.md命中改动类型时读命中的那几节(两档都读)该类型高频漏掉的专属缺陷
references/verification.md每条结论定级前读闸门表、证据阶梯、常见误判
references/regression-review.md走快模式时必读合并判定的校准细则与输出形态
references/output-template.md只在写报告前读骨架与措辞

不确定某条属于哪个 playbook:先读 references/playbooks.md 顶部的命中判据表(按载体认类型),再读命中的那几节——命中几节跑几节,必查项取并集,不要整份照单全扫。

两档模式

先判定本次要哪一档,再动手。判据是用户在问什么,不是改动有多大。

快模式(回归判定)全量模式(设计体检)
用户在问「这个 PR 有没有回归」「能不能合」「改坏了吗」「这套设计怎么样」「还有哪些坑」「按七维审」「给这个模块做体检」「按我的标准审」「要覆盖表」
做什么第一遍读全 + 类型 playbook 必查项 + 证伪第一遍 + 反驳 + 类型 playbook + 七维 + 规模扫描 + 证伪
只报什么本次引入、能落到触发路径或规模/频率证据的问题(含读全时撞见的七维类、规模/性能类、扩展维度类)七维全部 + 扩展维度 + 类型专属项 + 规模扫描
既有问题只在被本次直接放大、或本次功能直接依赖它时提标 [既有],与本次无关的不报(无基线时标 [现状])
输出紧凑列表,每条 = 标题 + 3 行(触发 / 影响 / 修法);P2/P3/「需确认」用「问题 / 证据 / 收益 / 修法」4 行,待拍板用「现状 / 证据 / 路线 A / 路线 B / 建议 / 要你定」;零发现一句话七段式 + 维度覆盖表

两档共用第一遍与证伪,不重复劳动。类型 playbook 的必查项两档都跑——它列的正是「本次引入的缺陷」(成功判定、幂等、退出码、默认值变更),是合并判定最该拦的一类;七维与扩展维度只在全量模式跑——指的是不单独跑那一遍扫描,不是「撞见的不报」:它们找的是设计与算法层面的问题,不是「这次改动有没有坏」,但读全时一眼撞见的照报(类目清单见下)。规模扫描不在快模式里单独跑,但读全时撞见的规模/性能类问题照报(闸门照旧,形态见 references/regression-review.md):快模式在第一遍读全时若一眼撞见七维类、规模/性能类、扩展维度类(安全、并发、兼容性、可观测性等)问题,照报,但仍按既有问题规则处理:与本次无关的不报,被本次直接放大、或本次功能直接依赖它的标 [既有]。「规模/性能类」= 覆盖表里的「规模与复杂度」与「性能」两行(两者的判据见 references/dimensions.md,分开报、不要重复)。拿不准就走全量——全量的产出可以裁成快模式,快模式的产出补不回全量。

快模式的校准细则(范围怎么定、只报什么、输出长什么样、什么时候该升级到全量)见 references/regression-review.md。

坐标系

先读,后问。只有当场无法判断、且换一种理解会改变审查结论时,才向用户确认。

  • 范围:文件 / 函数 / commit / 分支 / PR / 工作区改动。默认「工作区相对 HEAD 的 staged + unstaged + untracked」。审查对象是代码与配置(源码、配置、脚本、基础设施文件);没有外部可验证判据的纯文本产物不作为审查对象(散文类需求文档、prompt)——它们没有对错可判,审出来的是品味。但被 lint、类型检查、实际运行行为这类外部判据覆盖的契约类文档(配置格式、接口契约、本仓库的 references/*.md)仍可审——判据是「有没有外部可验证的对错」,不是「是不是散文」。代码里的注释、README 段落仍是第 4 维的检查对象。触发词里的「审契约」指 API / 方法契约,不是契约文档。
  • 基线:分支或 PR 用 merge-base,不拿整棵树当 diff。基线取法(细则与措辞以本节为准,references/regression-review.md 引用这一份):PR 用它的 base 分支;分支用用户指定的分支,用户没指定就用仓库的默认分支——依次取 git symbolic-ref --short refs/remotes/origin/HEAD → git remote show origin 的 HEAD branch → main / master 里实际存在的那个,三样都取不到才算「找不到默认分支」;commit 级范围用该 commit 的父提交(merge commit 用第一父);工作区改动相对 HEAD(staged + unstaged + untracked)。两档都要在报告开头写明范围与基线。
  • 基线取到了、但它与当前 HEAD 的 diff 为空时(分支已经合入 base):没有可审的改动。快模式按 references/regression-review.md 的零发现形态回答;全量模式按零发现模式给:范围行写「范围:<PR> 相对 <base> 的 0 个文件(merge-base xxxxxxx)」,覆盖表 15 行每行写「未扫(本次改动为空)」,结论行写「可以合」,最后那 1~2 句说明写「无可审改动:基线与 HEAD 无差异」(零发现模式没有第 7 段)。两档都不得退化成全仓体检——用户问的是这个 PR,不是这个目录。
  • 意图:PR 正文、commit message、关联 issue、测试名、文档。都没有时按最可能的意图理解,并在开头用一句话声明。
  • 项目约定:AGENTS.md、CONTRIBUTING、相邻同类实现。设计是否合理是相对本项目判断的,不是相对通用品味。

没有基线时(模块体检、目录审查、函数级审查):

  • 范围 = 用户指的那片路径,基线 = 无。范围行按下面「范围行按基线类型分形态」那张表的「无基线」行写,不要编一个 merge-base 出来。
  • 用户问的是分支 / PR 而基线取不到时(默认分支取不到、用户给的基线对象不存在等):不要静默变成全仓体检——范围 = 该分支 tip 的整棵树(git ls-tree -r --name-only <分支>),范围行按下面「范围行按基线类型分形态」那张表的「分支 / PR(基线取不到)」行写,并在「需要你拍板」里写一条「基线未确定:建议用 <候选> 作为基线重跑」(按待拍板项计数,不挡合并)。
  • 「本次改动」在这个场景不存在,因此第 5 步的既有问题规则不适用于被审路径内的代码——按新问题报,不标 [既有]。被审路径之外引用到的代码(依赖的公共库、上游服务)算既有问题:不计入 P0–P3 计数,不进「必须修/建议修/需要你拍板」——写进「本次没覆盖」(如「外部库 X 有一个已知问题,未在本次范围内」);与本次判断无关的可以不写。
  • 第 1 步没有 hunk 可读时,改成「把范围内的每个公开入口读完整函数」;「宣称覆盖的路径 vs 实际改到的路径」这组对照换成「入口声明的能力 vs 实现里真正做到的能力」。
  • 没有「改动」时,第 3 步那套以「改动」为前提的规则要换输入:playbook 按被审路径内出现的载体判定命中(references/playbooks.md 顶部的载体表照用);扩展维度按路径内是否存在该面取舍,没有该面就写「未扫(路径内无此面)」;规模自适应那套按「改动大小」分支的规则在这里没有输入——路径内有多少代码就按多少扫,不按「改动」切分;但路径本身很大时仍适用失败处理里的「范围太大 → 按风险排序抽查」——按风险抽查,报告里写清抽样比例与未覆盖部分。
  • 「兼容性」这一维在无基线体检里不是空转:拿 git 历史里最近一次改过同名契约的提交(或仓库发布的上一版产物)当「旧」的比对面——旧配置键、旧 CLI 参数、旧数据格式、默认值变更都在这一维里查。比对面不得是被审代码的同一份快照:若「最近一次改过同名契约的提交」的内容与被审内容完全一致(即被审的就是 HEAD 那个提交的内容),继续往前找上一个改过同名契约的提交;若被审内容含未提交改动,HEAD 本身即可作为比对面(它就是这份代码的上一版);仍找不到 → 写「未扫(无可比历史)」,不要写「无发现」——比对面与被审内容是同一份快照时差异恒为空,等于宣称「比过了、没问题」,而实际什么都没比。查不到比对面(如只有一个提交、没有可比的历史)同样写「未扫(无可比历史)」,不要写「无发现」——前者是「没得比」,后者是「比过了、没问题」,对读者的含义完全不同。

执行顺序

1. 第一遍:读全,不下结论

  • 每个改动 hunk 读完整函数,不只看 diff 的 ±3 行上下文。没有基线时(见坐标系)改成:把范围内每个公开入口读完整函数。
  • 改动符号的调用方、被调方、相关测试、相关文档都读一遍。
  • 画出数据流:入口 → 变换 → 输出 → 错误 → 清理。
  • 记下改动依赖的不变量(「这里假设 key 唯一」「调用方已持锁」)。

这一遍只建立模型。此时冒出的「感觉不对」先记在草稿里,不进报告。

读完后用一句话复述:这个改动宣称做到什么? 然后把「宣称覆盖的路径」与「实际改到的路径」各列一遍,差值就是最值得查的地方——dry-run 是否真的不写、重放是否真的复现、全量是否真的全量、只读是否真的只读。这组对照要落到纸上:在结论段之后写一行固定句式「宣称:<一句话> / 实际:<一句话>」,两档都要写(快模式写在开头三行之后、条目列表之前;全量模式写在结论段之后),它不计入结论段的 3 行。没有落差时这一行写成「宣称与实际一致」——写不出来说明第一步没做扎实。

2. 反驳:先假设这个设计可能是错的(仅全量模式)

第一遍建立的是「作者想做什么」。这一步反过来:先假设设计本身可能是错的,把它的前提摊开逐条质问。

对每个公开概念(导出的类型、接口、入口、操作、状态名)问四句:

  1. 它有几处实现? 同一个业务动作在系统里有几种做法、几个名字?把候选列出来。只有一处 → 这个概念可能是凭空长出来的;有多处 → 它们的行为可能不一致。
  2. 同一条业务动作,语义在所有路径上一致吗? 正常路径、异常路径、恢复路径上,同一个动作的行为是否相同?同一个名字在两条路径上做出不同结果,就是语义分裂。
  3. 统一调用方能用同一个类型/调用吗? 抽象层声明的参数与返回类型,和实现层、后端层实际用的类型,是同一个形状吗?换一个后端要不要连带改调用代码?形状不一致 → 统一调用方无法复用。
  4. 它凭什么存在、职责归谁? 它的职责有没有被另一个抽象也认领了?两个抽象的边界说不清,就是职责重叠。

每条质问都要落到「谁会因为这个不一致而写错、改错或被绊住」。指不出具体后果的,删掉——反驳不是找茬,是找出会咬人的裂缝。

这一遍的产出多半是设计取舍,多半该由人来定:概念该合并还是该拆、接口该统一成什么形状。四问各自的判据见 references/dimensions.md:「有几处实现」→ 第 7 维(只有一个实现、没有第二个使用者);「路径语义是否一致」「职责归谁」→ 第 6 维(语义分裂 / 职责重叠);「跨层形状」→ 第 2 维。但归到哪一栏由维度判据定,不由「它出自反驳这一步」定——下面这条规则优先。

反驳与七维会问出同一个事实(四问各自落在哪一维,见上一段)。同一根因只留一条,按维度判定的级别归并:维度判成 P0/P1 就进「必须修」,判成 P2/P3 就进「建议修」,维度判为「需拍板」才进「需要你拍板」。不要因为它出自反驳就一律降成拍板项——那会让同一种问题在两轮报告里落到不同栏,作者没法比较。

3. 第二遍:先按类型扫,再按维度扫

类型 playbook 的必查项两档都跑(快模式跑到这里为止);七维与扩展维度只在全量模式跑(不单独跑那一遍扫描;读全时撞见的仍照报,类目清单见上面「两档模式」一节)。规模扫描不在快模式里单独跑,但读全时撞见的规模/性能类问题照报(形态见 references/regression-review.md)。

顺序固定:先认改动类型(按 references/playbooks.md 的载体判据)→ 跑命中的那几节 playbook 的必查项 → 七维做通用面 → 规模扫描 → 扩展维度只做横向补充。

理由:playbook 覆盖的是该类型的高频专属缺陷(七维里没有),先跑它性价比最高;七维负责通用面;规模扫描查算法复杂度,七维的成立条件(「输入 → 错误结果」)管不到它,必须单独跑;扩展维度中与 playbook 重复的项,扫完 playbook 即视为已覆盖,不再重复扫。

按下表把七个维度各扫一遍。每个维度都要留下结论,包括「无发现」——未声明的「没问题」和不存在的「没问题」无法区分。

#维度要回答的问题
1设计:机制组合这个能力能不能由现有机制(中间件/切面/option/注册表/钩子/已有函数)自然组合出来?新增抽象是否替组合做了本不该它做的决定?
2方法契约参数是否遗漏?缺省值是否有歧义(没传 vs 传了零值)?调用方会不会传错、漏传,或被迫绕开函数自己拼?抽象层与实现层、后端层用的类型是同一个形状吗?
3实现在什么输入、状态或并发交错下会算错、漏处理、泄漏、死锁?
4文档读者照着注释/README/错误信息会不会做错事?
5硬编码哪个值被写死了,而它应当可变?分档判据见 references/dimensions.md 第 5 维。
6重复同一行为是否存在第二个真相源?同一个概念是否分裂成了几条行为不一致的路径?两个抽象是不是在认领同一段职责?
7过度设计删掉它,今天会有什么损失?

各维的成立条件与「不算」边界只在 references/dimensions.md 里定,不要在这张表里复述——同一判据写两份必然漂移,漂移的那一份会让这一维被系统性漏报。答不上成立条件就降级或删除。

这张速查表在 README.md 里也有一份(读者多从那边进来),两份必须逐行相同——行的顺序与问法都不能分叉;机器会逐行校验,改一处就要同时改另一处。

七维的扫描动作与误报边界见 references/dimensions.md(速查表以该文件的细则为准)。命中特定改动类型时,再追加 references/playbooks.md 里对应的必查项(批处理/Job、数据重放/迁移、API/库、并发/服务、配置/部署)——这些类型的专属缺陷不在七维里,漏掉一次就是整类问题。

规模扫描(七维之外单独跑一遍):对每条含循环、递归、聚合、查找、拷贝的路径问三句——

  1. 输入规模 N 是什么?这一步随 N 怎么长?
  2. 有没有重复访问同一个状态、每层拷贝完整结果、每次重新全量扫描?
  3. 爆炸点在哪——N 到多少会出事?

规模问题不需要有错误结果才算数:它没有「输入 → 错误结果」这种触发路径,它的证据是规模关系,实测数字是加分项不是准入门槛。成立与降级一律按 references/verification.md 闸门表「规模与复杂度」行处置:规模关系清楚就成立;拿不出 N 的取值 → 降为 P2 标「需确认:X」;N 与规模关系都清楚但拿不出实测数字 → 降为 P2 标「需确认:X(缺实测)」;只有三条都答不上来才删。本文件不另立判据——同一判据写两份必然漂移,漂移的那一份会让同一条候选在「删」与「留成计数的 P2」之间摇摆,直接改掉结论行的数字。细则见 references/dimensions.md 的「规模与复杂度」。

扩展维度在同一遍里按改动性质取舍,没扫的在报告里明说:错误路径与部分失败、并发与生命周期、安全、兼容性、可观测性、性能、测试盲区。测试只在「断言了错误行为」「改动会让既有测试静默失效」「关键分支完全没有覆盖且该分支高风险」时报告,不泛化建议补测试(『关键分支』与『高风险』的口径见 references/dimensions.md 的测试盲区节,本文件不复述)。「性能」判的是工程坏味道(N+1、循环内 IO、无界缓存);算法层面的复杂度爆炸归「规模扫描」,两者分开报,不要重复。

两个桶都成立时归哪一栏:判据见 references/dimensions.md 的「性能」一节(含 N+1 的兜底句),本文件不复述——同一判据写两份必然漂移,漂移的那一份会让真实缺陷被闸门删掉。同一根因只报一次,但覆盖表里两行都要有交代——不能一行写「无发现」(你强制跑了),也不能写「未扫」(你也强制跑了)。

规模自适应:单文件小改动七维全扫、扩展维度只扫高相关的两项;跨模块大改动先按风险排序分批读,报告里写清抽样与未覆盖部分。规模扫描不按「改动大小」取舍——只要代码里有循环、递归、聚合、查找或整块拷贝,就要跑。

4. 证伪

对每条候选结论回答 references/verification.md 的闸门问题。答不上来就按闸门表降级或删除,不要用措辞掩盖不确定。

降级档位只以 references/verification.md 的闸门表为准,这里不重复——同一判据写两份必然漂移,漂移的那一份会让同一条候选在「删」与「留成计数的 P2」之间摇摆,直接改掉结论行的数字。

5. 分级与排序

  • P0:数据错误或丢失、安全漏洞、线上不可用、不可回滚。合并前必须修。
  • P1:在可预测的真实输入/调用下出错,或设计会把后续使用者引向错误。应该修。
  • P2:当前行为正确,但会持续制造维护成本(第二个真相源、脆弱硬编码、不导致读者做错事的文档瑕疵)。建议修。
  • P3:可选改进;合并前可不做,但要写清收益。

文档类结论的分界(同一份文档只报一次,不按两级各报一次):读者会因此做错事(传错参数、以为幂等、以为线程安全、照着示例编译失败、照着文档操作直接失败)→ P1;只是措辞不准、示例陈旧、读者不会因此做错 → P2。判据见 references/dimensions.md 第 4 维的「成立条件」。

判断依据是「会不会真的咬人」,不是「改起来难不难」。同一个根因只报一次,把所有受影响位置列在同一节。

规模与复杂度问题按「N 到多少会出事」定级:在项目现实的规模下就会拖垮(延迟、内存、吞吐)→ P1;只在远超当前规模时才爆 → P2 或 P3。「拖垮」按可对照的锚点判,依次取第一个能取到的:① 项目约定里的口径(AGENTS.md、CONTRIBUTING、配置或监控里的超时 / p99 / 内存上限);② 同一仓库同类路径的实测耗时——超过它 10 倍,或超过 100ms 量级,或内存超过该请求原有占用的一倍;③ 前两项都取不到时,以 100ms 量级或内存翻倍为底线。采用了哪条锚点写进证据行——不写锚点的 P1 不成立。说不出 N 的取值就别往上定级:按闸门表降为 P2 并标「需确认:X」(闸门表口径)——它仍然计入结论行的 P2。

语义分裂按「谁会被绊住」定级:同一件事走两条路径结果不同,调用方无法预判 → P1。两条路径各自自洽、只是名字像同一个东西 → 不成立,不报(判据见 references/dimensions.md 第 6 维)。

既有问题(基线之前就存在)的处理,全文只有这一条规则:

这条只在有基线时适用。 没有基线的审查(模块体检、目录审查、函数级审查)里不存在「本次改动」,被审路径上的问题按新问题报,不标 [既有];被审路径之外引用到的代码才算既有问题。

  • 与本次改动无关 → 不报。
  • 被本次改动直接放大、或本次功能直接依赖它 → 报,但必须标 [既有],且不进「必须修」的 P0/P1:降为 P2,或进「需要你拍板」。
  • 分不清是不是既有问题 → 用 git blame 或与 base 对比确认;确认不了就降为 P2 并标「需确认:X」,不要凭印象归类。

不报清单:

  • 纯格式、命名口味、import 顺序、主观分层偏好——不作为缺陷报(不进「必须修/建议修」);命名/分包口味若要统一风格,只能进「需要你拍板」,判据见 references/dimensions.md 第 4 维。
  • 没有具体回归路径的「建议加测试」。
  • 基线之前就存在、且与本次改动无关的问题(见上条既有问题规则)。
  • 已被同一改动在别处处理过的问题。
  • 泛泛的「复杂度高」「可读性差」,除非能指出读者会在哪一步读错。

6. 输出

按 references/output-template.md 的结构。先结论,后证据;确定项与待拍板项分栏。

判定纪律

  • 先判「设计使然」还是「缺陷」。 由既有设计推导出的结果(例如缺省输入导致本节点不执行)放在「需要拍板」,不要写成 bug。可预期的跳过/缺省是正常语义;只有它与失败不可区分、或静默改变汇聚语义时才算问题。
  • 不要默认设计是对的。 审查不是只验证「实现有没有偏离声明」——还要质问「这个声明本身站不站得住」。同一个概念在两条路径上行为不同、抽象层与实现层形状不一致、职责被两个抽象同时认领,这些都要主动去问,别等它咬人。
  • 不要把「没现成实现」当成「缺失」。 被审项目的基础库若没有某个应用层能力,不等于引擎缺项;先用现有机制做一次最小组合验证,再下结论。

输出契约

宿主若提供结构化评审协议(例如 PR 行内评论格式),以宿主协议为准:本契约降级为内容要求(结论 + 分级 + 证据 + 边界),结构让位。

快模式不套下面的七段骨架——它的输出形态由 references/regression-review.md 定(紧凑列表,零发现一句话)。下面这些要求针对全量模式,只有「结论行写各级数量」这条原则上两档通用(快模式一条条目都没有时不写数量,其余情况写;写法见 references/regression-review.md)。「零发现」= 一条条目都没有——只有 P3 也算有条目(P3 的条目形态见 references/regression-review.md)。

开头三行内给出:范围与基线 → 总体结论(可以合 / 改完再合 / 方向先定)→ 各级数量(计数格式:P0 n、P1 n、P2 n、P3 n、待拍板 n;没有基线时范围行按下面「范围行按基线类型分形态」那张表的「无基线」行写)。结论里写的每个数字都要在正文里数得出来。

范围行按基线类型分形态(这一份是唯一定义处,references/regression-review.md 与 references/output-template.md 都引用它;别处只许引用这张表,不许复述表里的写法):

基线类型范围行写法
分支 / PR范围:<分支/PR> 相对 <base> 的 N 个文件(merge-base xxxxxxx)。
commit 级范围:<commit> 相对 <父提交> 的 N 个文件(基线为父提交,无 merge-base)。
工作区改动范围:工作区改动 相对 HEAD 的 N 个文件(无 merge-base)。
无基线范围:<路径>(无基线,全量体检)。
分支 / PR(基线取不到)范围:<分支>(无基线:<原因>,按该分支全量体检)。

「N 个文件」是相对基线实际改到的文件数;路径型范围(模块体检、单个文件)没有基线,按上面第四行走。

意图来源全都没有时(无基线体检最常见:没有 PR 正文、没有 commit message),在结论段后加一行「意图:<你理解的这份代码要做什么>」——说出来作者才能纠正,别默默按错误意图写完整份报告。有基线且读得到 PR 正文 / commit message 时不必写这一行;有基线但读不到意图来源时照样要写。

结论档按这张表选,从上到下匹配,命中第一条即定档:

情况结论档
有方向性取舍待拍板——接口形状、模块边界、数据模型这类一改就牵动多处的决策方向先定
否则有 P0/P1 要修改完再合
否则可以合
有维度未扫时:覆盖表该行写「未扫(理由)」,并把跳过的维度写进第 7 段「本次没覆盖」(零发现模式没有第 7 段——写进最后那 1~2 句说明)——「可以合」只表示扫过的范围内没有必须改的问题,不代表没扫的那几维也干净。

P2/P3 不挡合并:它们是「建议修」段的建议,不是必须修——所以「可以合」和「P2 3」可以同时出现。非方向性的待拍板项也不挡合并(局部决策,如「错误码沿用 409 还是新造 422」),照样计入 待拍板 n。方向待定不影响其他维度的分级——P0/P1/P2/P3 照常报、照常计数。

无基线体检时(那里没有「合」这个动作,别让读者以为这个模块被批准合并了):

  • 「方向先定」读作「本路径有一处方向要先定」;
  • 「改完再合」读作「本路径有必须改的问题(P0/P1)」;
  • 「可以合」读作「本路径没有必须改的问题」——不等于「无需改动」:P2/P3 仍然列在「建议修」段,它们是要改、但不挡合并的。

零发现模式

当没有 P0–P2、没有待拍板项时(最多有 P3),不要套七段骨架:给结论(可以合)+ **「宣称:… / 实际:…」**那一行 + 维度覆盖表(15 行,每行都要有结论)+ 1~2 句说明查了什么、没查什么。有维度未扫时,把跳过的维度写进最后那 1~2 句说明,不另起第 7 段。 结论行仍写各级数量(如 P0 0、P1 0、P2 0、P3 1、待拍板 0)。P3 每一条各自列成「可选改进」条目(字段仍按建议修的四行:问题 / 证据 / 收益 / 修法),不写成散文一行;P3 达两条及以上时改用七段骨架的「建议修」段——不为 P3 展开整份骨架,但也不把它丢掉。

覆盖表在零发现时也要写:它正是「我扫过了」与「压根没扫」的区分——「可以合」这句话之所以可信,靠的就是这 15 行。

完整结构(七段)

  1. 结论(≤3 行:范围与基线 / 总体结论与各级数量 / 一句话。「意图:」「宣称:… / 实际:…」两行写在结论段之后,不计入这 3 行)
  2. 维度覆盖(每个维度一行:结果 + 查了什么)
  3. 必须修(P0/P1,按严重度排序)
  4. 建议修(P2/P3,按严重度排序;每条给收益)
  5. 需要你拍板(设计取舍、兼容性决策;每条给现状、证据、路线 A、路线 B、建议、要你定)
  6. 已查过没问题(条件段,见下)
  7. 本次没覆盖(没权限、跨仓库、未运行的测试、跳过的维度)

每一级都要有落点:P0/P1 进「必须修」,P2/P3 进「建议修」,设计取舍进「需要你拍板」。不许出现「结论里写了 P2 3、正文里找不到这 3 条」——分级定了级就要写出来,没有落点的级别等于没分级。

没有条目的段整段省略(如没有 P0/P1 时不写「必须修」,没有待拍板项时不写「需要你拍板」):不要为了凑满七段写空段、空标题。结论行的各级数量照常写 0(P0 0、P1 0、P2 3、P3 0、待拍板 0)——段可以省,数字不能少。

「需确认」不是一个独立档位:它是降级后的 P2——证据不足以定级,但值得作者自己看一眼。条目里标「需确认:X」,写清缺什么证据。不要为它新增一个计数(结论行只有 P0–P3 与待拍板),也不要把没证实的候选塞进「必须修」去虚报 P1。

标签形态固定三段:[级别][维度][引入方式]。级别段合法取值只有 P0 / P1 / P2 / P3 / 待拍板——拍板项不是 P 级,别用 P2 装它,否则「P2 n」和「待拍板 n」对不上。引入方式段合法取值只有 本次引入 / 既有 / 现状——有基线用前两个,无基线用 现状(规则见下方「引入方式段」)。维度段以覆盖表行名为准(覆盖表 15 行,一行一个):设计 / 方法契约 / 实现 / 文档 / 硬编码 / 重复 / 过度设计 / 错误路径与部分失败 / 并发与生命周期 / 安全 / 兼容性 / 可观测性 / 性能 / 规模与复杂度 / 测试盲区——不要一处写「方法契约」、另一处写「方法」。扩展维度的条目同样要标维度段,例如 ### [P1][安全][本次引入] internal/user/query.go:42 — 用户输入直接拼进 SQL。 同一事实同时满足多个维度时只报一次,归哪一维:第 3 维与某个扩展维度重叠(部分失败、并发)时归扩展维度——扩展维度是专门扫它的那一维;扩展维度与第 6 维重叠(两条路径各自都不一致)时归第 6 维;第 4 维(文档 / 宣称与实现不符)与扩展维度重叠时归扩展维度——「宣称与实现不符」的危害通常落在扩展维度扫的那一面(半完成状态、错误可观测性、并发交错),文档只是它的表现;只有扩展维度那一面查下来干净、危害仅止于读者被误导时,才归第 4 维。第 2 维(方法契约)与第 3 维重叠时归第 2 维:闸门表把零值可用性、错误语义、单位一致性这类条目路由到第 3 维的判据(输入 → 走到哪行 → 错误结果)去验——借的是判据,维度段仍写「方法契约」(playbook 必查项按它括号里的归属,第 2 维自己扫出来的按第 2 维)。两条规则同时命中同一个事实时(扩展维度同时与第 3 维、第 6 维重叠),以危害最具体的那一维为准。

第 2 段每格用五种取值:无发现 / 已查(见 X) / 未扫(理由) / n 条(级别) / n 条(级别),另见 X;n>0 时必须带级别标注(2 条(P1)、1 条(待拍板))——不带级别就和结论行对不上账,n=0 写「无发现」。一个维度同时有多个级别时写「n 条(P2 1、待拍板 1)」——各级各占一个数量,与结论行逐级对账。第 5 种的用法见下一段。已查(见 X) 只在结论归到了别的维度时用——例如一条 N+1 同时是性能坏味道和规模问题,按「两个桶都成立」的归栏规则落在「规模与复杂度」,性能行就写「已查(见规模与复杂度)」。「已查(见 X)」这个 token 只出现在结果格,「查了什么」列不要用它——那列写「由 XX playbook 覆盖:<被覆盖的条目名> <查了什么>」这类措辞。「查了什么」列每行至少给一个可核对的事实:文件名/行号、函数或类型名、grep 到的调用点数量、读过的测试名,或「未跑测试」这类明确的否定。只写维度名、只写「已全面检查」、只写模板句,都不算——那与「我真扫了」逐字不可区分,读者无法复核。playbook 覆盖行同样适用:写成「由批处理 playbook 覆盖:成功判定、幂等两条在 batch.go:44 逐条走过」,不要只写「由批处理 playbook 覆盖」。扩展维度各自成行,不要合并成一行——合并之后「无」就分不清是「扫了没问题」还是「压根没扫」。「规模与复杂度」也单独成行,跑过就写结果,没跑就写「未扫(理由)」。 第 5 种写法(本维既有自己的条目、又有结论归到别的维度时):结果格写「n 条(级别),另见 X」——前半段是本维自己的条目数,后半段指向另一维,两件事都交代了;不要写成纯「已查(见 X)」(那样本维的条数在正文里数不出来),也不要写成纯「n 条」(那样读者看不到本维也扫过、结论落到了别处)。 第 6 段是条件段:只有当本次真的跑了测试、构造了复现、对比了 base,或对某个高风险面做了专门证伪时才写;否则整段省略,并在第 7 段写明未做验证。写了就每条带一句验证方式(跑了什么 / 读了什么 / 对比了哪个 base)——写不出验证方式的不要写,宁可这段不存在。

可选的附录:被排除的候选——一条一行,写「候选 → 推翻它的证据」。当误报风险高或作者会怀疑「为什么没提 X」时加,能显著提高报告可信度;不必要时不写。

每条 finding 的字段固定(references/output-template.md 只给骨架,字段以本节为准):

「必须修」(P0/P1):

### [P1][实现][本次引入] path/file.go:142 — 一句话标题
- 触发:具体输入/状态/调用路径
- 影响:谁会看到什么错误结果
- 证据:读到的位置、调用方、是否实测
- 修法:最小改动方向(一条;没有就写「需先定方向」)

「建议修」(P2/P3)——当前行为是对的,所以没有「触发」,要写清收益(除规模/性能类外:这一类的规模锚点就是触发路径,降级为 P2 后写在「问题」行,详见下面「问题」字段的定义):

### [P2][文档][本次引入] path/file.go:30 — 一句话标题
- 问题:现在是什么样,读者会在哪一步做错
- 证据:读到的位置、调用方、是否实测
- 收益:修掉之后少踩什么坑、少付什么维护成本
- 修法:最小改动方向(一条;没有就写「需先定方向」)

字段按结论类型填,不要拿代码的形状硬套设计类或维护成本类:

  • 「触发」:实现类填具体输入/状态/调用路径;设计类(设计不合理、职责重叠、过度设计)填「在什么情形下后续使用者会走错」,如「再加一个同类后端时,调用方不得不手写一遍装配」;规模与性能类填「规模/频率 → 开销」——有实测写「N=___ 时实测 」;拿不出实测、但 N 与规模关系都清楚写「N= 时随 N ___(缺实测)」;只有调用频率证据的性能类写「调用频率 ___ 时每次触发 N 次 ___(缺实测)」;规模关系清楚但 N 的取值拿不出写「N=(取值待确认)时随 N ___(缺 N 取值)」。每种句式后面都接一段定位:「;file.go:42」——实测过的写实测方式(如「基准 bench_test.go:31」),没实测的写「未实测」。规模关系与实测数字就是它的触发路径(这类结论本来就没有错误结果,判据见 references/verification.md 的闸门表「规模与复杂度」行——拿不出实测数字不等于这条不成立)。
  • 「影响」:填「谁会看到什么错误结果」;设计类没有错误结果时,填「谁会在什么时候做出什么错误动作」。
  • 「问题」:文档类填「读者会在哪一步做错」;设计类(设计不合理、职责重叠、过度设计)填「在什么情形下后续使用者会走错 + 现有机制在哪」——设计类降为 P2 后也保留这个后果锚点;维护成本类(第二个真相源、脆弱硬编码)填「维护成本落在哪」——多出来的同步点、每次改都要动几处;配置类填「哪个值被写死、谁会想改它」;规模/性能类填「规模/频率 → 开销」这个规模锚点——降为 P2 时它没有「触发」行,规模锚点挪到「问题」行,不要因为降级就把规模关系删掉。

写作规则:

  • 用用户的语言。设计取舍和跨模块影响用大白话讲清机制,配一个最小对照例子;术语只在需要精确时出现。
  • 行号只用于定位,不当论据;论据是调用方、数据流、测试结果。
  • 引入方式段:三段始终齐全,合法取值只有 本次引入 / 既有 / 现状。有基线(commit/branch/PR/工作区改动)时:本次改动引入的标 [本次引入],基线之前就存在、被本次依赖或放大的标 [既有](且 [既有] 不进「必须修」)。无基线时标 [现状]——范围内的问题都是现状问题,不存在「谁引入的」这个问题;范围外引用到的代码不算 finding(写进「本次没覆盖」)。快模式必有基线,所以快模式不出现 [现状]。
  • 不重复贴 diff,不复述改动做了什么。
  • 确定项与待确认项分开;不确定的写「需确认:X」,不要写成断言。
  • 「已查过没问题」只写真正验证过的项,每项带一句验证方式;不要把「看起来合理」写成「没问题」。

只读边界与修复

审查本身是只读的:不改文件、不提交、不推送。「只读」指不改被审仓库——取证(基准、探针、最小复现)可以在仓库外的临时副本里做,被审目录保持原样。副本要跟被审的是同一份系统(同一个 commit + 同一份构建输入;带不过去的构建输入就别引用实测数字,判据见 references/dimensions.md 的「怎么拿实测数字」)。

用户明确要求「顺手修」时,才进入修改流程,并遵守项目的修复交付约定(通常是先 issue、再 PR;未合并 PR 上的问题修在原分支,已合并代码上的问题开新票)。不要只留本地改动。

失败处理

  • 读不到完整改动(二进制、生成代码、私有依赖)→ 明说哪部分没看,不要默认它没问题。
  • 找不到调用方(跨仓库、反射、DI、动态派发)→ 降为 P2 并标「需确认:X」,不要把「我没找到」写成「没有调用方」。
  • 没法运行测试 → 明说结论基于静态阅读,不要暗示已验证。
  • 范围太大 → 先按风险排序抽查,报告里写清抽样比例和未覆盖部分。
  • 一开始就发现意图理解可能有偏差 → 停下来用一句话确认,不要按错误意图写完整份报告。

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.