Communitygithub.com

hmisaki/voicebox

使用此技能在發布前對待處理的 PR 佇列進行分類。將每個開啟的 PR 分類為「必須合併」、「候選」、「已取代」或「延後」;撰寫一份可追蹤的分類文件;並端到端執行合併循環。專為發布前的「PR 衝刺」設計,讓單一維護者能在一次會話中清理累積的待辦事項。

voicebox 是什麼?

此技能旨在幫助維護者在發布前,透過結構化的工作流程,高效地對大量待處理的 Pull Request (PR) 進行分類、審查和合併。它能快速產出一份名為 `<VERSION>_PR_TRIAGE.md` 的動態分類文件,將所有開啟的 PR 明確分為「必須合併」、「候選」、「已取代」和「延後」四個等級,並提供可追蹤的進度指標。此技能支援 GitHub CLI (`gh`),並建議在獨立的 git worktree 中操作,以避免污染主分支。其核心優勢在於能防止因直接比較 `main..HEAD` 而產生的誤判,並自動處理因分支過時可能導致的合併衝突,最終實現乾淨的 squash 合併,同時在關閉 PR 時給予作者應有的 credit,確保發布流程的順暢與高效。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/hmisaki/voicebox/tree/HEAD/.agents/skills/triage-prs

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

分類 PRs

目標

將大量開啟的 PR 轉化為在一次專注會話中交付的合併。產出一份可追蹤、可恢復的計畫 (<VERSION>_PR_TRIAGE.md),然後依序執行 — 在需要時進行變基、以隔離安全的批次合併、應用合併後的後續修正,並在關閉已取代或部分適用的 PR 時給予作者 credit。

此技能與 draft-release-notes 和 release-bump 搭配使用:先進行分類,然後針對新的 main 分支起草發行說明,最後進行版本發行。

使用時機

  • 在次要或主要版本發布前,當累積了 10 個以上的開啟 PR 時
  • 當您希望在不失去哪些功能將被合併的整體脈絡下,打通合併流程時
  • 當您知道自己無法深入審查每一個 PR,但需要快速合併關鍵子集時

先決條件

  • gh CLI 已針對該儲存庫進行驗證
  • 一個專門用於 PR 審查的工作樹 (避免用貢獻者的分支污染 main)
  • 清楚目標版本 — 分類文件將以版本命名 (例如 0.4.0_PR_TRIAGE.md)

工作流程

1. 設定隔離的 PR 審查工作樹

git worktree list  # 先檢查是否有舊的
git worktree prune
git worktree add ../voicebox-pr-review -b pr-review-<VERSION> main

保留主工作樹用於發布準備工作 (變更日誌草稿、直接寫入 main 的後續修正)。保留審查工作樹用於 gh pr checkout — 每次 checkout 都會將 HEAD 移動到某個貢獻者分支,而您不希望在主工作樹中執行此操作。

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 的以下資訊:

  • 大小 (+新增行數/-刪除行數)
  • 可合併狀態 (CLEAN, UNSTABLE, DIRTY = 衝突, UNKNOWN = GitHub 仍在計算)
  • 是否允許維護者編輯該分支 (稍後若需要為作者變基時會用到)
  • 觸及的檔案路徑 (有助於發現 PR 之間的重疊)

在向 main 推送之後,UNKNOWN 很常見 — 直接嘗試合併即可。

3. 分類為不同等級

將每個 PR 歸入恰好一個類別:

第 1 級 — 合併: 小型、可合併、修復實際錯誤、CI 乾淨、審查成本低。一行修正、依賴放寬、有針對性的安全性強化。這些是容易得手的成果。

第 2 級 — 候選,需要審查: 中型 (50-200 行),觸及更多表面,看起來合理但需要仔細閱讀。符合產品方向的新使用者面向功能。

已取代: 該修正或功能已被其他已合併的內容涵蓋。關閉時加上註解,指向取代的 PR。仔細檢查 — 「相似的標題」不能作為證據;比較實際的 diff。

延後到下個版本: 大型功能、骯髒的衝突、草稿 PR、任何以會引入風險的方式觸及發布管線的內容。不要在衝刺中合併這些 — 它們需要專門的關注。

4. 撰寫分類文件

在 PR 審查工作樹的根目錄中建立 <VERSION>_PR_TRIAGE.md。結構如下:

# <儲存庫> <VERSION> — PR 分類

用於追蹤哪些開啟的 PR 會進入 <VERSION> 的工作文件。在發布完成後刪除。

最後更新:<DATE>

## 進度

**第 1 級:0 / N 已合併**
**第 2 級:0 / M 已處理**
**已取代分類:待處理**

---

## 為 <VERSION> 合併 — 關鍵錯誤修復

| PR | 狀態 | 大小 | 修正內容 | 必須合併的理由 |
|---|---|---|---|---|
| [#123](url) | [ ] | +5/-0 | ... | ... |

## 強烈候選 — 需要快速審查

| PR | 狀態 | 大小 | 摘要 |
|---|---|---|---|

## 關閉為已取代

| PR | 狀態 | 原因 |
|---|---|---|

## 延後至 <NEXT_VERSION>

- [#xxx](url) ... — 原因

---

## 攻擊順序

1. 關閉已取代的 PR (一行註解)
2. 以無依賴衝突的批次合併第 1 級 — 檢查檔案路徑不重疊
3. 逐一審查第 2 級
4. 重新執行 `draft-release-notes` 以納入所有內容
5. 執行 `release-bump`

進度 標頭是最重要的部分 — 這是您的計分板,讓您能在會話中斷時乾淨地恢復。

5. 執行循環 — 每個 PR

對於第 1 級 / 第 2 級清單中的每個 PR:

a. 在審查工作樹中切換分支:

cd ../voicebox-pr-review
git checkout pr-review-<VERSION>  # 重置為中性基礎
git checkout -b pr-review-<VERSION>
gh pr checkout <N>

b. 閱讀 實際的 commit,而非 main..HEAD:

git show HEAD                    # PR 的實際變更
git show --stat HEAD             # 觸及的檔案 + 行數

如果 PR 分支比 main 舊,不要透過 git diff main..HEAD 進行審查。那個 diff 會將 PR 分支建立後合併到 main 上的每一個 commit 都顯示為 - (刪除) 行。一個 3 行的 PR 可能會看起來像是一個 700 行的還原提交。這是錯誤判斷 PR 最容易的方式。

c. 評估關注點: 正確性、範圍、與已合併工作的互動、版本相容性 (例如不能使用需要我們尚未鎖定版本的依賴項的 API)。

d. 如果分支落後於 main,則進行變基:

git fetch origin main
git rebase origin/main

在 squash 合併之前,這是至關重要的。GitHub 的 squash 計算的是 diff(PR-head, merge-base) — 在一個過時的分支上,這個 diff 包含了還原每個中間 commit 的動作。變基會將合併基礎向前移動,使 squash 變得乾淨。

e. 如果允許維護者編輯,則將變基後的內容推回貢獻者的分支:

git remote add <author> https://github.com/<author>/<repo>.git
git fetch <author> <branch>              # 先取得他們的 ref
git push <author> HEAD:<branch> --force-with-lease

這可以保持 GitHub 的 PR 介面與變基後的狀態同步,並使 GitHub 端的合併變得乾淨。

f. 合併:

gh pr merge <N> --squash

g. 更新分類文件 — 將核取方塊標記為 ✅ merged <sha> (使用 gh pr view <N> --json mergeCommit --jq '.mergeCommit.oid[0:7]' 產生的短 SHA)。更新進度標頭。

6. 批次處理小型修正

對於變更 ≤5 行、CI 乾淨、檔案路徑不重疊且意圖明顯正確 (例如一行依賴放寬、環境變數新增、導入路徑修正) 的 PR,可以在一個循環中合併,而無需進行逐個 PR 的審查儀式:

for pr in 425 384 416 429; do
  echo "=== 正在合併 PR $pr ==="
  gh pr merge $pr --squash
done

之後驗證每個 PR 都已乾淨合併:

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 映射、過時的哨兵清理)。不要阻礙合併;在合併後立即以一個普通分支加 PR 的方式套用後續修正:

cd <main-worktree>
git pull --ff-only origin main
git checkout -b fix/<short-name>
# 編輯...
git commit -m "fix(<area>): <one-liner>"
git push -u origin fix/<short-name>
gh pr create --title "..." --body "接續 #<N>。..."

在分類文件中記錄兩個 SHA (✅ merged <pr-sha> + follow-up <pr>)。

直接寫入 main 的例外: 僅在明確、限定的政策下 (例如「發布衝刺」)。不要將其設為預設值。

8. 已取代:關閉並附上給予 credit 的註解

gh pr close <N> --comment "關閉 — 已被已合併的 #<M> 取代,該 PR 已合併 <簡短描述>。謝謝!"

先檢查 diff — 「相似的標題」是不夠的。如果 PR 是部分被取代 (診斷是正確的,但只有一半的變更仍然需要),則改用部分套用模式。

9. 部分套用模式

當一個 PR 同時包含了有價值和有問題的變更時:

cd <main-worktree>
git pull --ff-only origin main

# 從 PR 分支精選特定檔案
git checkout <pr-commit-sha> -- <file1> <file2>

# 審查暫存的變更,根據需要進行調整
git diff --cached

# 對您不想批量替換的檔案進行外科手術式編輯
# (例如 PR 的檔案早於您需要保留的最近 main commit)

# 使用帶有原始作者 credit 的 trailer 提交
git commit -m "$(cat <<'EOF'
<主旨>

<說明保留了什麼和捨棄了什麼的主體>

Co-Authored-By: <author> <[email protected]>
EOF
)"
git push ...  # 分支加 PR,除非在直接寫入 main 的例外情況

然後關閉 PR,並附上說明套用了什麼和捨棄了什麼的註解,並引用 commit SHA。

10. 保持文件更新

每次合併、每次關閉、每次後續修正 → 都更新 <VERSION>_PR_TRIAGE.md。這份文件是您的會話記錄。如果您被打斷並在明天恢復,這份文件是「我進行到哪裡了」的唯一真相來源。

11. 當分類完成時

  • 文件中的每個 PR 都有一個最終狀態 (✅ merged / ✅ closed / deferred)
  • 進度標頭顯示每個等級的 N/N
  • 接下來要執行的技能是 draft-release-notes (根據新的 main 重新產生 [Unreleased]),然後是 release-bump

您可以在發布之後刪除分類文件,或者將其保留在版本歷史中作為記錄。

潛在陷阱

  • 過時分支上的 main..HEAD 會誤導人。 它會將分支分岔後 main 獲得的所有內容顯示為刪除。務必透過 git show HEAD 審查 PR 的實際 commit。
  • 對未經變基的分支進行 Squash 合併會還原中間的 work。 Squash 計算的是 diff(PR-head, merge-base)。變基會將合併基礎向前移動。
  • mergeable=UNKNOWN 是暫時的 — GitHub 正在重新計算。直接嘗試合併即可。
  • 路由順序很重要 (FastAPI 和類似框架): DELETE /history/failed 必須在 之前 註冊 DELETE /history/{id},否則參數化的路徑會將 "failed" 作為 ID 消耗掉。
  • Apple 的 -weak_framework 會覆蓋 -framework 對於同一個 framework,無論順序如何 — 當某個依賴項硬連結了某些可選項目時,請透過 cargo:rustc-link-arg=-Wl,-weak_framework,Name 使用它。
  • 依賴項版本下限限制了您可以套用的內容。 在接受像 torch_dtype= → dtype= 這樣的關鍵字參數重新命名之前,請檢查最小版本 pin 是否支援它。有時正確的做法是挑選 PR 的一半。
  • cpal::Stream 和類似的 !Send 音訊類型 不能跨越 await 點或 spawn_blocking。有時一個「不理想但正確」的同步等待是可用的最佳修正;標記但不要阻止。
  • PyTorch nightly 版本不能用於發行版 — 非確定性,可能在運行之間回歸。如果某個 PR 建議切換到 nightly 以修復 GPU 問題,請優先使用 TORCH_CUDA_ARCH_LIST=...+PTX 或等待穩定版本支援。

規範命令參考

# 批次 PR 中繼資料
gh pr list --state open --limit 50 --json number,title,author,mergeable,mergeStateStatus,additions,deletions,maintainerCanModify,files

# 詳細的單一 PR 檢視
gh pr view <N> --json body,author,headRefName,baseRefName,mergeable,maintainerCanModify,files,statusCheckRollup

# 實際的 commit,而非分支與 main 的 diff
git show HEAD
git show --stat HEAD
gh pr diff <N>

# 將貢獻者分支變基到當前的 main
git fetch origin main && git rebase origin/main

# 將變基後的內容推回貢獻者的 fork (需要 maintainerCanModify=true)
git remote add <author> https://github.com/<author>/<repo>.git
git fetch <author> <branch>
git push <author> HEAD:<branch> --force-with-lease

# 合併
gh pr merge <N> --squash

# 確認合併 SHA 以供分類文件使用
gh pr view <N> --json state,mergeCommit --jq '{state, sha: .mergeCommit.oid[0:7]}'

# 關閉已取代的
gh pr close <N> --comment "關閉 — 已被已合併的 #<M> 取代。謝謝!"

備註

  • 永遠不要透過 main..HEAD 審查過時的分支。 這是此技能中最重要的一行。
  • 分類文件是會話狀態。 遺失文件,就遺失了會話。每次操作後都更新它。
  • 即使在部分套用中也要給予貢獻者 credit。 使用 Co-Authored-By: trailer 和連結到已套用 commit 的關閉註解。
  • 不要讓完美成為交付的敵人。 一個從「損壞」變成「可以運作但有個小已知問題」的修正就是一個嚴格的改進。標記該問題,建立一個後續,然後合併修正。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能