关于 doris-repo-review
doris-repo-review 是面向 Apache Doris 贡献者的本地 PR 代码审查技能,基于当前克隆仓库和本地 Git 差异运行。它生成中英文审查文档,并通过校验脚本检查覆盖范围、结论与发现。
主要功能
- 01对齐当前目录到 PR head
- 02生成权威差异与审查上下文
- 03记录文件覆盖与审查过程
- 04校验中英文审查文档
- 05符合条件时发布 PASS 评论
适用场景
- 01在本地审查 Apache Doris PR
- 02检查改动文件是否均已覆盖
- 03整理并验证中英文审查结论
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ apache/ doris-skills --skill doris-repo-review安装后在 Claude Code 中输入/doris-repo-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/doc-templates.mdpr-comment-format.mdprompts.mdqualified-runtime.mdscripts/align-to-pr.shcoverage-report.shpost-pass-comment.shprepare-review-context.shrecord-review-runtime.shreview-runtime-policy.shsave-run-state.shverify-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.md | doris-repo-review/v1 评论架构、字段含义,以及程序读取该评论的方式 |
references/qualified-runtime.md | 如何选择或委派符合条件的主审查者和覆盖范围审查者 |
要求:git、已通过身份验证的 gh CLI、jq 和 python3。克隆仓库必须包含完整历史(浅克隆仓库需运行 git fetch --unshallow),因为权威差异是从合并基点生成的三点差异。
0. 基本规则
阅读源代码之前,先阅读 references/qualified-runtime.md,并确定一位符合资质的主审查员。如果要求委派,调用方应委派整个任务,自己不进行任何审查。如果没有符合资质的运行环境,只生成本地文档,不得发布等同于流水线 PASS 的评论。
- 只操作当前目录,绝不扰动本地工作。 如果当前目录有已修改的受跟踪文件,拒绝切换目录,说明情况,让用户自行提交或暂存这些更改——绝不代替用户暂存、重置或删除分支,也绝不四处寻找其他目录来开展工作。所有切换操作都使用
git checkout --detach,确保分支引用保持原样。 - 只读审查。 不得构建、运行测试或修改仓库中的任何源文件。本地只允许写入
review-docs/下的两份文档和上下文目录$CTX。 任何时候,GitHub 上最多只能写入一项内容:步骤 10 中的 PASS 评论,且仅在明确请求的审查得出收敛的 APPROVE 结果后,通过post-pass-comment.sh自动发布。REQUEST_CHANGES 审查不发布任何内容。不得发布行内评论、提交审查、添加标签、编辑 PR 正文,也绝不在正在审查的 PR 之外的任何 PR 上发表评论。 - 差异的来源只有一个:
$CTX/pr.diff和$CTX/pr_changed_files.txt。不得使用gh pr diff、Web 界面或手动编写的git diff获取更改列表——以其他方式获取意味着采用了不同的基准。 - 读取路径前先确认路径。 如果
pr_changed_files.txt、pr.diff或之前成功执行的命令输出中尚未确认某路径,先运行rg --files确认该路径。 4a. 证据可能位于仓库之外,而且你应当主动查找。 规则 3 规定了更改列表的来源;它并未规定证据的来源。当某个依赖自身的内容决定了正在审查的行为时,应阅读该依赖:解压构建实际固定的~/.m2/repository/...中的 jar 并阅读其中的资源,阅读相关语义所依赖的库的-sources.jar,阅读随仓库提供的服务定义,阅读注释所声称的契约对应的 JDK 类。只查看差异无法发现缺陷:这类缺陷的两部分分别是“被修改的代码行”和“该代码行现在会调用到的内容”——最严重的问题恰恰藏在这里。报告问题时,务必注明构件名称和版本,以便复现证据。 - 不要发现第一个阻断问题就停止。 检查所有已修改文件、相关控制流、测试,以及并行或特殊情况路径。
- 每个疑点都必须得出结论:将其作为问题报告,判定为“已由现有评论覆盖”,或以具体代码证据排除,并写入“考虑后排除”。悄悄放弃疑点意味着审查尚未完成。
- 本地没有行内评论,因此定位信息是读者唯一的入口。 每个问题都必须带有
path:line定位信息(新版本侧的行号)以及逐字摘录的代码片段,并且文档最终必须通过verify-review-docs.py的验证。
1. 对齐当前目录
CTX="<session scratchpad>/review-ctx"
$S/align-to-pr.sh <PR> --out "$CTX/align.env"
该脚本会执行四项操作,且全部仅针对当前工作树:
-
解析 PR:接受 URL、
owner/repo#N或纯数字;gh api会提供标题、状态、head 分支 / 仓库 / sha,以及 base 分支 / sha。 -
诊断一致性(用户明确要求的两项检查):
branch check:当前分支是否为 PR 的 head 分支?commit check:same/ahead:N/behind:N/diverged:a/b/unrelated将这两行原样转告用户。 特别是,ahead:N表示存在从未推送的本地提交,这些提交不属于本次审查范围。
-
获取 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 已更改”的保护机制一致)。 -
对齐当前目录:
当前目录的状态 操作 HEAD 已与 PR head 相同 ALIGN_MODE=already,无需操作干净 git checkout --detach <PR head>,ALIGN_MODE=switched存在已修改的跟踪文件 / 正在进行 rebase、merge 或 cherry-pick ALIGN_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.env | PR 信息、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 应依次阅读以下内容:
$WORKDIR/.claude/skills/code-review/SKILL.md——Doris 审查清单;整个审查过程中必须严格遵循。在较旧的分支中,若该文件不存在,请使用gh api repos/apache/doris/contents/.claude/skills/code-review/SKILL.md?ref=master获取。$CTX/required_agents.txt中列出的所有 AGENTS.md 文件,并逐个完整阅读。仅列出目录内容或搜索路径不算阅读。$CTX/pr_review_threads.md——将所有现有评论视为已知上下文。不要再次提出相同或实质上相似的问题,即使表述不同。只有当此 PR 在其他位置引入了现有评论未涵盖的真正不同实例时,才提出相似的疑虑,并说明其不同之处。 3a.$CTX/prior_runs/下的所有内容——同一 PR 之前各次审查的结论。参见第 2.1 节:沿用驳回理由及其证据;若已接受的发现现已修复,则在结尾报告中用一行说明;若之前的运行针对不同的 head,则比较内容而非提交哈希。$CTX/review_focus.txt——照常进行完整审查,并特别关注其中的要点;最终文档必须逐一回应每个要点,即使结论是“针对该要点未发现其他问题”。$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 中的覆盖审查规则:
| 类型 | 数量 | 职责 |
|---|---|---|
| 完整审查子 Agent | 1-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. 结束报告
告知用户:
-
两份文档的路径、判定结果(REQUEST_CHANGES / APPROVE)、各严重级别的发现项数量、每项标记为
Regression: yes的发现及其类别(这些是 PR 正文未声明的行为变更;作者应当声明或修复它们),以及第 7 步所述轮次是否收敛——若已收敛,说明“判定已在第 N 轮确定”;若未收敛,说明未满足的条件。即使运行结束时只剩下 Minor/Nit,也仍然算作已收敛;不要将其报告为失败。 1a.prior_runs/非空时,说明本次运行继承了什么:之前评审过哪些 head、带入了多少项驳回记录,以及这些已接受发现中有哪些已在当前 head 修复。如果没有之前的运行记录,说明这是对此 PR 的首次评审。 -
branch check/commit check的结果,尤其是ahead:N(尚未推送、因此未评审的提交)。 -
PASS 评论的处理结果:发布或更新评论时给出 URL;未发布时说明原因(REQUEST_CHANGES、未收敛、运行时条件不合格、存在未披露的回归,或 PR head 已变更)。
-
当前目录的状态:当
ALIGN_MODE=switched时,目录处于 PR head 的分离状态,执行git checkout <PREV_REF>可恢复。不要自动切回——用户可能仍想阅读代码。 -
review-docs/在 doris 仓库中未被 gitignore,因此不要自动提交;由用户自行处理。 -
结束前保存运行状态——这能降低下一次评审的成本,也让本次评审可审计:
$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 sha | align-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>免责声明。