关于 double-check
分阶段执行 GitHub Pull Request 二次审查的技能,涵盖准备、独立审查、修复和发布结论。它核验先前意见并检查当前差异,且仅在提交与审查版本匹配时发布评论和标签。
主要功能
- 01四阶段顺序审查
- 02隔离式关键审查
- 03基于 patch-id 延续结论
- 04针对新提交审查差异
- 05精确提交版本发布门槛
适用场景
- 01复核先前的 PR 审查
- 02审查修复后的变更
- 03差异未变时沿用审查结论
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fellowship-dev/ dogfooded-skills --skill double-check安装后在 Claude Code 中输入/double-check确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CONTEXT.mdreferences/follow-up-issue-template.mdshared/exact-head-receipt.shreport-template.mdreview-comment-template.mdstages/01-setup/CONTEXT.mdstages/02-review/CONTEXT.mdstages/03-fix/CONTEXT.mdstages/04-post/CONTEXT.mdtests/base-merge-no-push.test.shexact-head-receipt.test.shpatch-id-carry.test.shspeed-contract.test.shtest_installed_resources.pytests/fixtures/pr-3179-stage03-to-stage04.json详细介绍
double-check
目的
独立的第二轮 PR 审查,按阶段拆分为 ICM 流程。获取 PR、第一轮审查和差异,并检出代码(setup),随后在干净的上下文中进行一次连贯的审慎评审(核实第一轮审查的结论、查找遗漏的边缘情况,并结合检查测试和文档 → 得出单一的综合结论),然后在需要时应用修复并重新运行测试(fix),最后发布整理后的评论、应用 double-checked 标签并撰写报告(post)。行为上等同于原始的 double-check 技能,只是将其隔离,避免评审判断受到编排器历史记录的影响。
参数
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
pr | 是 | — | PR 编号,例如 742 |
repo | 是 | — | org/repo,例如 fellowship-dev/booster-pack |
从 $ARGUMENTS 解析:第一个 token 是 pr,第二个是 repo。
GitHub 身份验证由环境提供 — 无需 token 环境变量。pod 的 git-credential-pylot 辅助程序和 gh
shim 会为每项操作生成短期 App 安装令牌,因此 git URL 必须保持为普通形式
(https://github.com/<org>/<repo>.git);内联凭据会绕过辅助程序,并在运行过程中途失效。
功能说明
4 阶段顺序执行的 ICM 流程(无并行阶段):
| 阶段 | 模式 | 说明 |
|---|---|---|
| 01-setup | 子 Agent | 获取 PR 元数据,包括当前 HEAD;将收到的第一轮审查回执(其 Head reviewed 行)归类为 current/stale/absent;收集评论和完整差异;检出 PR 分支和合并基准 |
| 02-review | 子 Agent | 在干净的上下文中进行一次连贯的审慎评审:核实第一轮审查的结论、查找遗漏的边缘情况、检查测试和文档 → 得出综合结论(仅当没有未解决的 MUST-FIX 代码问题时才为 ready)和整理后的发现 |
| 03-fix | 子 Agent | 应用 MUST-FIX(以及值得做的 NICE-TO-HAVE)修复,重新运行测试并推送 — 仅在需要修复时执行 |
| 04-post | 内联 | 重新获取当前 head;如果它与阶段 02 审查的确切 40 位十六进制 head 相同,或携带相同的 patch-id,则晋级;否则执行一次重启。随后发布整理后的审查评论,并且仅针对匹配的 head 应用标签 |
Patch-id 延续与差异审查(pylot#3738)
每份结论回执都会记录 head 和 PR 的 patch-id
(git diff base...head | git patch-id --verbatim)。仅 head 发生变化不会导致审查失效:
- Patch-id 相同(变基、从基准分支合并):结论和
double-checked会延续到新 head,并附上一行“结论已延续:patch-id 未变化”的说明。Setup 会报告review_scope: carry,跳过阶段 02 和 03,阶段 04 执行延续分支。 - 新增提交(返工、阶段 03 修复):阶段 02 只审查自上次结论以来 hunks 发生变化的文件,
以及这些改动旨在修复的发现(
review_scope: delta)。如果 PR 中超过 30% 的文件发生变化,或无法找到可达的先前结论,则进行完整审查。 - 冲突解决导致差异变化会产生新的 patch-id,因此始终需要重新审查。
交接位置
所有交接文件都位于仓库工作目录中:
.procedure-output/double-check/{stage}/handoff.md
Setup 阶段会在交接文件中记录本地检出目录(REPO_DIR),以便 fix 阶段在同一工作树中操作。每个子 Agent 阶段只会收到所需的交接文件 —
而非完整的编排器上下文。
执行
在更改工作目录之前,使用已加载的 SKILL.md 所提供的资源路径,将
DC_SKILL_DIR 解析为其所在目录的绝对路径。对阶段说明、模板和辅助程序使用同一根目录;不要根据已审查仓库的 cwd 推断该目录,也不要优先选择其他已安装版本。明确将其传递给每个阶段,并在该阶段的 shell 中导出。如果无法获取已加载资源路径,请先停止并解析该路径,再运行各阶段。
精确头部周期(顺序子代理)
每个阶段运行一个 Task,依次执行。不要并行启动任何阶段。当前阶段完成前,不要启动下一阶段。以 restart_count=0 开始。如果阶段 04 发现完整远程 SHA 不同且 patch-id 不同,则停止,不发布判定,也不触碰标签;协调器在观测到的 SHA 上运行一个完整的条件式 01 → 02 → 03 → 04 周期,并设置 restart_count=1,将 delta_base_head(上一周期审查的头部)传给阶段 01,并先将上一阶段 02 的交接文件移至 .procedure-output/double-check/prior-review.md,这样新周期只审查增量及其修复的发现。阶段 02 仍只接收新的设置交接文件和工件:绝不接收编排历史。若再次发生转换,或实时头部无法读取,则停止并阻塞。头部移动但 patch-id 相同不算转换:阶段 04 保留判定。
每个 Task 提示词都必须自包含:
- 仅包含该阶段的输入交接文件路径
- 包含该阶段
CONTEXT.md的路径 - 传入
pr和repo值 - 不要传入编排历史或先前推理
Task 提示词模板:
You are running stage {NN}-{name} of the double-check procedure.
PR: {pr} REPO: {repo}
DC_SKILL_DIR: {absolute skill directory}
Export DC_SKILL_DIR to this absolute path before executing shell snippets.
Read your stage instructions:
{DC_SKILL_DIR}/stages/{NN}-{name}/CONTEXT.md
Your inputs:
{list only the input handoff paths from that stage's CONTEXT.md}
Write your output to:
.procedure-output/double-check/{NN}-{name}/handoff.md
Execute all steps in CONTEXT.md. Write handoff.md before exiting.
阶段门控:
- 阶段 02 是独立的关键判断步骤。其提示词必须仅包含设置交接文件(PR + 首次审查 + 差异)——不得包含其他内容。这是整个流程存在的目的:提供干净的上下文窗口。
- 阶段 01 完成后,读取其交接文件。如果
review_scope: carry,则跳过阶段 02 和 03,运行阶段 04 的 carry 分支(自ready判定后,PR 自身的差异未发生变化)。 - 阶段 02 完成后,读取其交接文件。如果
fixes_needed: false,则跳过阶段 03(无需应用修复),直接进入阶段 04。否则运行阶段 03。
阶段 04(内联)
在协调器上下文中自行运行阶段 04——不要派生 Task。读取 CONTEXT.md:
{DC_SKILL_DIR}/stages/04-post/CONTEXT.md
运行实时精确头部门控(gh pr view),然后仅当头部匹配时才发表评论并应用标签。验证标签和评论确实已生效,写入报告文件,并由协调器发出 [pylot] outcome=... 标记(绝不能由子代理发出)。
如果阶段 04 以 3 退出,则设置 RESTART_COUNT=1,并在当前实时头部上运行新的完整条件式阶段 01 → 02 → 03 → 04 周期;不要传入旧的阶段 02 或阶段 03 交接文件。如果以 2 退出,则表示终止阻塞:不要运行晋级路径。
阶段交接链
01-setup ─► 02-review ─► 03-fix ─► 04-post (inline, reads 01+02+03)
│ ▲
└── fixes_needed:false ┘ (skip 03)
退出路径
- 首次检查失败并默认关闭(至少有一个未解决的 MUST-FIX 代码项):stage 04 会发布一条
`` 评论,移除/不添加
double-checked,添加或 保留needs-work,且不会创建任何正向后续操作。它会发出:[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-check {repo}#{pr} — verdict {verdict}, double-checked withheld, needs-work retained" status=success - 重新检查通过(PR 带有
needs-work,verdict=ready):stage 04 会移除needs-work,重新切换double-checked(移除后重新添加),并发出:[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-checked re-check PASS {repo}#{pr} — loop closed, cto-review re-fired" status=success - 重新检查失败(PR 带有
needs-work,且至少有一个未解决的 MUST-FIX 代码项):stage 04 会保留needs-work, 不会重新切换double-checked,会发布一条带有 `` 标记的结构化 verdict 评论(幂等——若标记已存在则跳过),并发出:[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-checked re-check FAIL {repo}#{pr} — needs-work retained" status=success - 首次检查成功:只有在精确匹配当前 40 位十六进制 head 时,明确给出
readyverdict 才能 应用double-checked;它会发出:[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-checked {repo}#{pr} — verdict ready, {N} findings curated, {N} fixes pushed" status=success - 沿用 verdict(
review_scope: carry):stage 04 会发布一行沿用说明,保留 标签,并发出:[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-check {repo}#{pr} — verdict carried: patch-id unchanged, no re-review" status=success - 失败:失败的 stage 会发出
[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-check failed at stage NN: {reason}" status=failed - 受阻:设置过程无法获取/检出 PR(例如合并冲突、PR 缺失)、无法读取当前
head,或发生第二次 head 转换 →
[pylot:$PYLOT_OUTCOME_NONCE] outcome="double-check blocked: {reason}" status=blocked(这是有意停止——blocked是独立的终止状态,并非失败)
返工跟进模式
是否存在“产出修复”跟进派发,以及如何派发,属于仓库策略,而非协议:有些组织会在此技能之上配置 CTO 返工自动化,有些则不会。阅读仓库操作手册(GET /admin/playbooks/<org>/<repo>),查找返工派发部分;如果其中定义了约定,内容会类似这样:
当先前的审查结果为
needs-work时,门禁规则会将任务派回此技能,并通过任务字符串标明这是 PRODUCE 跟进(而非重新评判)。操作手册会指定要匹配的确切触发字符串/标签,以及自动化的身份——请将它们视为下文已确定的值,而不要在此处硬编码字面文本。
在此约定下收到派发时,请在运行上述阶段之前执行以下操作(操作手册是触发条件和规则名称的准确信息来源——请使本协议与其保持同步,避免两者不一致):
- 先扫描 PR 的评论。如果先前的尝试已经发布了
⚠️ NEEDS HUMAN标记,或者先前的返工已经在同一个门禁上尝试但失败,就不要循环处理——留给人工处理并停止。切勿在未产出任何内容的情况下重新检查并再次应用needs-work。 - 阅读最新的 CTO 审查评论,了解其中的具体待办事项。
- 实际解决这些事项。代码/测试/文档修复:进行更改并推送。要求编辑 PR 正文或提供暂存环境证据不属于返工事项:暂存是发布列车步骤(pylot#3389),而正文不会决定审查结论。如果没有剩余代码事项,就运行下述阶段;MUST-FIX 项为零即为通过。如果请求确实超出此技能的范围(需要专用运行器、人工决策,或你不具备的凭据),请勿悄悄地重新应用
needs-work——发布一条以⚠️ NEEDS HUMAN开头的评论,明确说明阻碍因素及解除阻碍所需的条件,然后停止。 - 只有在实际解决这些事项后,才运行
gh pr edit <number> --repo <repo> --remove-label "double-checked,needs-work",然后运行上述阶段,以重新触发标签链。设置流程会将返工后的差异与上次审查结论的 patch-id 进行比较,因此审查只涵盖返工增量及其解决的发现项(如果增量涉及的文件超过 30%,则进行完整审查)。
操作手册中没有返工派发部分? 此模式不适用——按正常的审查/修复/发布流程运行上述阶段。
硬性规则
- 只能顺序执行 —— 每个阶段只启动一个 Task,依次运行。绝不并行启动 Task。
- 评审是一个连贯的阶段 —— 不要将阶段 02 拆分为按文件或按维度进行的子代理。正确性、边界情况、测试、文档、依赖项和安全性必须一并评判,给出单一结论。
- 阶段 02 使用干净的上下文 —— 只传入设置阶段的交接内容及其产物文件(PR 正文、首次评审、变更文件、diff)。绝不要将编排器历史传入其中。
- 阶段 04 内联运行 ——
[pylot] outcome=...标记必须由编排器发出。 - 绝不将完整的编排器上下文传入子代理 Task 提示词 —— 仅传入输入内容。
- 每个阶段都必须在下一阶段读取前写入 handoff.md。
- 不得跳过阶段,除非
fixes_needed: false时跳过阶段 03(这是明确允许的例外)。 - 禁止使用 Quest。 仅通过本地报告文件进行报告——不得 POST 到 Quest,不得使用
127.0.0.1:4242、quest.fellowship.dev或QUEST_TOKEN。 - 仅在评论成功发布后才应用标签(阶段 04)。重新检查通过时,先移除
needs-work,再添加double-checked——这是打破结构性循环的关键。重新检查未通过时,不得触碰标签或重新切换double-checked。 - 由代码决定结论;没有 MUST-FIX 即通过。 仅当没有尚未解决的 MUST-FIX 代码问题时,结论才是
ready,首次检查和重新检查均如此;阶段 04 会将must_fix_open: 0的needs-work规范化为ready。PR 正文仅表达意图:不进行声明核对,过时的正文至多只需加一行说明。(这取代了 pylot#2649 的声明门槛:在一轮 30 天的检查中,其大多数needs-work标记都是由正文过时造成,而 diff 本身没有问题;每次都因此产生返工。) - 阶段 04 必须验证自身副作用 ——应用标签后,使用
gh pr view查看 PR,并确认预期的标签和评论确实存在。未验证副作用就报告成功,正是这项技能旨在防止的问题。 - 整理首次评审,绝不盲目重新推导 ——设置阶段逐字捕获首次评审评论及其
Head reviewed回执行;阶段 02 按层级调整评审深度,且只增不减。未找到首次评审时,执行全深度的新评审。 - 过时的首次评审是历史证据,不是阻碍,也不代表当前覆盖范围 ——将其
head_sha与变基后的 PR HEAD 进行比较。针对完整的当前 diff 继续进行连贯评审,重新检查此前的问题,并发布新的当前 HEAD 回执。绝不要仅因收到的回执已过时,就重启整个流程。 - 晋级必须绑定最终完整 SHA ——每次发布结论评论或修改标签前,立即获取
headRefOid。40 个字符的实时 SHA 必须与阶段 02 的reviewed_head_sha相同,或与已评审 diff 具有相同的完整 patch-id(此时实时 HEAD 成为晋级后的 HEAD);否则必须安全失败。首次 patch-id 变化时,重新开始增量评审;第二次变化或获取失败时,停止并阻止晋级。文件列表、diff 统计、短 SHA 或本地 HEAD 均不能替代 SHA 或 patch-id 的相等性。运行结果过时或被阻止时,绝不能修改double-checked,也不能触发下游自动化。 - 首次检查的晋级必须明确为正向 ——只有评审结论恰为
ready且绑定到完全一致的实时 HEAD 时,才应用double-checked。任何负面、缺失、格式错误、过时或相互矛盾的信号都必须安全失败:移除或不添加double-checked,添加或保留needs-work,并且不得创建 CTO、FlowChad、staging 或合并后续流程。 - 成功标记只能来自阶段 04 的模板(pylot#3392)。 阶段 04“发出 outcome 标记”部分中记录的五个
status=success标记(四个结论分支和一个延续分支)是完整集合。绝不能由编排器自行编造自由格式的成功标记;除非阶段 04 已完整运行——回执评论已发布且后续验证已通过——否则绝不能发出任何status=success。2026-09-05,编排器未运行阶段 04 就发出了“queued-worker result ok”:任务被终结为done,却没有回执,已完成的工作也因此约 6 小时不可见。如果无法运行阶段 04,结果必须是status=failed或status=blocked,并说明原因——伪造成功是最糟糕的退出方式。 - 逐字产物必须由 shell 写入,绝不能重新键入。 阶段 01 将 PR 正文、首次评审、清单和 diff 重定向到与其 handoff 同目录的文件中。通过 Write 工具复制这些内容是设置阶段最大的耗时来源,而且可能在不知不觉中偏离源内容。
- 等待进程,不要等待时间。 长命令(测试、带钩子的
git push)应以前台方式运行并使用工具允许的最长超时;或在后台运行、记录其 PID,并进行阻塞式等待,在命令退出时立即返回(参见阶段 03“等待长命令”)。绝不要使用 30 秒或更长的固定sleep N轮询:实测中最长曾在进程完成后仍多等待 10 分钟。 - 验证遵循所属仓库的约定。 阶段 03 根据修复增量及受影响的使用方,选择有意义的检查,并保留必要的广泛/全量、依赖项、转换、集成和发布门槛。只有当前仓库策略和已配置的工作流明确规定某项门槛由 CI 负责时,才可由 CI 承担。只有在策略允许复用且范围/环境一致时,才避免在同一修订版本上重复运行;如有变更、失败或未解决的问题,必须重新运行。绕过钩子必须获得仓库的明确许可,并证明所有被绕过的检查均已通过。阶段 02 的独立评审和阶段 04 的精确 HEAD 验收仍是必需步骤;聚焦测试不能免除任何一项。
参考文件
CONTEXT.md— 架构概述stages/NN-name/CONTEXT.md— 各阶段的输入、任务和输出约定shared/review-comment-template.md— 精选的 PR 评论模板(阶段 04)shared/report-template.md— 本地报告文件模板(阶段 04)