request-pr-review — 請人來看已經開好的 PR
實作做完、PR 開出來了,缺的是最後一步:請同事看。這支 skill 承載那一步的全部——這批 總共有多少、分在哪些 repo、每一個現在被誰看了、要通知誰。
三步,順序固定,中間那一步是人:
- query 名下所有 open PR,帶回每一個的 review 狀態。
- 列出來問使用者要怎麼處理——列的東西要足夠讓他當場決定,不用再去 GitHub 翻一遍。
- 照他的決定通知對的人。
這支 skill 不知道的三件事
要看哪個 org、哪個 repo 通知誰、一張單長什麼樣。 三者都由認領那個 org 的 skill 在
自己的 SKILL.md 宣告,這裡掃所有 skill 找那一行:
<!-- {任意前綴}-PR-CONTEXT-{org}: {命令} -->
鍵裡那一段就是 org 名,所以掃一次同時回答「要 query 誰」與「拿到之後問誰」。一個宣告都 沒有時說出來並停下——不猜一個 org,不用占位字串當預設值。
命令裡的相對路徑,錨是宣告它的那份 SKILL.md 所在的目錄,不是呼叫者當下的 cwd——核心
跑它之前會先切過去。宣告行寫得出來的只有相對路徑,而「相對於我自己」在 skill 被單獨帶到
別的環境時仍然成立,「這個 workspace 的 root 在哪」在那裡沒有答案。
宣告的命令要認得兩個模式,第一個參數就是模式名:
| 模式 | 進 | 出 |
|---|---|---|
notify --repo R |
參數 | 這個 repo 的 PR 通知誰,格式由那一層自己定 |
ticket |
stdin:repo⇥number⇥title⇥branch |
repo⇥number⇥單號⇥單的 URL |
ticket 走 TSV 而不是把 PR 的 JSON 丟過去:那個資料結構是這支 skill 的事,交出去等於逼
每一家公司的知識層都得認得它。這裡不認得任何一家公司的單號長什麼樣,也不該認得。
所以這裡沒有前置設定要人先準備,也不需要任何環境變數。author 沒指定時自己偵測:
MY_USER="$(gh api user --jq '.login')"
approval threshold 是公司 pack 的知識,不是這支 skill 的:幾票算看過了由那家公司說,寫在它自己
的 pack 裡(repo-notes 那支的 PR 與 review 慣例那一份)。check-pr-approval-status.sh 的
--threshold 沒給時用 2,公司 pack 說的數字不是 2 時就帶 --threshold N。以前這裡寫「讀
workspace config 的 shared defaults」並在 pipe 裡放一個 $APPROVAL_THRESHOLD——那個鍵從來
不存在,那個變數整支 skill 沒有人賦值,讀到的永遠是腳本的預設值。
Bundled Scripts
路徑相對於本 skill 目錄。
| Script | 用途 | 輸出 |
|---|---|---|
scripts/resolve-pr-context.sh |
orgs 列出被宣告的 org;notify / ticket 轉交給認領那個 org 的命令 |
org 清單/那一層的答案 |
scripts/fetch-user-open-prs.sh |
名下所有 open PR,含 org/base/head | PR JSON array |
scripts/check-pr-approval-status.sh |
approval 數、stale、被指名還沒看的 reviewer | 加上 approval 欄位 |
scripts/fetch-pr-review-comments.sh |
還沒被回覆的 actionable comments | 加上 unaddressed_comments |
scripts/check-pr-ci-status.sh |
CI 狀態 | 加上 ci |
scripts/attach-pr-ticket.sh |
這個 PR 屬於哪一張單 | 加上 ticket |
scripts/plan-pr-notify.sh |
這批要通知哪裡,逐個 repo 給建議 | repo⇥狀態⇥數量⇥目的地 |
Script 是 deterministic source;不要在入口重寫它們的 API、stale 或 bot filter 邏輯。
Lazy-load Map
| 何時讀 | Reference | 用途 |
|---|---|---|
| 產出報告、加 label、組通知訊息時 | references/request-pr-review-reporting.md |
報告表格、label 處理、訊息要說出哪幾件事 |
| 那份報告要分哪幾段、每一段寫什麼 | references/report-format.md |
交出去給人讀的報告共用的段落骨架 |
| 判讀 approval / stale 語意前 | references/stale-approval-detection.md |
stale approval 的權威定義 |
| 訊息送出前 | .claude/rules/style-and-language.md |
語言關卡 |
1. Query:這批是哪些、狀態如何
一條 pipe 走完,每一段各補一類狀態:
"$SKILL_DIR/scripts/fetch-user-open-prs.sh" \
| "$SKILL_DIR/scripts/check-pr-approval-status.sh" \
| "$SKILL_DIR/scripts/fetch-pr-review-comments.sh" --author "$MY_USER" \
| "$SKILL_DIR/scripts/check-pr-ci-status.sh" \
| "$SKILL_DIR/scripts/attach-pr-ticket.sh"
結果為 [] 就回報沒有 open PR,結束。
問不到的東西不會被讀成「沒問題」,這是這一步的重點:
| 欄位 | 值 | 意思 |
|---|---|---|
branch_status |
unreachable |
base/head 沒問到。不是「base 是預設分支」 |
ci.state |
UNREACHABLE |
這一趟沒問到 CI。不是 PASS,也不是 NONE |
ci.state |
NONE |
真的沒有設任何 check |
ticket |
null |
沒查出屬於哪張單。不是「這個 PR 沒有單」——那幾筆會被逐個指名 |
某個 org 整個問不到時,其餘 org 的結果照常回來,問不到的那些被逐個指名;全部問不到時 非 0 退出,而不是回一個看起來像「沒有 PR」的空陣列。
2. 列出來,讓使用者決定
報告要讓人當場決定,不用再去別的地方查。逐個 PR 給出:
- repo、編號、標題、連結
- 它屬於哪一張單(
ticket.key與ticket.url)。卡住的 PR 要回頭解問題時,那張單就是 去處。查不出來的標成「查不出」並且不要寫成「沒有單」——那是兩件事 - approval:
valid_approvals/threshold,有 stale 要標出來 - 被指名但還沒回應的 reviewer(
requested_reviewers) - 還沒被回覆的意見數(
unaddressed_comments) - CI 狀態;
FAIL要帶上是哪幾個 check(ci.failing) - 這個 PR 適不適合現在請人看,不適合的話卡在哪。
unaddressed_comments不是 0 的不請票: reviewer 留的每一串含 nit 都要處置並回在原串(swe-knowledge第 5 條),還沒做到就先做, 請人再看一次沒處理完的東西是浪費他的時間 valid_approvals到了threshold、unaddressed_comments是 0、CI 綠的那幾顆,不是 「請人看」的候選,是「按 merge」的候選。 誰按由公司 pack 說; 公司 pack 說作者按的,就直接按,不把「要不要 merge」列成待決
最後給出總數與 repo 分布。
然後停下來等使用者選。 沒有得到選擇之前不通知、不加標記、不動任何一個 repo。
不自動挑一批送出去。 判斷可以給,決定不代做。
3. 通知對的人
只處理使用者選中的那些。先給出建議,再送:
printf '%s' "$SELECTED" | "$SKILL_DIR/scripts/plan-pr-notify.sh"
它逐個 repo(不是逐個 PR)算出要送去哪,輸出 repo⇥狀態⇥數量⇥目的地。把這份建議連同
目的地一起給使用者看過再送——目的地是通知的一半,一個沒被看過的目的地跟沒被選過一樣。
退出碼 4 的意思是「我答完我答得出的,剩下這幾個要你給」,不是壞掉:
known→ 照宣告那一支 skill 自己的說明送出。怎麼送、送到哪、訊息長什麼樣,都是 那一層的事——這支 skill 不認得任何一個目的地的形狀。目的地那一欄裡指出一份訊息版型時 就去讀它,照它說的形狀組;沒有版型的話說出來,不要安靜地自己定一個形狀。unknown→ 對使用者說出「這幾個 repo 我不知道要通知誰」,把宣告那一層印出來的 修法原樣附上,問他要一個目的地。不猜一個,也不沿用別的 repo 的——同一個 org 底下 不同 repo 送去不同地方是常態。- 拿到答案之後當下記下來,照宣告那一層說的方式(訊息裡會寫)。只用在這一次的話, 下一次會再問一次同樣的問題,而被問的人會以為自己上次沒講清楚。
一個 repo 問不到不擋其餘的:其他 repo 照常送,問不到的那幾個等答案。
訊息的預設形狀是每個 PR 的標題與連結,加一句請人看,沒有別的。 要多說必須說得出
「讀的人不看這段就決定不了要不要點進去」,而且形狀是一個 PR 一行。approval 數、逐位
reviewer 狀態、CI、施工過程都不進通知——那些在上一步給使用者看的那份報告裡。判準與正反例
在 references/request-pr-review-reporting.md 的〈通知訊息〉。
沒有任何東西在擋長訊息,語言關卡只驗語言。這是刻意的:寫的人自己負責。
送出前把訊息寫成 temp markdown 並通過語言關卡:
bash "$SKILL_DIR/scripts/validate-language-policy.sh" --blocking --mode artifact <訊息檔>
送完之後逐個回報:送出去的是哪幾個、送到哪、成功與否。送不出去要說出來,不能只報成功 的那些。
Hard Safety Rules
- 不動任何一個 repo 的 git 狀態:不 rebase、不 push、不切分支、不 stash。base 過期是 報告上的一行,要不要處理是使用者的決定。
- 不自動修正 CI failure 或 review comments。
- 不代替使用者決定要通知誰、通知哪幾個。
- 不使用
gh pr view --json reviews取代 bundled approval script。 - 不使用
gh pr checks --json取代 bundled CI script。 - 不把問不到的狀態讀成通過——
UNREACHABLE不是PASS。 - 不通知已達標的 PR。
- 不把「已達標」寫成「可 release / 已完成」。
- 不忽略 stale approval。
- 不把未通過語言關卡的訊息送出。
- 訊息不使用「催促」、「催」、「趕快」等字眼;用「麻煩大家幫忙」、「有空幫忙看一下」。