关于 triage-prs
triage-prs 是用于整理开放 GitHub PR 并推进合并的技能,通过可续接的计划记录进度。它面向准备发布的维护者,提供隔离审查、分类处理和后续跟踪流程。
主要功能
- 01收集开放 PR 的元数据
- 02按合并优先级分类 PR
- 03在分流文档中跟踪进度
- 04审查、变基并压缩合并 PR
- 05处理已被取代及部分 PR
- 06记录后续修复和贡献者署名
适用场景
- 01准备合并积压 PR 的版本发布
- 02恢复中断的 PR 分流会话
- 03区分低风险合并与需深入审查的改动
- 04从部分被取代的 PR 中应用选定改动
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jamiepine/ voicebox --skill triage-prs安装后在 Claude Code 中输入/triage-prs确认已加载。
详细介绍
PR 分类处理
目标
在一次专注的工作会话中,将积压的开放 PR 转化为一批已合并的成果。先制定一份可跟踪、可恢复的计划(<VERSION>_PR_TRIAGE.md),然后执行它——在需要时进行 rebase,按隔离安全的批次合并,应用合并后的后续操作,并关闭已被取代或部分适用的 PR,同时向其作者致谢。
本技能与 draft-release-notes 和 release-bump 搭配使用:先进行分类处理,然后针对新的 main 起草发布说明,最后发布版本。
何时使用
- 在次要或主要版本发布之前,当已积累 10 个以上开放 PR 时
- 当你希望在不丢失落地内容脉络的情况下解除合并阻塞时
- 当你清楚自己无法亲自深入审查每个 PR,但需要快速落地关键子集时
前提条件
- 已针对仓库完成身份验证的
gh命令行 - 用于 PR 审查的专用 worktree(避免用贡献者分支的检出污染
main) - 明确目标版本——分类处理文档以它命名(例如
0.4.0_PR_TRIAGE.md)
工作流
1. 设置隔离的 PR 审查 worktree
git worktree list # check for stale ones first
git worktree prune
git worktree add ../voicebox-pr-review -b pr-review-<VERSION> main
将主 worktree 保留用于发布准备工作(变更日志草稿、直接提交到 main 的后续操作)。将审查 worktree 保留用于 gh pr checkout——每次检出都会将 HEAD 移动到贡献者分支,而你并不希望在主 worktree 中这样做。
2. 收集每个开放 PR 的元数据
gh pr list --state open --limit 50 --json \
number,title,author,isDraft,mergeable,mergeStateStatus,files,additions,deletions,reviewDecision,statusCheckRollup,maintainerCanModify \
--jq '.[] | {num: .number, title, author: .author.login, mergeable, state: .mergeStateStatus, canModify: .maintainerCanModify, changes: "+\(.additions)/-\(.deletions)", files: [.files[].path]}'
对于每个 PR,你需要:
- 规模(
+additions/-deletions) - 可合并状态(
CLEAN、UNSTABLE、DIRTY= 冲突、UNKNOWN= GitHub 仍在计算) - 是否允许维护者编辑该分支(如果你代表作者进行 rebase,稍后需要此项)
- 涉及的文件路径(有助于发现 PR 之间的重叠)
UNKNOWN 在推送到 main 之后很常见——直接尝试合并看看即可。
3. 分类为不同层级
将每个 PR 归入且仅归入一个类别:
第 1 层——合并: 规模小、可合并、修复真实 bug、CI 干净、审查成本低。单行修改、依赖放宽、针对性安全加固。这些是轻松取胜的部分。
第 2 层——候选,待审查: 中等规模(50-200 行)、涉及更多表面区域、看起来合理但需要更仔细阅读。符合产品方向的新用户可见功能。
取代: 该修复或功能已被某个已合并的内容覆盖。关闭并附上指向取代它的 PR 的评论。仔细检查——“标题相似”不是证据;比较实际 diff。
推迟到下一版本: 大型功能、脏冲突、草稿 PR、任何以会引入风险的方式触及发布流水线的内容。不要在快速冲刺中合并这些——它们需要专门的关注。
4. 编写分类处理文档
在 PR 审查 worktree 根目录创建 <VERSION>_PR_TRIAGE.md。结构如下:
# <Repo> <VERSION> — PR Triage
Working doc for tracking which open PRs land in <VERSION>. Delete after release cut.
Last updated: <DATE>
## Progress
**Tier 1: 0 / N merged**
**Tier 2: 0 / M handled**
**Supersede triage: pending**
---
## Merge for <VERSION> — critical bug fixes
| PR | Status | Size | What it fixes | Why must-have |
|---|---|---|---|---|
| [#123](url) | [ ] | +5/-0 | ... | ... |
## Strong candidate — needs a quick review
| PR | Status | Size | Summary |
|---|---|---|---|
## Close as superseded
| PR | Status | Reason |
|---|---|---|
## Defer to <NEXT_VERSION>
- [#xxx](url) ... — reason
---
## Order of attack
1. Close superseded PRs (one-liner comments)
2. Merge tier-1 in dependency-free batches — check file paths don't overlap
3. Review tier-2 individually
4. Rerun `draft-release-notes` to pick up everything
5. Run `release-bump`
Progress 标头是最重要的部分——它是你的记分板,让你在会话被中断时能够干净地恢复。
5. 按 PR 逐个执行循环
对于 tier-1 / tier-2 列表中的每个 PR:
a. 在 review worktree 中检出:
cd ../voicebox-pr-review
git checkout pr-review-<VERSION> # reset to neutral base
gh pr checkout <N>
b. 读取实际的提交,而不是 main..HEAD:
git show HEAD # the PR's actual changes
git show --stat HEAD # files touched + line counts
不要通过 git diff main..HEAD 来审查,如果该 PR 分支比 main 旧。该 diff 会把PR 分叉之后落到 main 上的每一个提交都算作 -(删除)行。一个 3 行的 PR 看起来可能像 700 行的回退。这是最容易误判 PR 的方式。
c. 评估关注点: 正确性、范围、与已合并工作的交互、版本兼容性(例如不能使用需要某个我们尚未固定版本的依赖项的 API)。
d. 如果分支落后于 main,则 rebase:
git fetch origin main
git rebase origin/main
这在 squash 合并之前是必不可少的。GitHub 的 squash 计算的是 diff(PR-head, merge-base) —— 在一个过时的分支上,该 diff 会包含回退中间每一个提交。Rebase 会把 merge-base 向前移动,使 squash 干净。
e. 如果允许维护者编辑,把 rebase 结果推回贡献者的 fork:
git remote add <author> https://github.com/<author>/<repo>.git
git fetch <author> <branch> # get their ref first
git push <author> HEAD:<branch> --force-with-lease
这能让 GitHub 的 PR UI 与 rebase 后的状态保持同步,并使从 GitHub 侧的合并干净。
f. 合并:
gh pr merge <N> --squash
g. 更新 triage 文档 —— 把复选框改为 ✅ merged <sha>(使用来自 gh pr view <N> --json mergeCommit --jq '.mergeCommit.oid[0:7]' 的短 SHA)。更新 Progress 标题。
6. 批量处理小修复
变更行数 ≤5、CI 干净、文件路径不重叠且意图明显正确(例如放宽一行依赖、添加环境变量、修复 import 路径)的 PR,可以在一个循环中合并,无需逐 PR 的审查仪式:
for pr in 425 384 416 429; do
echo "=== Merging PR $pr ==="
gh pr merge $pr --squash
done
之后验证每一个都干净落地:
for pr in 425 384 416 429; do
gh pr view $pr --json state,mergeCommit --jq "{pr: $pr, state, sha: .mergeCommit.oid[0:7]}"
done
7. 合并后的后续跟进
有时一个 PR 尽管存在已知的小问题(例如不完整的 dtype 映射、过时的 sentinel 清理)仍值得合并。不要阻塞合并;紧接着以普通分支 + PR 的方式应用后续跟进:
cd <main-worktree>
git pull --ff-only origin main
git checkout -b fix/<short-name>
# edit...
git commit -m "fix(<area>): <one-liner>"
git push -u origin fix/<short-name>
gh pr create --title "..." --body "Follow-up to #<N>. ..."
在 triage 文档中记录两个 SHA(✅ merged <pr-sha> + follow-up <pr>)。
直接提交到 main 的例外: 仅在明确、有限定范围的策略下(例如"release speedrun")。不要默认采用。
8. 取代:以指向功劳的评论关闭
gh pr close <N> --comment "Closing — superseded by merged #<M> which landed <brief description>. Thanks!"
先检查 diff —— 仅"标题相似"是不够的。如果该 PR 被部分取代(诊断正确,但只有一半的改动仍然需要),则改为部分应用。
9. 部分应用模式
当一个 PR 同时捆绑了有价值和有问题的改动时:
cd <main-worktree>
git pull --ff-only origin main
# Cherry-pick specific files from the PR branch
git checkout <pr-commit-sha> -- <file1> <file2>
# Review the staged changes, adjust as needed
git diff --cached
# Apply any surgical edits to files you don't want to bulk-replace
# (e.g. the PR's file predates a recent main commit you need to preserve)
# Commit with a trailer crediting the original author
git commit -m "$(cat <<'EOF'
<subject>
<body explaining what was kept vs dropped>
Co-Authored-By: <author> <[email protected]>
EOF
)"
git push ... # branch + PR, unless under the direct-to-main exception
然后用评论关闭该 PR,说明应用了什么、舍弃了什么,并引用提交 SHA。
10. 保持文档最新
每一次合并、每一次关闭、每一次后续跟进 → 更新 <VERSION>_PR_TRIAGE.md。该文档就是你的会话日志。如果你被打断并在明天继续,该文档是"我进行到哪里了"的唯一事实来源。
11. 当 triage 完成时
- 文档中的每个 PR 都有终态(✅ merged / ✅ closed / deferred)
- Progress 标题为每个 tier 显示 N/N
- 下一个要运行的 skill 是
draft-release-notes(针对新的 main 重新生成[Unreleased]),然后是release-bump
你可以在 release 发布后删除 triage 文档,或将其保留在版本历史中作为记录。
注意事项
- 在过期的分支上,
main..HEAD会骗人。 它会把 main 自分支分叉以来新增的所有内容显示为删除。务必通过git show HEAD查看 PR 的实际提交。 - 对未 rebase 的分支进行 squash 合并会回退中间的改动。 squash 计算的是
diff(PR-head, merge-base)。rebase 会把 merge-base 向前移动。 mergeable=UNKNOWN是暂时状态——GitHub 正在推送后重新计算。直接尝试合并即可。- 路由顺序很重要(FastAPI 及类似框架):
DELETE /history/failed必须注册在DELETE /history/{id}之前,否则带参数的路径会把"failed"当作 ID 消费掉。 - Apple 的
-weak_framework会覆盖同一框架的-framework,与顺序无关——当某个依赖硬链接了可选内容时,通过cargo:rustc-link-arg=-Wl,-weak_framework,Name使用它。 - 依赖版本下限会限制你能应用的内容。 在接受像
torch_dtype=→dtype=这样的 kwarg 重命名之前,先检查最低版本 pin 是否支持它。有时正确的做法是只 cherry-pick PR 的一半。 cpal::Stream及类似的!Send音频类型无法跨越await点或spawn_blocking。有时一个"不理想但正确"的同步等待是现有条件下最好的修复;标记它但不要阻塞。- PyTorch nightly 构建不适合发布——非确定性,且可能在两次运行之间发生回归。如果某个 PR 建议切换到 nightly 来修复 GPU 问题,优先选择
TORCH_CUDA_ARCH_LIST=...+PTX或等待稳定版支持。
规范命令参考
# Bulk PR metadata
gh pr list --state open --limit 50 --json number,title,author,mergeable,mergeStateStatus,additions,deletions,maintainerCanModify,files
# Detailed single-PR view
gh pr view <N> --json body,author,headRefName,baseRefName,mergeable,maintainerCanModify,files,statusCheckRollup
# The actual commit, not the branch-vs-main diff
git show HEAD
git show --stat HEAD
gh pr diff <N>
# Rebase contributor branch onto current main
git fetch origin main && git rebase origin/main
# Push rebase back to contributor fork (maintainerCanModify=true required)
git remote add <author> https://github.com/<author>/<repo>.git
git fetch <author> <branch>
git push <author> HEAD:<branch> --force-with-lease
# Merge
gh pr merge <N> --squash
# Confirm merge SHA for triage doc
gh pr view <N> --json state,mergeCommit --jq '{state, sha: .mergeCommit.oid[0:7]}'
# Close superseded
gh pr close <N> --comment "Closing — superseded by merged #<M>. Thanks!"
备注
- 绝不通过
main..HEAD审查过期分支。 这是本技能中最重要的一条。 - triage 文档就是会话状态。 丢了文档,就丢了会话。每次操作后都要更新它。
- 即使只部分应用,也要署名贡献者。 使用
Co-Authored-By:trailer,并关闭那些链接到已应用提交的评论。 - 不要让完美成为交付的敌人。 一个从"损坏"变成"可用但带有已知小问题"的修复是严格的改进。标记该问题,创建后续任务,合并该修复。