mcpField
D
doris-repo-review

doris-repo-review

github.com/apache测试与质量4745 次安装4 周前更新
代码仓库

关于 doris-repo-review

doris-repo-review 是面向 Apache Doris 贡献者的本地 PR 代码审查技能,基于当前克隆仓库和本地 Git 差异运行。它生成中英文审查文档,并通过校验脚本检查覆盖范围、结论与发现。

主要功能

  1. 01对齐当前目录到 PR head
  2. 02生成权威差异与审查上下文
  3. 03记录文件覆盖与审查过程
  4. 04校验中英文审查文档
  5. 05符合条件时发布 PASS 评论

适用场景

  1. 01在本地审查 Apache Doris PR
  2. 02检查改动文件是否均已覆盖
  3. 03整理并验证中英文审查结论

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/apache/doris-skills --skill doris-repo-review

安装后在 Claude Code 中输入/doris-repo-review确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
doc-templates.md
pr-comment-format.md
prompts.md
qualified-runtime.md
scripts/
align-to-pr.sh
coverage-report.sh
post-pass-comment.sh
prepare-review-context.sh
record-review-runtime.sh
review-runtime-policy.sh
save-run-state.sh
verify-review-docs.py

详细介绍

本地流水线式 Doris 代码审查

此技能会针对本地克隆的 Apache Doris 源代码仓库审查拉取请求。 这是一个面向贡献者的工作流,而非集群运维技能:它不会连接正在运行的集群,也不需要集群。

用法:

/doris-repo-review https://github.com/apache/doris/pull/66807
/doris-repo-review https://github.com/apache/doris/pull/66807 focus on class loading and compatibility
/doris-repo-review 66807            # a bare number defaults to apache/doris

此技能将 apache/doris/.github/workflows/code-review-runner.yml 中的审查流程移植到本地机器:首先将当前目录对齐到 PR 的头部提交,从本地 git 获取差异,并在当前目录下的 review-docs/ 中生成一份英文文档和一份中文文档。其他所有内容——必读材料、主 Agent 风险扫描、子 Agent 分工、共享台账、最多 3 轮收敛、每个检查点都明确给出结论——均与 CI 一致。

审查范围仅限于你当前所在的这个工作树。 不会扫描、选择或创建其他工作树;不会通过环境变量选择其他目录;也不会对机器的目录布局作任何假设。

设 $S 为此技能自身的 scripts/ 目录(默认 Claude Code 安装位置为 ~/.claude/skills/doris-repo-review/scripts):

$S/align-to-pr.sh <PR> --check                      # diagnosis only, changes nothing
$S/align-to-pr.sh <PR> --out "$CTX/align.env"       # step 1: align the current directory
$S/prepare-review-context.sh --ctx "$CTX" --align "$CTX/align.env"   # step 2: gather context
$S/coverage-report.sh --ctx "$CTX"                  # step 6a: what has nobody read yet
python3 $S/verify-review-docs.py --ctx "$CTX" --doc <en> --doc <zh>  # step 9: verify documents
$S/record-review-runtime.sh --ctx "$CTX" --model <id> --effort <effort>
$S/post-pass-comment.sh --ctx "$CTX"                                 # step 10: auto-post PASS
$S/review-runtime-policy.sh check <model> <effort>                    # reviewer eligibility
$S/save-run-state.sh --ctx "$CTX" --verdict ... --findings b,m,mi,n  # step 11: leave state for next time
文件用途
scripts/align-to-pr.sh解析 PR,诊断当前目录与其的关系,并将目录对齐到 PR 的头部提交
scripts/prepare-review-context.sh生成权威差异、新侧行范围、必读的 AGENTS.md 列表、已有评论和台账骨架
scripts/coverage-report.sh机械检查尚未被任何台账文件提及的变更文件
scripts/verify-review-docs.py验证提交、锚点、中英文一致性、结论、发现、每项发现的类别与回归标记(含类别感知的 Major 下限)、轮次和收敛情况
scripts/record-review-runtime.sh记录符合条件的审查者模型、投入程度和确切提交
scripts/review-runtime-policy.sh与流水线等效审查所允许的确切模型和投入程度
scripts/post-pass-comment.sh生成并发布机器可读的 PASS 评论;若审查未收敛、未通过或存在低于规定级别的回归,则拒绝发布;若有其他回归未披露,也拒绝发布
scripts/save-run-state.sh将本次运行合并后的台账保存到稳定的 PR 状态目录,以便下次审查沿用已驳回的发现
references/prompts.md子 Agent 提示词模板(CI 措辞,原样沿用)
references/doc-templates.md两份文档的模板、锚点格式和结论规则
references/pr-comment-format.mddoris-repo-review/v1 评论架构、字段含义,以及程序读取该评论的方式
references/qualified-runtime.md如何选择或委派符合条件的主审查者和覆盖范围审查者

要求:git、已通过身份验证的 gh CLI、jq 和 python3。克隆仓库必须包含完整历史(浅克隆仓库需运行 git fetch --unshallow),因为权威差异是从合并基点生成的三点差异。

0. 基本规则

阅读源代码之前,先阅读 references/qualified-runtime.md,并确定一位符合资质的主审查员。如果要求委派,调用方应委派整个任务,自己不进行任何审查。如果没有符合资质的运行环境,只生成本地文档,不得发布等同于流水线 PASS 的评论。

  1. 只操作当前目录,绝不扰动本地工作。 如果当前目录有已修改的受跟踪文件,拒绝切换目录,说明情况,让用户自行提交或暂存这些更改——绝不代替用户暂存、重置或删除分支,也绝不四处寻找其他目录来开展工作。所有切换操作都使用 git checkout --detach,确保分支引用保持原样。
  2. 只读审查。 不得构建、运行测试或修改仓库中的任何源文件。本地只允许写入 review-docs/ 下的两份文档和上下文目录 $CTX。 任何时候,GitHub 上最多只能写入一项内容:步骤 10 中的 PASS 评论,且仅在明确请求的审查得出收敛的 APPROVE 结果后,通过 post-pass-comment.sh 自动发布。REQUEST_CHANGES 审查不发布任何内容。不得发布行内评论、提交审查、添加标签、编辑 PR 正文,也绝不在正在审查的 PR 之外的任何 PR 上发表评论。
  3. 差异的来源只有一个:$CTX/pr.diff 和 $CTX/pr_changed_files.txt。不得使用 gh pr diff、Web 界面或手动编写的 git diff 获取更改列表——以其他方式获取意味着采用了不同的基准。
  4. 读取路径前先确认路径。 如果 pr_changed_files.txt、pr.diff 或之前成功执行的命令输出中尚未确认某路径,先运行 rg --files 确认该路径。 4a. 证据可能位于仓库之外,而且你应当主动查找。 规则 3 规定了更改列表的来源;它并未规定证据的来源。当某个依赖自身的内容决定了正在审查的行为时,应阅读该依赖:解压构建实际固定的 ~/.m2/repository/... 中的 jar 并阅读其中的资源,阅读相关语义所依赖的库的 -sources.jar,阅读随仓库提供的服务定义,阅读注释所声称的契约对应的 JDK 类。只查看差异无法发现缺陷:这类缺陷的两部分分别是“被修改的代码行”和“该代码行现在会调用到的内容”——最严重的问题恰恰藏在这里。报告问题时,务必注明构件名称和版本,以便复现证据。
  5. 不要发现第一个阻断问题就停止。 检查所有已修改文件、相关控制流、测试,以及并行或特殊情况路径。
  6. 每个疑点都必须得出结论:将其作为问题报告,判定为“已由现有评论覆盖”,或以具体代码证据排除,并写入“考虑后排除”。悄悄放弃疑点意味着审查尚未完成。
  7. 本地没有行内评论,因此定位信息是读者唯一的入口。 每个问题都必须带有 path:line 定位信息(新版本侧的行号)以及逐字摘录的代码片段,并且文档最终必须通过 verify-review-docs.py 的验证。

1. 对齐当前目录

CTX="<session scratchpad>/review-ctx"
$S/align-to-pr.sh <PR> --out "$CTX/align.env"

该脚本会执行四项操作,且全部仅针对当前工作树:

  1. 解析 PR:接受 URL、owner/repo#N 或纯数字;gh api 会提供标题、状态、head 分支 / 仓库 / sha,以及 base 分支 / sha。

  2. 诊断一致性(用户明确要求的两项检查):

    • branch check:当前分支是否为 PR 的 head 分支?
    • commit check:same / ahead:N / behind:N / diverged:a/b / unrelated 将这两行原样转告用户。 特别是,ahead:N 表示存在从未推送的本地提交,这些提交不属于本次审查范围。
  3. 获取 PR head:git fetch <upstream> +refs/pull/N/head:refs/doris-review/pr-N。它会通过 refs/pull/* 获取,因此 fork PR 无需额外的远程仓库。获取到的 sha 必须与 API 返回的 head sha 相同;如果不同,说明准备上下文期间 PR 有新推送——脚本会重新读取一次 API;若仍不一致,则报错并要求重新运行(这与 CI 中“准备审查上下文时 PR 已更改”的保护机制一致)。

  4. 对齐当前目录:

    当前目录的状态操作
    HEAD 已与 PR head 相同ALIGN_MODE=already,无需操作
    干净git checkout --detach <PR head>,ALIGN_MODE=switched
    存在已修改的跟踪文件 / 正在进行 rebase、merge 或 cherry-pickALIGN_MODE=blocked,拒绝操作并退出

    “干净”是指没有已修改的跟踪文件。未跟踪文件不计入(git checkout 不会删除它们;如果会覆盖未跟踪文件则会直接拒绝),因此 review-docs/ 这样的未跟踪目录不会影响判断,也会在切换后保留。

遇到 BLOCKED 时的处理方式:转告用户脚本打印的脏文件列表,然后让用户选择——(a) 自行提交,(b) 自行暂存,或 (c) 在其他目录重新开始。不要替用户执行这些操作;用户选择并执行后,重新运行此技能。如需在不进行任何更改的情况下查看诊断结果,请使用 --check(它仍会在报告 BLOCKED 之前完整打印 branch check / commit check)。

对齐后,$CTX/align.env 会提供 WORKDIR(= 当前目录)、DOCS_ROOT(= WORKDIR)、PR_HEAD_SHA / PR_BASE_SHA、PREV_REF(恢复点)、BRANCH_MATCHES_PR 和 LOCAL_VS_PR。


2. 准备上下文

$S/prepare-review-context.sh \
    --ctx "$CTX" --align "$CTX/align.env" [--focus "<user focus>"] [--fresh]

--align 会沿用 WORKDIR、PR 编号、上游仓库和 PR 基准 sha,并验证检出状态是否确实停留在 PR head 上(如果不是,脚本会报错并要求你重新执行步骤 1)。--fresh 会清除并重建所有内容,包括 ledger;不使用该参数时会保留 ledger,以便多轮运行可以继续。用户在命令行 PR URL 后输入的任何自由文本,都会作为 --focus 的值。

输出(位于 $CTX 下):

文件内容
meta.envPR 信息、BASE_SHA / HEAD_SHA / MERGE_BASE、TARGET_AHEAD(自合并基点以来的目标分支提交数)、差异范围、DOCS_ROOT、ALIGN_MODE、日期、脏文件数量
pr.diff / pr_changed_files.txt / pr_changed_files_status.txt权威差异和更改列表
changed_line_ranges.txt / .tsv新侧(更改后)的行范围——锚点的来源
pr_commits.txt / pr_diffstat.txt提交列表和更改规模
pr_review_threads.md现有行内评论(最多 30 个线程,每条正文截断为 1200 个字符)
required_agents.txt / required_agents_prompt.txt必需的 AGENTS.md 文件,根据更改文件的上级目录确定
review_focus.txt用户的关注点
worktree_status.txt审查树中的未提交更改(不属于审查范围;请在文档中指出)
ledger/共享 ledger 骨架
coverage_checklist.tsv每个更改文件一行,用于步骤 6a 的机械覆盖报告
prior_runs/先前对同一 PR的审查结论——见下文

如果 BASE_SOURCE 不是 PR base sha (matches CI),则基线与 CI 的基线不同,文档中必须说明这一点。此步骤会产生两个不同的“之前”提交,它们不可互换:BASE_SHA 是生成差异所依据的目标分支头部,也是 PASS 回执所绑定的提交;MERGE_BASE 是 PR 实际分出的基点,也是回答“这段代码在 PR 之前做了什么”的唯一正确依据(步骤 4.1 和 6)。当 TARGET_AHEAD 不为零时,两者会有所不同,脚本也会说明这一点。

在阅读源代码之前,记录步骤 0 中选定的、已通过资格验证的运行时:

$S/record-review-runtime.sh --ctx "$CTX" --model "<exact model>" --effort "<exact effort>"

如果运行时遭到拒绝,请仅使用本地文档继续,不要发布与流水线等效的 PASS 评论。

2.1 同一 PR 的过往运行结果是输入,而非历史记录

一个 PR 通常会被审查多次——作者推送更新后,你会再次运行。过去,每次运行都会从头开始,重新推导相同的驳回理由、重新阅读相同的文件,而这些内容也不会留存。这是预算浪费的最大来源,也正因如此,以前无法回答“为什么上次审查没发现这个问题?”

因此,prepare-review-context.sh 会将每次运行合并后的记录保存在每个 PR 独有的稳定状态目录中,该目录位于会话临时目录和仓库之外:

${DORIS_REVIEW_STATE:-${XDG_CACHE_HOME:-$HOME/.cache}/doris-repo-review}/<owner>-<repo>/pr-<N>/
    runs/<head7>/main-merged.md      one per reviewed head
    runs/<head7>/meta.env
    index.tsv                        head sha, date, verdict, finding counts

并将找到的内容复制到 $CTX/prior_runs/。请在步骤 3 中阅读这些内容,紧接着阅读 pr_review_threads.md,并像对待已有审查讨论串那样对待它们:

  • “Considered and Dismissed” 表格是最耗费精力的部分。 仍然成立的驳回理由无需重新推导;将其连同原始证据一起延续到本次运行的表格中,并注明已重新确认(或说明为何不再成立)。只有当新的 head 可能改变某条驳回理由的前提时,才重新推导该理由。
  • 已接受的发现会告诉你作者此前已获知哪些问题。若某个问题现在已修复,只需在结尾报告中用一行说明(“已在此 head 修复”),不要将其列为发现。
  • 如果之前的运行审查的是不同的 head,请说明这一点,并检查改动内容。 分支可能已被变基,因此按提交哈希比较并不可靠:请比较内容(git show <old head>:<path>),再声称某条发现是新的。

空的 prior_runs/ 表示这是对此 PR 的首次审查,而非此前从未有人审查过。请在“Coverage and Limits”中说明属于哪种情况。


3. 必须阅读的内容(按此顺序)

在查看任何代码之前,主 Agent 应依次阅读以下内容:

  1. $WORKDIR/.claude/skills/code-review/SKILL.md——Doris 审查清单;整个审查过程中必须严格遵循。在较旧的分支中,若该文件不存在,请使用 gh api repos/apache/doris/contents/.claude/skills/code-review/SKILL.md?ref=master 获取。
  2. $CTX/required_agents.txt 中列出的所有 AGENTS.md 文件,并逐个完整阅读。仅列出目录内容或搜索路径不算阅读。
  3. $CTX/pr_review_threads.md——将所有现有评论视为已知上下文。不要再次提出相同或实质上相似的问题,即使表述不同。只有当此 PR 在其他位置引入了现有评论未涵盖的真正不同实例时,才提出相似的疑虑,并说明其不同之处。 3a. $CTX/prior_runs/ 下的所有内容——同一 PR 之前各次审查的结论。参见第 2.1 节:沿用驳回理由及其证据;若已接受的发现现已修复,则在结尾报告中用一行说明;若之前的运行针对不同的 head,则比较内容而非提交哈希。
  4. $CTX/review_focus.txt——照常进行完整审查,并特别关注其中的要点;最终文档必须逐一回应每个要点,即使结论是“针对该要点未发现其他问题”。
  5. $CTX/pr.diff 的全部内容以及 $CTX/pr_commits.txt。当 diff 较大(> 5000 行)时,按目录或模块分块阅读,直到覆盖全部内容;并在“Coverage and Limits”中说明阅读方式——不要声称自己读完了全部内容。

4. 主 Agent 初始风险扫描(生成任何子 Agent 之前)

必须在生成任何子 Agent 之前完成此步骤。 通读整个变更,理解它涉及的每种机制,然后回答:如果这个 PR 有问题,最可能出在哪里?哪些点看起来有风险?

将结果写入 $CTX/ledger/00-main-risk-scan.md,每项都包含:ID、涉及的变更文件/行、要检查的相关机制、可疑之处、必须一并阅读的上游或下游文件、风险聚焦子 Agent 必须回答的具体问题,以及——见下文——前提检查。

风险扫描为空,说明你还没有理解这个 PR——回去阅读,不要跳过此步骤。

4.1 每项风险都要包含前提检查,并在生成任何子 Agent 之前执行检查

风险项总是基于某个前提:“之前是 X,现在是 Y”、“没有任何内容会验证 Z”、“只有一个调用方会执行 W”。如果前提不成立,子 Agent 就会把全部精力都花在证明你错了,而你学不到任何无法在 30 秒内自行确认的内容。

因此,每项都要再添加一个字段:

  Premise:        <the one factual claim the whole item rests on>
  Premise check:  <a single command that confirms or kills it>
  Premise result: <confirmed | FALSE - item dismissed | cannot be checked cheaply>

在第 5 步之前执行所有检查。检查几乎总是针对合并基准(meta.env 中的 MERGE_BASE——而不是 BASE_SHA,它是目标分支的最新提交,可能包含 PR 从未见过的提交;TARGET_AHEAD 表示领先的提交数)的一行命令:

git show "$MERGE_BASE:path/to/File.java" | grep -n 'thing I think is new'
git show "$MERGE_BASE:path/to/File.java" | sed -n '120,140p'
rg -n 'symbol' --files-with-matches            # "only one caller" claims

如果前提检查结果为 FALSE,就不要将其交给子 Agent,而是在“已考虑并排除”中新增一行,并将命令作为证据。这一行与一项发现同样有价值:它表明审查中“已检查过这一点”,而成本只是执行一条命令,而不是调用一个 Agent。

如果某个前提确实无法通过低成本检查,就可以将其交给子 Agent,并在提示词中说明该前提尚未验证,让子 Agent 先检查;如果检查不成立,就尽早停止。


5. 生成子 Agent

按照代码审查技能要求的覆盖范围进行拆分,并且在一条消息中发送给所有子 Agent,让它们并行运行(通用子 Agent)。使用与主 Agent 相同的、符合资格的模型和推理力度;遵循 references/qualified-runtime.md 中的覆盖审查规则:

类型数量职责
完整审查子 Agent1-3 个(视 PR 复杂度而定)每个子 Agent 负责一个切片;所有切片的合并范围必须覆盖整个 PR,以及技能要求的每个检查点
风险聚焦子 Agent每个可疑机制一个独立于完整审查子 Agent,准确回答风险扫描中的一个问题

按模块或机制拆分(例如“FE SPI 接口和加载路径”、“Ranger 插件实现和类加载”、“构建脚本和打包/部署”),不要平均分配文件数量——如果拆分点落在某个机制中间,双方都无法发现其中的错误。为每个子 Agent 分配自己的账本文件 $CTX/ledger/sub-<round>-<id>.md。

从 references/prompts.md 获取提示词:第 A 节(通用前言)加上第 B 节(完整审查)或第 C 节(风险聚焦),并替换 {CTX} / {REPO_ROOT}(即 $WORKDIR)/ {BASE_SHA} / {HEAD_SHA} / {ROUND} / {AGENT_ID} / {FOCUS}。**每个子 Agent 的提示词都必须写明账本目录,以及该子 Agent 专属文件的路径。**该文件的第 D 节列出了实际有效的发现问题技巧;请在每个提示词中指定要使用的技巧,不要让子 Agent 自行想办法。

与 CI 的一处实现差异:CI 使用一个带多个分节的 subagent_review_findings.md;本地并行写入同一文件时,补丁会发生冲突,因此改为在 ledger/ 下为每个负责人单独创建一个文件。语义不变——仍使用一个共享的事实来源,所有人都读取其全部内容,各自只写入自己的文件,最后由主 Agent 汇总。

5.1 第 1 轮分两波进行,因为开始时台账为空

去重规则(“审查前先读取 ledger/ 中的每个文件”)在第 1 轮行不通: 所有子 Agent 都在同一时刻开始工作,而目录还是空的。实际操作中,三个 Agent 会各自重新发现同一个缺陷,三份预算却只换来一个发现。

因此,第 1 轮——也只有第 1 轮——分两波派发:

  • A 波:完整审查子 Agent。 与之前一样,全部并发执行。它们负责各自的切片, 因此覆盖范围必须合并起来涵盖全部内容。
  • B 波:聚焦风险的子 Agent。 A 波返回后再派发,彼此并发执行。它们先读取 A 波的台账文件, 因此,A 波已经确定的机制会被记为重复项,而不是引发第二次调查;A 波已经回答的风险项也会在耗费资源前被移除。

两点让这种做法既省时又不拖慢进度。无论如何,A 波都是耗时最长的部分。而且主 Agent 在这期间并未闲置——它会在 B 波运行时合并 A 波的结果(步骤 6)。

如果确实无法安排两波——例如 PR 很小,或风险项非常具体,不属于任何完整审查切片——就一次性派发所有 Agent,改为预先填充台账: 启动前,将你预计各切片会发现的缺陷写入 00-main-risk-scan.md,这样并发执行的 Agent 就能识别出其中属于你已记录的缺陷,并将其标记为重复项,而不是从头撰写报告。

从第 2 轮开始,台账已经有内容,因此每轮的所有子 Agent 都会一起派发。


6. 主 Agent 合并、验证、去重

每次子 Agent 返回时,都要重新阅读整个 ledger/ 目录,然后将每个候选项处理到 $CTX/ledger/main-merged.md:

  • 验证:亲自阅读代码确认;不要轻信子 Agent 的结论。常见的错误模式包括:把上游保证已覆盖的本地模式当作缺陷,以及提出“如果 A,那么 B”,但 A 没有具体场景。
  • 去重:与现有候选项以及 pr_review_threads.md 中的现有评论进行比对。
  • 指定状态:accepted / dismissed_with_evidence / duplicated。驳回时必须提供具体代码证据,并将其原文放入文档的“Considered and Dismissed”部分。
  • 分类:为每个已接受的候选项指定一个且仅一个来自 references/doc-templates.md 封闭词表的 Category:functional-bug、functional-loss、data-error、resource-leak、performance、observability、test-coverage、wording、maintainability;分类应描述其后果,领域(并发、生命周期、兼容性、配置等)放在其后的括号中。验证器会拒绝任何其他值。
  • 标记回归:对每个已接受的候选项,基于合并基点判定 Regression: yes | no——HEAD 的行为是否与 git show $MERGE_BASE:<path> 不同,且 PR 正文未说明这是有意的?不要使用 $BASE_SHA:当 TARGET_AHEAD 非零时,目标分支包含 PR 未曾看到的提交,而该分支上已落地的修复可能不会出现在 HEAD 中,尽管 PR 并未删除任何内容(正常合并会保留该修复)——这不属于回归;如果存在语义合并问题,应单独列出。将标记及其证据记录在 main-merged.md 中;它会成为问题条目中必填的 Regression 行。然后应用 references/doc-templates.md 中的严重性下限:functional-bug、functional-loss、data-error、resource-leak 或 performance 类别中的回归至少为 Major;对于声称保持行为不变的 PR,差异表中每个未声明的不同单元格都属于此类回归。其他四种类别中的回归根据其后果判定,但低于 Major 时必须写明 Severity rationale,解释为何该行为变化可以接受。严重性取决于触发时的后果,而非发生概率;“触发范围窄”“仅限云端”“只需修改一行”以及“结果没有错误”都不是降低严重性的理由。verify-review-docs.py 会拒绝将触发下限的回归评为 Minor/Nit,也会拒绝未触发下限且没有理由说明的情况,因此应在此处作出判断,而不是等到编写文档时再决定。
  • 降级前提出严重性质疑:如果你打算将候选项评为低于子 Agent 建议的严重性,或驳回 functional-bug / functional-loss / data-error / resource-leak 候选项,先将 Severity rationale(触发时的后果、谁会注意到、哪些内容仍然泄漏或错误、为何不属于 Major)写入 main-merged.md,然后按照 references/prompts.md 的 E 节提示词,派出一个严重性质疑子 Agent——它唯一的任务是根据一手资料论证更高的严重性。只有在它返回后才能最终确定严重性,并将理由保留在问题条目中。这是一个简短的 Agent 步骤;跳过它,就会重蹈 apache/doris#67900 的覆辙:发布了一个 Minor 问题,下一位审核者不得不将其退回并要求作为必须修复的问题处理。
  • 补全风险条目:为 00-main-risk-scan.md 中的每个条目更新 Status 和 Final conclusion。

完成此步骤时,不得有任何候选项缺少状态、类别或回归标记;若候选项被降级,或属于未触发严重性下限且低于 Major 的回归,还必须附有严重性理由。

6a. 在每轮结束时运行覆盖率报告
$S/coverage-report.sh --ctx "$CTX"

这是一项机械检查,不涉及判断:它遍历 pr_changed_files.txt,报告哪些变更文件在任何账目文件中都没有被提及。发现“有 13 个变更文件无人查看”,是 grep 的工作,不该留给第 3 轮的子 Agent——而在第 1 轮结束后就知道这一点,才能让第 2 轮有针对性,而不是靠猜。

报告结果是下一轮划分审查范围以及作出结论的依据:**没人读过的文件,不等于没有发现问题的文件。**要么在下一轮覆盖它,要么在第 8 步亲自阅读。


7. 收敛循环(最多 3 轮)

一轮 = 第 5 步 + 第 6 步 + 第 6a 步。在 main-merged.md 的 Convergence Rounds 表格中记录结果,每轮一行,包含:子 Agent、新发现的问题(按严重程度)、剩余覆盖缺口、结论。

**收敛关注的是结论,而不是数量。**设置多轮,是为了让你确信即将给出的答案——哪些发现会阻止合并——而不是清空小问题。只要继续查找,大型 PR 总会出现另一个 Minor;若规则要求等到不再出现 Minor,再结合下面的重新划分规则,就永远无法结束。

因此,满足以下两个条件时,这一轮就算收敛:

  • 没有发现新的 Blocker 或 Major;并且
  • 第 6a 步的覆盖率报告无遗漏——每个变更文件都已被某人阅读。

然后进入第 8 步,并在文档中记录:从第 N 轮起,结论已保持稳定。

否则开始新一轮:根据本轮了解到的情况重新划分覆盖范围(不要原样重复同一划分),并针对新发现的可疑机制添加聚焦风险的子 Agent。新一轮要针对实际产生严重问题的部分,而不是针对剩余部分。

  • 最多 3 轮。如果达到上限时仍在出现 Blocker/Major 候选问题,或仍有覆盖缺口,照常完成审查,但要在结论和“Coverage and Limits”中说明本次审查未收敛,并指出未满足的是哪一个条件。
  • 如果达到上限时只剩下零星的 Minor/Nit,则视为已收敛,并应在文档中明确写明:“结论已在第 N 轮确定;之后的轮次只新增了 Minor 和 Nit 级问题。”不要把已收敛的审查报告为失败。
  • **仅返回 Nit 的一轮是停止信号,不是继续下一轮的理由。**记录本轮产出(投入的 Agent 数量、各严重程度的问题数量),以便后续人员了解收益何时开始递减。

8. 最终检查

编写文档前,明确检查变更文件列表和未解决候选问题列表:

  • 最后运行一次 $S/coverage-report.sh --ctx "$CTX"。每个变更文件是否都至少由一个子 Agent 覆盖?亲自检查剩余文件,并在“Coverage and Limits”中说明哪些文件是你在此处才阅读的。
  • 每个疑点是否都有结论?
  • 从 prior_runs/ 沿用的每项驳回,是否都已重新确认或重新审查?
  • 代码审查技能 Part 1.3 中每个适用项是否都有明确结论?
  • 是否还有任何不确定之处,或可能调查得不够深入的地方?现在就调查。

只有完成检查后,才能编写文档。

9. 生成这两份文档

将文档写入当前目录下的 review-docs/(如果需要,先运行 mkdir -p),并按实际审查的 head 命名:

review-docs/pr-<N>-review.<head7>.en.md      e.g. pr-66770-review.3f45815.en.md
review-docs/pr-<N>-review.<head7>.zh.md
review-docs/pr-<N>-review.en.md              symlink -> the newest of the above
review-docs/pr-<N>-review.zh.md              symlink -> the newest of the above

绝不要覆盖之前运行生成的文档。 名称中包含 head sha,正是为了防止重新审查时破坏上次得出的结论:只有这份记录能回答“上次是否考虑过这个问题,以及根据什么证据将其排除?”——一旦记录消失,就无法找回,因为 review-docs/ 未纳入版本控制。两个不带后缀的名称是便捷符号链接;历史记录不断累积时,任何指向它们的链接仍可正常工作。

让 verify-review-docs.py 指向实际文件,而不是符号链接。如果这个确切 head 的文档已经存在,说明你正在针对未变更的 head 重新运行:覆盖那一份文档,且只能覆盖那一份。

references/doc-templates.md 包含模板、锚点格式和结论规则(Blocker/Major → REQUEST_CHANGES;只有 Minor/Nit → APPROVE)。要点如下:

  • 两份文档的内容必须等同:finding ID、顺序和锚点相同。中文文档必须是真正以中文撰写的审查,而非逐字翻译;标识符、路径、日志消息、配置名称、代码片段和严重程度用词都保留原文。
  • 文档头必须注明:PR 链接和状态、实际审查的 head sha、diff 范围、审查目录(WORKDIR),以及第 1 步中的 branch check / commit check 结果。
  • 每个 finding 必须包含:严重程度、来自封闭词汇表的 Category、带有 merge-base 证据的 Regression: yes | no 行、path:line 锚点、逐字引用的片段,以及四个部分:问题所在 / 问题产生的原因 / 何时会造成影响 / 建议的修复方案。如果严重程度低于子 Agent 提议的等级,或者 observability / test-coverage / wording / maintainability 类别的回归问题被评为低于 Major,还必须说明严重程度判定依据。如果修复方案较小且独立完整,请提供 diff 补丁;对于架构性问题,只用文字描述。
  • “Critical Checkpoints”表格中的每一行都必须有结论;不适用的项目标记为 n/a,并附上简短理由——绝不要删除行。
  • “Response to Review Focus”须逐一回答用户提出的所有关注点。
  • “Considered and Dismissed”须列出每一项被排除的疑虑及其证据。
  • “Coverage and Limits”须说明:哪些内容经过深入阅读、哪些仅略读,子 Agent 如何分工,本地未验证的事项(构建、测试、任何需要真实集群的事项),worktree_status.txt 中被排除的未提交更改,本次审查基于该 PR 的哪些更早运行记录(来自 prior_runs/ 的 heads 和日期,或“首次审查此 PR”),以及审查是否收敛(按第 7 步的定义)——注明结论停止变化的轮次;若未收敛,则说明未满足两个条件中的哪一个。

文档写好后,必须运行验证器;如果验证失败,请修正文档并重新运行,直到验证通过:

python3 $S/verify-review-docs.py --ctx "$CTX" \
    --doc review-docs/pr-<N>-review.en.md \
    --doc review-docs/pr-<N>-review.zh.md

验证器会检查审查的 head、锚点、中英文 finding 顺序、严重程度、类别和回归标记、按类别判定的回归严重程度下限、低于 Major 且未达到回归下限时所需的判定依据、结论、轮次和收敛情况。继续之前,修正所有错误——如果错误提示“is a regression in category … but rated Minor”,应修正严重程度(并相应调整结论),而不是修改回归标记或类别。

10. 将 PASS 评论发布到 PR

仅当判定结果为 APPROVE、评审已收敛且第 9 步通过时才执行。REQUEST_CHANGES 或未收敛的评审不会发布任何内容——在结束报告中说明这一点,然后停止。已设最低级别的类别中只能在 REQUEST_CHANGES 评审中出现回归,因此不会到达评论发布程序;在 observability / test-coverage / wording / maintainability 中评级为 Minor / Nit 且标记为 Regression: yes 的项会到达评论发布程序,回执必须披露其中每一项。

撰写备注并运行一次评论发布程序:

# At most 5 bullets, each anchored where it can be. Every finding flagged `Regression: yes`
# comes first, named with its ID and category; the poster refuses to post without notes when
# such a finding exists. Skip the file only when there is nothing to say and no regression.
cat > "$CTX/pr-comment-notes.md" <<'EOF'
- F-01 (observability, undeclared behaviour change) `fe/fe-core/src/main/java/org/apache/doris/X.java:214` — <what changed and why it is acceptable>
- `fe/fe-core/src/main/java/org/apache/doris/Y.java:88` — <what the maintainer should know>
EOF

$S/post-pass-comment.sh --ctx "$CTX" \
    --notes-file "$CTX/pr-comment-notes.md"

评论发布程序会从 review-runtime.json 读取模型、投入量和提交;它会自行调用 verify-review-docs.py 获取判定结果、发现项、回归计数、轮次和收敛情况; 然后重新检查 PR 当前的 head。拒绝即为最终结果。正常流程会立即发布;--dry-run 仅供维护者测试。相同账号加相同提交会更新现有评论,而新提交会创建新评论。请保持 references/pr-comment-format.md 中的架构由机器生成。


11. 结束报告

告知用户:

  1. 两份文档的路径、判定结果(REQUEST_CHANGES / APPROVE)、各严重级别的发现项数量、每项标记为 Regression: yes 的发现及其类别(这些是 PR 正文未声明的行为变更;作者应当声明或修复它们),以及第 7 步所述轮次是否收敛——若已收敛,说明“判定已在第 N 轮确定”;若未收敛,说明未满足的条件。即使运行结束时只剩下 Minor/Nit,也仍然算作已收敛;不要将其报告为失败。 1a. prior_runs/ 非空时,说明本次运行继承了什么:之前评审过哪些 head、带入了多少项驳回记录,以及这些已接受发现中有哪些已在当前 head 修复。如果没有之前的运行记录,说明这是对此 PR 的首次评审。

  2. branch check / commit check 的结果,尤其是 ahead:N(尚未推送、因此未评审的提交)。

  3. PASS 评论的处理结果:发布或更新评论时给出 URL;未发布时说明原因(REQUEST_CHANGES、未收敛、运行时条件不合格、存在未披露的回归,或 PR head 已变更)。

  4. 当前目录的状态:当 ALIGN_MODE=switched 时,目录处于 PR head 的分离状态,执行 git checkout <PREV_REF> 可恢复。不要自动切回——用户可能仍想阅读代码。

  5. review-docs/ 在 doris 仓库中未被 gitignore,因此不要自动提交;由用户自行处理。

  6. 结束前保存运行状态——这能降低下一次评审的成本,也让本次评审可审计:

    $S/save-run-state.sh --ctx "$CTX" \
        --verdict REQUEST_CHANGES --findings <blocker>,<major>,<minor>,<nit> \
        --rounds <r> --converged <true|false> \
        --docs "review-docs/pr-<N>-review.<head7>.en.md" --note "<one line: the headline finding>"
    

    两种判定结果都会执行此操作——REQUEST_CHANGES 评审需要交接的信息与通过评审一样多,甚至更多。告知用户保存位置,并打印该操作回显的每 PR 历史记录,以便在一处查看此 PR 的评审序列。


12. 与 CI 流水线的对应关系

CI(code-review-runner.yml)本地
codex exec --goal(gpt-6-sol,xhigh)明确选定一位符合资格的主审查者
检出 PR head shaalign-to-pr.sh 将当前目录切换到同一 sha
将 git diff BASE...HEAD 作为权威差异相同,由 prepare-review-context.sh 生成
“准备期间 PR 已更改”检查获取的 sha 必须与 API head sha 相同,否则需要重新运行
prepare_review_agents.py 收集 AGENTS.md相同的祖先目录算法,内置于脚本中
获取现有的内联讨论串(30 个讨论串 / 1200 个字符)相同的 jq
/review 后的文本 = 审查重点PR URL 后的自由文本 → --focus
带分节的单文件台账一个 ledger/ 目录,每位负责人一个文件
主要风险扫描 → 1-3 个完整审查子 Agent + 聚焦风险审查 → 合并 → ≤3 轮流程相同,另有四项本地补充:每个风险项都带有一个前提检查,由主 Agent 在派发前执行(4.1);第 1 轮分两批启动,以便台账进行去重(5.1);每条发现都带有一个类别,以及相对于合并基准的回归标记;功能 / 数据 / 资源 / 性能类别中的回归严重程度至少为 Major,降级则需要书面说明以及一个严重程度质疑 Agent(6);一轮审查根据严重程度加覆盖范围收敛,而非要求“完全没有新候选项”(7)
CI 单独审查一次推送从稳定状态目录加载同一 PR 的先前运行结果,并将其作为输入读取(2.1);文档以 head sha 命名,且绝不覆盖(9)
通过 gh pr review / Reviews API 发布内联评论两份 review-docs/ 文档(EN + ZH),带有 path:line 锚点
CI 的审查结论显示在 PR 本身通过符合资格的收敛审查后,发布一条绑定到确切提交的自动 doris-repo-review/v1 评论;否则不发布任何内容
60 分钟超时没有硬性超时,但同样不要让单轮审查变成无止境的挖掘

13. 常见陷阱

  • 将“又出现了一条 Minor”视为未收敛。 无论 PR 规模多大,只要你继续查看,就会发现另一条 Minor。必须停止变化的是 结论——见步骤 7。如果连续两轮都报告“未收敛”,而阻塞性发现已在第 1 轮解决,那是评审标准有问题,不是 PR 很复杂。
  • 因为触发条件狭窄或修复只需一行,就降低回归的严重性。 严重程度取决于触发时的后果,而不是触发频率。重构移除了逐次尝试重置,就属于 functional-loss 回归,不管它是否需要“Flight + 重新规划 + 一次失败的重试”才会触发;应评为 Major,否则 PASS 回执就会掩盖已知的行为变化,而团队的修复门槛(“仅限 Blocker/Major”)会悄然漏掉它——apache/doris#67900,2026-09-13。
  • 把实际的行为变化归入宽松类别,以绕过严重性下限。 停止发出的指标属于 observability;停止发生的重置属于 functional-loss,即使目前唯一可见的症状只是一条日志也一样。类别应指出后果,而不是代码所在的文件。
  • 将 HEAD 与目标分支的最新提交比较,而不是与合并基点比较。 当 TARGET_AHEAD 不为零时,git show $BASE_SHA:<path> 显示的是 PR 分支从未包含过的代码;如果某项修复在 PR 分支分出后才合入,那么它在 HEAD 中“缺失”,并不代表 PR 删除了任何内容,正常合并也会保留它。这不是回归,不得将其评为 Major。回归证据应使用 git show $MERGE_BASE:<path>;BASE_SHA 用于差异范围和回执。
  • 在未核实前提的情况下派遣子 Agent。 步骤 4.1 的前提检查只需运行一条命令;跳过检查会浪费整个 Agent,而 Agent 返回时证明的将是你的判断有误,而不是完成了任何评审。
  • 以为第 1 轮的记录表会自动去重。 第 1 轮开始时记录表是空的。请按步骤 5.1 分两轮处理,或预先填充记录表。
  • 覆盖上一次运行的文档。 这些文件未受版本控制,因此覆盖后无法恢复,还会毁掉已考虑并排除的内容这一唯一记录。请写入 pr-<N>-review.<head7>.*,让符号链接自行更新。
  • 变基后按提交哈希比较运行结果。 重新评审的分支通常已经变基,因此 git log old..new 会重新列出整个 PR,毫无参考价值。在声称某项发现是新引入的之前,请使用 git show <old head>:<path> 比较内容。
  • 证据位于依赖项中,却只查看差异内容。 见基本规则 4a。如果某项发现的作用机制最后是“……而该库会执行 X”,就打开该库查看。
  • 本地检出版本领先于 PR HEAD。 commit check: ahead:N 表示存在尚未推送的提交,这些提交不属于本次评审范围。这是读者最容易误解的地方,因此必须在文档标题和最终报告中都说明。
  • 锚定到错误的一侧。 changed_line_ranges.txt 给出的是变更后的行号,与读取当前目录中的文件时看到的行号一致。纯删除在新版本中没有对应行——请锚定到衔接处的行,并注明 (deleted, base line N)。
  • 切换版本会使当前目录的增量构建失效。 output/、be/build_* 和 */target/ 都是按提交构建的,因此切换到 PR HEAD 再切回来通常会触发重新构建。切换前请提醒用户。
  • 未提交的更改不在差异内容中。 git diff BASE...HEAD 只会查看已提交内容。对齐操作已确保切换时没有未提交的已跟踪文件,但用户之后可能编辑过内容;worktree_status.txt 中的所有内容都必须在文档的“Coverage and Limits”部分列出。
  • 子 Agent 在记录表上发生竞争。 设置每位负责者各自的文件正是为了解决这个问题;在提示词中务必明确写出每个子 Agent 自己的文件名。
  • 重新提出已有评论的问题。 CI 会禁止这样做,本地运行也一样——请先阅读 pr_review_threads.md。
  • 不能全盘接受子 Agent 的结论。 主 Agent 必须重新阅读代码进行确认;任何无法给出具体 A 场景的“如果 A,则 B”结论,都应降级或舍弃。
  • 不要提交 review-docs/。 doris 仓库未将其加入忽略规则,自动提交会将其悄然带入 PR。
  • PASS 评论是公开的,并以用户姓名署名。 为 PR 调用此技能,即授权在达到条件且通过收敛检查后自动发表评论一次。切勿手动修改评论正文。
  • 通过不等于人工批准合并。 仓库策略可能会接受针对特定提交的回执,但它不代表 Apache 签署认可。请保留 <sub> 免责声明。

评论

支持 Markdown