背景
中型團隊的日常:早上進公司,看到 8 個 open PR 等 review。問題不是「review 哪個」,而是:
- 哪個 PR 的改動風險最高,要先看?
- 哪兩個 PR 會衝突,要排 merge 順序?
- 哪個 PR 是孤島,可以無腦先 merge?
傳統排程靠數字:diff 行數、改幾個檔、CI 紅綠。但這些都看不到圖譜層級的衝突。兩個 PR 改的檔案不重疊,卻都動到同一個 god node 或同一個 community,merge 後一樣會炸。
graphify 的 prs --triage 跟 prs --conflicts 用 graph impact 來排優先度跟找隱性衝突。reviewer 拿到的是「按社群重疊跟 god node 觸動排出的清單」,不是按檔案行數排的。
⚠️ 與範例 002 差異:002 示範 3 個 PR 的衝突分析。本範例聚焦在日常 reviewer 的工作流:每天早上的 triage routine、CI 整合、團隊紀律。
適用情境
- 一天有 5 個以上 open PR 的團隊
- reviewer 輪值,需要快速决定今天看哪幾個
- 多個 feature branch 平行開發,預期會有 graph-level 衝突
- 想把 graph-impact 整合進 CI,讓每個 PR 自動帶影響半徑標籤
完整流程
步驟 1:確保 repo 是 graphify-ready
triage 的前提是圖譜反映 main branch 的現況,而且 hook 已裝:
cd ~/team-monorepo
uv tool install graphifyy && graphify install
graphify extract . --code-only
graphify hook install
graphify hook status
hook status 應該顯示 post-commit / post-checkout / merge driver 都已安裝。沒裝好,後面 triage 的圖譜會跟實際 branch 脫鉤。
步驟 2:早上 routine 第一行:triage
進公司、開終端,第一行指令就是 triage:
graphify prs --triage
graphify 收集所有 open PR,比對各自的 diff 跟當前圖譜,輸出按風險排序的清單:
Triage ranking (8 open PRs):
1. #58 (HIGH) billing refactor
- 3 god nodes touched in Community 3 (billing)
- 12 modified files, CI failing
- Suggested reviewer: domain owner (Alice)
2. #52 (MED) export endpoint
- Community 3 (billing) overlap with #58
- 3 files, CI passing
3. #61 (MED) auth rate limiter
- 1 god node touched in Community 1 (auth)
- 5 files, CI pending
4. #41 (LOW) typo fix in README
- No graph impact
- 1 file, CI passing
... (4 more)
reviewer 看一眼就知道:今天優先看 #58,因為它動到 3 個 god node、CI 又紅了。
步驟 3:找隱性衝突配對
接著跑衝突配對:
graphify prs --conflicts
graphify 比對所有 PR 兩兩之間的 community 重疊:
Conflict pairs (graph-level):
PR #52 ↔ #58 shared community: billing (Community 3)
→ merge order: #58 first, rebase #52 after
PR #61 ↔ #67 shared god node: AuthMiddleware.verify
→ merge order: queue serially, do not parallel merge
PR #41 ↔ (none) isolated
這份清單看不到檔案層級的 diff 重疊,但 graph 重疊才是真正的風險。#52 跟 #58 改的檔案可能完全不重疊,但都動到 billing community 的 god node,先 merge 誰、後 rebase 誰會决定後續 PR 多乾淨。
步驟 4:决定 merge 順序
基於步驟 2 跟 3 的輸出,今天的 merge 計畫:
1. 先 merge #41(孤島,無風險)
2. 再 merge #58(HIGH,先解 CI)
3. rebase #52 onto main(因為跟 #58 共用 billing community)
4. 處理 #61 跟 #67 序列合併(共用 god node)
這個順序保證:高風險先處理,後續每個 rebase 都對著已穩定的 main 做,不會反覆處理同一條 community 的 conflict。
步驟 5:CI 自動貼 triage 標籤
把 triage 整合進 GitHub Actions,每個 PR 自動帶 graph-impact 標籤:
.github/workflows/pr-graph-impact.yml:
name: PR Graph Impact
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install graphify
run: pipx install graphifyy
- name: Build main branch graph
run: graphify extract . --code-only
- name: Run triage
run: graphify prs --triage > triage.md
- name: Run conflict check
run: graphify prs --conflicts > conflicts.md
- uses: actions/upload-artifact@v4
with:
name: graphify-triage
path: |
triage.md
conflicts.md
每個 PR 自動 attach 兩份報告:triage 排序跟衝突清單。reviewer 不必親自跑指令。
步驟 6:在 PR 討論串用 triage 結果
reviewer 看 PR 時,先看 triage 標籤决定深度:
- LOW:skimmable review,看個 5 分鐘就 approve
- MED:聚焦 review,重點看 community 重疊處
- HIGH:找 domain owner 配對 review,CI 必過才 merge
對 PR 作者也一樣:作者開 PR 前可以先 graphify prs --conflicts 預查自己會跟誰衝突,提前 rebase 或先跟對方溝通。
真實輸出範例
$ graphify prs --triage
Triage ranking (8 open PRs):
1. #58 (HIGH) billing refactor
- 3 god nodes touched in Community 3 (billing)
- 12 modified files, CI failing
- Suggested reviewer: domain owner (Alice)
2. #52 (MED) export endpoint
- Community 3 (billing) overlap with #58
- 3 files, CI passing
3. #61 (MED) auth rate limiter
- 1 god node touched in Community 1 (auth)
- 5 files, CI pending
4. #41 (LOW) typo fix in README
- No graph impact
- 1 file, CI passing
5. #43 (LOW) bump deps
6. #44 (LOW) docs: add architecture diagram
7. #45 (LOW) chore: extract util function
8. #47 (LOW) test: add coverage for notifier
$ graphify prs --conflicts
Conflict pairs (graph-level):
PR #52 ↔ #58 shared community: billing (Community 3)
→ merge order: #58 first, rebase #52 after
PR #61 ↔ #67 shared god node: AuthMiddleware.verify
→ merge order: queue serially, do not parallel merge
PR #41 ↔ (none) isolated
PR #43 ↔ (none) isolated
PR #44 ↔ (none) isolated
對比傳統 PR 流程
| 任務 | 傳統(檔案 diff + CI) | graphify triage |
|---|---|---|
| 排 review 優先度 | 看檔案數、行數、CI 紅綠 | 加上 graph impact 跟 god node 觸動 |
| 找衝突 | 等 git conflict marker | prs --conflicts 在 merge 前預警 |
| 决定 merge 順序 | 憑感覺或看 PR 號 | 依 community / god node 重疊排 |
| 找孤島 PR | 要自己過濾 | triage 直接標 LOW / isolated |
| 跨檔隱性衝突 | 完全看不到 | graph-level 才看得到 |
graph-level 衝突的價值在於:兩個 PR 改的檔案不重疊、git 不會報 conflict,但都動到同一個 god node,後 merge 的那個其實是建立在過時的圖譜假設上。prs --conflicts 提前抓出來。
失敗處理
| 情境 | 排查 |
|---|---|
prs 回 gh not authenticated |
在本地跑 gh auth login;在 CI 設 GITHUB_TOKEN(建議用 pull-requests: read 權限的 GITHUB_TOKEN,不要塞 PAT) |
prs --triage 結果沒排序 |
沒設語意後端,graphify auto-detect 失敗。手動設 GRAPHIFY_TRIAGE_BACKEND=kimi(或 claude / openai / gemini) |
| 衝突結果為空但實際會衝突 | 圖譜過時。確認 graphify hook status 顯示 hook 跑過,或手動 graphify update |
CI 裡 graphify extract 失敗 |
CI runner 記憶體不夠,或某個檔 AST parse 失敗。加 --code-only 跟排除 .graphifyignore 範圍 |
| triage 把無關 PR 排成 HIGH | 某個 god node 被誤判。看該 PR 的 Communities impacted,如果是誤判,可能是 --code-only 沒抓到 dynamic dispatch |
| 兩 PR diff 完全一樣卻沒被標衝突 | graphify 是 graph-level,不是檔案層級。檔案相同但 community 不重疊就不會標。若要找檔案層級衝突,加 git 工具(例如 gh pr view --json files) |
內部連結
下一步
- PR 審查 + 團隊共享 graphify-out(團隊層級的 graph 共享與 HTTP MCP server)
- 大型 monorepo 重購(triage 之外的重購影響分析)
關鍵學習點
- triage 排程依 graph impact 而不是 diff 行數。god node 觸動才是真正的風險訊號。
prs --conflicts抓的是 graph-level 衝突,比 git conflict marker 更早預警。兩 PR 檔案不重疊仍可能 community 重疊。- 標準早上 routine 兩行:先
prs --triage排優先度,再prs --conflicts找配對。5 分鐘决定今天的 review 與 merge 計畫。 - CI 整合讓每個 PR 自動帶 triage 報告,reviewer 不必親自跑。這是把 graphify 從個人工具昇級成團隊紀律的關鍵步驟。
- triage 需要語意後端(
GRAPHIFY_TRIAGE_BACKEND),沒設會退化成沒排序的清單。團隊統一設一個,避免結果不一致。