首页
/ Deno PR Review Skill:一套五步自动化 Pull Request 审查工作流的设计与实现

Deno PR Review Skill:一套五步自动化 Pull Request 审查工作流的设计与实现

2026-09-06 20:23:03作者:冯梦姬Eddie

本文围绕 Deno 仓库内为 AI Agent 编写的 PR 审查技能定义 .claude/skills/review-pr/SKILL.md,完整拆解其“收集上下文 → 门禁检查 → 代码审查 → 类型专项检查 → 撰写评审”的五步工作流。读完后,你可以理解 Deno 项目对贡献者 PR 的具体质量要求(标题规范、测试分层、primordials、权限系统等)如何在自动化审查中被逐条落实,并能参考这套模式为任意开源仓库设计自己的 Agent 审查流程。

一、Skill 是什么:一个面向 Agent 的 PR 审查器

SKILL.md 是 Claude Code 风格的技能(skill)文件:YAML frontmatter 声明元信息,正文是给 Agent 的逐步操作指令。其 frontmatter 定义了:

  • name: review-pr:技能名;
  • description:审查 Deno 运行时 PR 的正确性、测试、安全性与规范,当被要求审查 PR 或提供 PR 编号/URL 时触发;
  • argument-hint: <pr-number-or-url>:调用时需传入 PR 编号或 URL;
  • allowed-tools: Bash(gh *) Bash(git *) Read Glob Grep Agent:仅允许 gh/git 命令、文件读取与检索类工具——即整个审查流程完全基于 gh CLI 与本地仓库文件系统完成。

该技能位于 .claude/skills/ 目录下,与 fmtlint-alllint-jsissue-triagenode-compat 等技能并列,构成 Deno 仓库内一组可复用的 Agent 工作流。下文按文档原始步骤逐一展开,并结合仓库源码核实每条规则的实际依据。

二、Step 1:收集 PR 上下文(Gather PR context)

在审代码之前,Agent 先用 gh 命令拉取四类信息,文档中给出的原始命令为(! 前缀表示执行型代码块,$ARGUMENTS 即传入的 PR 编号或 URL):

# PR 元数据:编号、标题、正文、作者、标签、状态、评审决定、提交、文件列表
gh pr view $ARGUMENTS --json number,title,body,author,labels,state,reviewDecision,commits,files,isDraft,createdAt,url

# 完整 diff
gh pr diff $ARGUMENTS

# PR 评论区内容
gh pr view $ARGUMENTS --comments --json comments

# CI 检查结果(无检查时降级为空输出,避免命令报错中断流程)
gh pr checks $ARGUMENTS --json name,state,conclusion 2>/dev/null || echo "No checks found"

这四组输出分别服务于后续步骤:元数据用于门禁 6(外部贡献者需关联 issue)与合并就绪评论;diff 是代码审查的输入;评论区用于理解讨论上下文;CI 结果直接决定门禁 1 是否通过。

三、Step 2:六道门禁检查(Gate checks)

文档明确要求:任何门禁失败时,必须把问题显著地列在评审意见最顶部,且不得 approve。六道门禁如下,每一条都能在仓库中找到对应的执行依据。

1. CI 状态

所有检查必须通过;失败时应指出具体是哪个检查挂了。标注为 ci-test-flaky 的已知不稳定测试可以重跑。这一门禁与 CLAUDE.md 中 “PR 必须经 CI 合入 main” 的标准 git 工作流一致。

2. PR 标题格式

标题必须遵循 type(scope): description。技能文档列出的类型包括 featfixperfrefactorchoredocstestrevertBREAKING,scope 示例如 ext/nodeext/fetchclilspruntime

这一规则在 CI 侧由 tools/verify_pr_title.js 强制校验。从源码结构看,实际被接受的合法前缀比技能文档列出的更宽,还包括 cicleanupbenchbuildRevert Reland (用于在 changelog 中标记被回退后又重新合入的提交),以及形如 x.y.z 的发布 PR 标题;另外该脚本还有一条专门针对 chore: ... upgrade deno_core/v8 的细化规则——要求把 deno_core/V8 升级归类为 feat:fix:refactor:,并在标题中说明修复/新增的具体问题,而不是一句 fix: upgrade deno_core。PR 模板 .github/PULL_REQUEST_TEMPLATE.md 也给出了好/坏标题对照示例(如 fix(ext/net): fix race condition in TCP listener 为合格,fix #7123update docs 为不合格)。

3. 禁止 force push

Deno 采用 squash-merge,贡献者应持续追加新提交而非重写历史。CLAUDE.md 的 Git workflow 一节对此有原文级说明:追加提交“allows reviewers to see the incremental changes you made in response to feedback”,评审人因此能看到针对反馈的增量修改。

4. 聚焦的改动范围

不允许夹带无关的顺手清理(drive-by cleanups),那些应拆到独立 PR。CLAUDE.md 同样规定 “Keep your changes minimal, don't do drive-by changes in a PR”。

5. AI 使用披露

如果 PR 疑似 AI 生成(模板化措辞过多、注释泛泛、改动范围可疑地广)但没有任何披露,应主动询问。仓库侧有硬性要求:PR 模板 .github/PULL_REQUEST_TEMPLATE.md 顶部注明 “If you used AI tools ... you MUST disclose it in the PR description. PRs will be rejected if there is suspicion of undisclosed AI usage.”——门禁 5 是对该模板条款的人工/Agent 复核。

6. 外部贡献者需关联 issue

若作者不是 denoland 组织成员,PR 必须链接到一个 issue;没有时应当 request changes,要求作者先开 issue 讨论方案。PR 模板第 2 条(“Ensure there is related issue and it is referenced in the PR text”)与此呼应。

四、Step 3:代码审查准则

文档要求“读遍 diff 中每一个被修改的文件”,并在需要时用仓库工具(ReadGrepGlob)理解周边上下文。审查标准按语言与关注面分为四组。

Rust 代码

  • 正确性:边界情况是否处理?对用户可控数据不能 .unwrap()
  • 错误处理:合适的错误类型、有意义的错误消息、不吞掉错误;
  • 性能:无不必要的分配/拷贝,async 代码中不阻塞;
  • 安全性:无强理由不得 unsafe;杜绝命令注入、路径穿越、权限绕过;
  • 权限:新增能力必须走 Deno 的权限系统;要特别盯防 ext/node/——Node.js API 有时假设拥有完全访问权。权限系统的核心实现在 runtime/permissions.rs,这也是下一步“安全敏感区”列出的第一个文件;
  • 依赖:新增 Cargo 依赖必须有强理由,优先复用现有依赖或标准库。Deno 的 crate 划分(cli/runtime/ext/libs/)可在根 Cargo.tomlCLAUDE.md 的 “High Level Overview” 中对照理解。

JavaScript / TypeScript 代码

  • Node.js 兼容性ext/node/):实现必须与 Node.js 的实际行为一致,需对照 Node.js 文档甚至源码,而不是“看起来对”;
  • Primordials:内部 JS 应使用 primordials(globalThis.__bootstrap.primordials)以避免原型污染——对用户可控对象不得直接调用内置方法,必须经 primordial 包装。这在仓库中有完整的实现与执法链条:
    • libs/core/00_primordials.js 是 primordials 的源头实现,文件头注明其思路基于 Node.js 的同名内部模块,并按构造函数/迭代器等方式批量构造 ArrayPrototypeIncludesSafeMap 等安全引用,注释还提到“对性能有显著影响,应优先使用”;
    • tools/lint_plugins/prefer_primordials.tstools/lint.js 专用的自定义 lint 插件,定义 prefer-primordials 规则并维护一份 GLOBAL_TARGETS 黑名单(JSONMathArray 等全局),在 runtime/ext/ 的引导代码上强制该约定;
    • 实际用法可对照 ext/node/polyfills/01_require.js:其头部即从 ext:core/mod.js 解构 primordials 并引入 ArrayPrototypeIncludesObjectGetOwnPropertyDescriptor 等安全引用,而非裸调 Array.prototype.includes
  • Web 标准:Web API 实现应遵循相应 spec,优先补充 WPT 覆盖(WPT 相关测试组织在 tests/wpt/);
  • 懒加载:所有代码应尽可能使用 lazy-loaded imports 以降低启动开销。

测试

  • 每个 bug fix 都必须附一个“能抓到这个 bug”的测试;每个 feature 需要 happy-path + 边界用例;
  • 测试层级优先级:单元测试 > spec 测试 > 集成测试,只有当行为必须依赖 CLI 级验证时才使用 spec 测试;
  • Spec 测试位于 tests/specs/,以 __test__.jsonc 声明测试步骤;非确定性输出用 [WILDCARD],顺序不确定的输出用 [UNORDERED_START]/[UNORDERED_END] 包裹;
  • 测试必须确定性:无竞态、无计时依赖、无端口冲突。

这部分规范在 CLAUDE.md 的 “spec tests” 章节有更详细的展开,包括 __test__.jsonc 的完整 schema(“tests” 对象、args/steps/output 字段)、期望文件 .out 的匹配语言([WILDCARD][WILDLINE][WILDCHAR][WILDCHARS(5)][UNORDERED_START]/[UNORDERED_END][# 注释]),以及测试目录划分(tests/specs/tests/unit/tests/integration/tests/wpt/)。评审时可直接引用这些标准判断新增测试是否规范。

安全敏感区

文档列出的需加倍警惕的改动位置:

  • runtime/permissions.rs 及散布各处的权限检查;
  • ext/net/ext/fs/ —— 网络与文件系统访问;
  • ext/node/ —— Node 兼容层需要自己补权限检查(因为上游 API 假设完全访问);
  • cli/tools/compile.rs —— 独立二进制(standalone binary)编译;
  • 任何 shell 外呼或处理用户可控路径/URL 的代码。

五、Step 4:按 PR 类型的专项检查

在通用标准之上,文档要求识别 PR 类型并追加专项检查:

  • Node.js 兼容ext/node/):必须对照 Node.js 文档/源码验证行为。新增 polyfill 必须注册进 ext/node/polyfills/01_require.js——从源码看,该文件是 require 机制的核心(3500+ 行),集中导入 op_require_* 系列 ops 并实现模块解析、CJS/ESM 判定等逻辑,新内置模块不在此注册就无法被 require 到;
  • 性能:必须附带改动前后的 benchmark 数据,或给出清晰的收益论证,同时警惕正确性回退;仓库在 cli/benches/tests/bench/ 等目录维护基准,PR 模板还支持添加 ci-bench 标签在 CI 上跑基准;
  • 依赖更新:检查 changelog 中的破坏性变更,安全类更新优先处理;
  • WPT 改动:确认“通过”是真通过而非断言被跳过;expectation 文件的更新必须与实际结果一致;若未打标签,建议补上 ci-wpt-test
  • CI/发布工具:必须 @ 维护者 @bartlomieju 审查,Agent 不得自行 approve。

六、Step 5:撰写评审意见

结构

文档规定评审意见固定分四段:

  1. Summary(1–2 句):PR 做了什么、总体评价;
  2. Gate issues(如有):必须修复的阻塞问题;
  3. Code comments:具体、可执行的反馈,指向精确的文件与行号;非阻塞建议用 nit: 前缀;尽量给出修复方案而非仅说“这里错了”;
  4. Verdict:approve / request changes / comment。

语气

  • 直接:说 “This needs a test”,而不是 “It would be wonderful if we could add a test here.”;
  • 友善:感谢贡献者,尤其首次贡献者,假定善意;
  • 有帮助:拒绝时展示“好的版本”应该是什么样;
  • 简洁:如果贡献者明显经验丰富,就长话短说。

提交评审

优先在具体行内联评论,一次 review 同时包含摘要正文与内联评论:

gh api repos/denoland/deno/pulls/{number}/reviews \
  -f event=COMMENT \
  -f body="summary" \
  -f comments='[{"path":"file.rs","line":42,"body":"comment"}]'

结论为通过或要求修改时,将 event=COMMENT 换成 event=APPROVEevent=REQUEST_CHANGES。若无需内联评论的简单评审,退回 gh pr review $ARGUMENTS --comment --body "review text"

合并就绪(Merge readiness)

Agent 没有合并权限。PR 就绪时按贡献者身份发不同评论:

  • 首次贡献者:@bartlomieju LGTM, needs maintainer signoff (first-time contributor)
  • 常规贡献者:@bartlomieju this is ready to merge

这解释了为什么 CI/发布类 PR 也要交给同一维护者把关——最终合并权集中在人工维护者一侧。

七、硬性规则(Rules)与能力边界

文档末尾列出 Agent 审查的不可越界项:

  • 绝不 approve 一个 CI 失败的 PR;
  • 绝不 approve 绕过权限系统的 PR;
  • 大型架构变更必须在拉出给维护者讨论之后才能推进;
  • 不要为 lint 已经放行的风格问题纠缠(bikeshed);
  • 不要对自动化检查已强制的事项重复 request changes;
  • 任何发布到 GitHub 的评审评论,必须先与用户确认——这是 Agent 侧的最后护栏,保证自动化审查不会产生未经授权的公开行为。

八、小结

.claude/skills/review-pr/SKILL.md 把 Deno 的社区贡献规范沉淀成了一条可执行的 Agent 流水线:它用 CLAUDE.mdtools/verify_pr_title.js.github/PULL_REQUEST_TEMPLATE.md 等仓库内的既有约定作为事实来源,用 gh CLI 完成信息获取与评论发布,并用 libs/core/00_primordials.jstools/lint_plugins/prefer_primordials.tsruntime/permissions.rs 等核心源码作为审查深度的锚点。对读者而言,它既是理解 Deno 工程文化的窗口(标题规范、测试分层、primordials、权限优先、squash-merge 与集中式合并权),也是一份可直接借鉴的模板:如何把“项目价值观”翻译成 Agent 能逐步执行、且每步都有仓库证据支撑的审查清单。

登录后查看全文
热门项目推荐
相关项目推荐