Community程式設計與開發github.com

chrislacey89/pre-merge

主要管線審查步驟,用於在合併前建立PR、追蹤血緣關係、執行架構審查。不適用於QA接收、規劃或實作工作。

pre-merge 是什麼?

此技能是開發管線中的一個關鍵閘門,在實作驗證完成後、合併至主分支前使用。它會自動建立一個GitHub PR,並連結回對應的PRD(產品需求文件)與子任務,接著根據專案的架構原則對完整差異進行審查。審查結果以建議形式呈現,不會封鎖合併、自動修復程式碼或自動建立議題。 支援三種操作模式:作者模式(預設)用於自己的分支,會建立PR並將審查結果輸出至終端機;審查者模式需指定 `--pr <number>` 參數,用於審查他人發起的PR,產生草稿評論文字;循環模式以 `--loop` 啟動,會將所有發現記錄在持久化紀錄中(如PR主體或工作項目),且不會自動修復,而是讓操作者決定如何處理每個發現。 此技能可在標準GitHub CLI環境或「側車」後端(sidecar backend)中運作,後端會自動偵測。在循環模式下,設有「最低發現數量」保護機制,避免審查在接近空白的結果下過早結束。技能會根據分支狀態決定審查範圍:首次審查涵蓋整個分支;若PR已帶有審查標記且頭部有進展,則僅審查新增的差異部分。

相容平台✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/chrislacey89/skills/tree/HEAD/pre-merge

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

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

說明文件

pre-merge 是做什麼的?

建立一個連結回PRD與子任務的GitHub PR,然後根據專案的架構原則審查完整差異。產生建議性發現結果 — 不封鎖合併、不自動修復程式碼、也不建立議題。

調用位置

此技能是主要管線技能,用於實作已驗證後、合併至 main 分支之前,或在接手他人的PR進行審查時使用。

當分支準備好建立PR、進行架構審查以及最終的計畫與程式碼對照時,使用 /pre-merge。當你要審查非自己建立的PR時,使用 /pre-merge --pr <number>。

請勿將其用作實作驗證、QA接收或重構規劃的替代品。它假設工作已經完成並準備好進行審查。

模式

/pre-merge 以三種模式之一執行。所有三種模式都重複使用 review-checklist.md 中定義的架構審查維度;它們在消費內容和產出結果上有所不同。

  • 作者模式(預設) — 在自己的分支上呼叫,不帶 --pr 參數。技能會建立PR(階段二),並將發現結果以建議形式輸出至終端機(階段四),然後交回控制權 — 它不會自己進行任何修復提交,也不會開啟第二輪審查。 這是 /execute 步驟六在HITL路徑上自動調用的模式。它讀取的內容由分支狀態決定,而非由旗標決定: 首次審查會讀取整個子任務,也就是整個分支,除非該子任務的工作項目記錄了起始點;若針對已帶有審查貨幣戳記、且頭部後續有變動的PR重新執行,則只會取戳記後的差異 — 戳記SHA之後的提交 — 作為審查的 主體,而不會重新讀取先前審查已涵蓋的內容。階段一步驟四會做出此決定並輸出。差異是主體,而非審查者可能閱讀範圍的限制:review-checklist.md 規定了審查者應讀取的內容,而這包含差異周圍的目錄樹結構。
  • 審查者模式 — 使用 /pre-merge --pr <number> 呼叫,針對非你建立的PR。技能會跳過PR建立(PR已存在),並產生 草稿評論文字(階段四)供你審查與發布,這些文字根據 references/comment-craft.md(5P閘門、三重R、評論訊號、MMG交換)的格式組織。
  • 循環模式 — 使用 /pre-merge --loop 呼叫,或當 /execute 在AFK執行中交回控制權時自動進入。此模式執行階段一至四,如同作者模式,但階段一步驟四的範圍決策不同:循環模式總是讀取整個分支,從不採用戳記後的差異,然後繼續進入階段五:它將每個發現記錄在持久化記錄簿中 — 在PR主體或工作項目上,根據 references/state-backend.md § 審查記錄的儲存位置所述 — 並附上支持或反駁每個發現的證據,然後交回給操作者。在此模式下,發現結果不再是終端機輸出;它們變成了有負責人的持久化記錄行。循環模式不修復任何東西,也不進行任何提交 — 操作者決定每個發現的後續處理方式(參見階段五 § 為什麼循環不進行修復)。

循環模式的退出條件是每個發現都有負責人 — 絕不是審查結果為零。 「零發現」是禁止的終止訊號,與階段四現有的最低發現數量保護機制一致,此機制的設立是因為對於一個非平凡的差異,近乎空白的審查結果代表審查過早結束,而非程式碼完美無瑕。一個以乾淨審查結果終止的循環只會可靠地產出乾淨的審查結果,而沒有其他價值(Meadows, Seeking the Wrong Goal; Leveson: 永遠不要獎勵低事故數量 — 獎勵報告行為本身)。

如果你在使用者工作分支以外的分支上執行,且未提供 --pr,則詢問使用者一次是否要針對特定PR號碼使用審查者模式,而非自行猜測 — 自動偵測可以省下按鍵次數,但錯誤分類模式會產生本應是本地建議的草稿評論,反之亦然。

使用時機

  • 作者模式: QA通過後、將功能分支合併至 main 之前;Ralph完成AFK執行且你已驗證行為後;或任何希望在合併前進行審查的分支,即使沒有完整的管線執行。
  • 審查者模式: 當團隊成員或外部貢獻者開啟PR,且你想對他們的差異套用 review-checklist.md 中的完整架構審查,並產出具建設性的評論文字時。
  • 循環模式: 當發現結果必須在產生它們的 session 之外留存時 — 例如來自 /execute 的AFK交接,或任何你將分多次審查、且不希望每次重新推導發現結果的分支。當你付出的代價是遺漏追蹤發現結果,而非進行修復時使用它。

執行流程

階段一:收集背景資訊

作者模式:

  1. 詢問PRD議題編號。 如果此變更未經過完整管線,則接受「無」。在來自AFK /execute 交接的循環模式中,不要詢問 — 從交接中取得議題編號(/execute 步驟六傳遞它),如果沒有,則視為「無」。此時沒有人可以回答,因此在此處提出無條件問題會使執行在第一步就卡住。在側車後端,答案是一個 W-<n> 工作項目ID(步驟二)。此處的「無」會使階段二沒有地方記錄PR,且審查註記和循環模式記錄簿沒有歸宿:參見 references/state-backend.md § 審查記錄的儲存位置以了解它們的處理方式。

  2. 如果提供了PRD:

    gh issue view <number>
    gh issue list --search "in:body #<prd-number>" --state all --json number,title,state,body --limit 100
    

    從每個子任務議題主體中解析邊界對應(產出/消費區段)。

    在側車後端(bash "$SC" backend slices 回答 sidecar,根據 references/state-backend.md;以下區塊會詢問它,並在拒絕時停止,而非回退到 gh issue),PRD及其子任務是工作項目,步驟一中詢問的編號是 W-<n> ID:此分支實作的子任務,由 /execute 傳遞,或其PRD。不會執行任何 gh issue 命令;血緣關係和邊界對應來自工作項目:

    SC="<this skill's directory>/references/sidecar.sh"
    W="W-<n>"
    case "$(bash "$SC" backend slices)" in
      sidecar) ;;
      github) echo "backend is github: read the lineage with the issue block above"; exit 0 ;;
      *) echo "lineage aborted: sidecar.sh backend slices failed; not guessing the github path" >&2; exit 1 ;;
    esac
    J="$(bash "$SC" work view "$W" --json)" || { echo "lineage aborted: cannot read $W" >&2; exit 1; }
    case "$(jq -r '.kind // empty' <<<"$J")" in
      slice) PRD="$(jq -r '.parent // empty' <<<"$J")" ;;
      prd)   PRD="$W" ;;
      *) echo "lineage aborted: $W has no kind" >&2; exit 1 ;;
    esac
    SLICES="$W"
    if [ -n "$PRD" ]; then
      bash "$SC" work view "$PRD" --json | jq -r '"# \(.id) (prd, \(.state))\n\(.body)"'
      SLICES="$(bash "$SC" work list --kind slice --parent "$PRD" --state all)" \
        || { echo "lineage aborted: cannot list the slices of $PRD" >&2; exit 1; }
    fi
    while IFS= read -r S; do
      [ -n "$S" ] || continue
      bash "$SC" work view "$S" --json | jq -r '"# \(.id) (slice, \(.state))\n\(.body)"'
    done <<<"$SLICES"
    

    從那些主體中解析邊界對應,就像從議題主體中解析一樣。保留 $W:階段二會在其上記錄PR。

  3. 偵測基礎分支並評估差異:

    SC="<this skill's directory>/references/sidecar.sh"
    # $BASE_REF 是範圍端點;$BASE_BRANCH 是 `--base` 所接受的名稱。
    BASE_REF="$(bash "$SC" base-ref)" || { echo "base-ref failed: not guessing a base" >&2; exit 1; }
    BASE_BRANCH="$(bash "$SC" base-ref --name)" || { echo "base-ref --name failed: not guessing a base" >&2; exit 1; }
    git diff "$BASE_REF...HEAD" --stat
    git log --oneline "$BASE_REF..HEAD"
    

    base-ref 在有宣告的側車 base_ref 時列印它,否則列印 origin/HEAD 預設值。在非零退出時,它不列印任何 ref:停止並轉發其 stderr,而不是猜測基礎。對於堆疊式PR子任務,此處設定的變數不會到達後續區塊:將兄弟子任務的分支名稱(PR將目標的上游)放入每個消費該變數的區塊(你的後端的階段二區塊和階段四的戳記區塊)的 BASE_BRANCH= 佔位符中。如果與基礎沒有差異,告知使用者沒有東西需要審查並停止。

    殘留: $BASE_REF 只有在上次 git fetch 之後才是最新的。當 origin/<base> 無法解析時,base-ref 會針對本地分支進行測量並在 stderr 上說明;如果計數看起來錯誤,請執行 git fetch 並重新執行。

  4. 決定範圍 — 整個子任務,或戳記後的差異。 此決定僅適用於作者模式。循環模式會跳過它,並無條件地將 SCOPE_FROM 設定為 "$BASE_REF",每次執行都讀取整個分支,根據階段三的循環模式項目。首次審查讀取整個子任務:sidecar.sh range 給出其左端點,即當步驟二命名的工作項目在此分支上仍有記錄的起始點時為該記錄的起始點,否則為 $BASE_REF,因此在 github 後端上,子任務就是整個分支。針對已由本技能戳記、且其頭部後續有變動的PR重新執行 — 例如 /fix-findings 回合,或在故意刪除鎖定後的手動修復 — 會將戳記後的差異作為其主體。戳記存在於 backend stamp 所指定的位置,根據 references/state-backend.md:PR主體或側車。使用 /closeout 步驟二所使用的相同運算式讀取PR主體;scripts/test-review-currency-marker.sh 確認兩者字節相同,而 scripts/test-delta-review-scope.sh 針對其命名的每個分支狀態、在兩個後端上針對 fixture 執行此區塊:

    SC="<this skill's directory>/references/sidecar.sh"
    case "$(bash "$SC" backend stamp)" in
      sidecar)
        REVIEWED_SHA=$(bash "$SC" stamp read "$(git branch --show-current)") ;;
      github)
        REVIEWED_SHA=$(gh pr list --head "$(git branch --show-current)" --json body -q '.[0].body // ""' \
          | sed -n 's/.*<!-- reviewed-at: \([0-9a-f]\{40\}\) -->.*/\1/p' | tail -1) ;;
      *) echo "scope aborted: sidecar.sh backend stamp failed; not guessing the github path" >&2; exit 1 ;;
    esac
    W="<步驟二命名的 W-<n>,在 github 後端上為空>";
    BASE_REF="$(bash "$SC" base-ref)" || { echo "scope aborted: sidecar.sh base-ref failed" >&2; exit 1; }
    RANGE="$(bash "$SC" range ${W:+"$W"})" || { echo "scope aborted: sidecar.sh range failed" >&2; exit 1; }
    SLICE_FROM="${RANGE%%..*}"
    SCOPE_FROM="$SLICE_FROM"
    if [ -n "$REVIEWED_SHA" ] \
      && git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \
      && [ "$REVIEWED_SHA" != "$(git rev-parse HEAD)" ] \
      && [ -z "$(git log --merges --format=%H "$REVIEWED_SHA..HEAD")" ]; then
      SCOPE_FROM="$REVIEWED_SHA"
    elif [ -n "$REVIEWED_SHA" ] \
      && ! git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \
      && [ "$(git rev-parse -q --verify "$REVIEWED_SHA^{tree}")" = "$(git rev-parse 'HEAD^{tree}')" ]; then
      SCOPE_FROM="HEAD"
    fi
    if [ "$SCOPE_FROM" = HEAD ]; then
      echo "review scope: none, history-only rewrite — HEAD carries the tree reviewed at $REVIEWED_SHA"
    elif [ "$SCOPE_FROM" = "$BASE_REF" ]; then
      echo "review scope: whole branch, from $BASE_REF"
    elif [ "$SCOPE_FROM" = "$SLICE_FROM" ]; then
      echo "review scope: whole slice, from $SLICE_FROM ($W's recorded start)"
    else
      echo "review scope: post-stamp delta, from $SCOPE_FROM"
    fi
    git diff "$SCOPE_FROM...HEAD" --stat
    git log --oneline "$SCOPE_FROM..HEAD"
    

    四種狀態會回退到整個子任務,每種都是故意的:沒有戳記(首次審查或手動編寫的PR);戳記不是 HEAD 的祖先且 HEAD 不再承載其目錄樹(分支被重寫且內容隨之改變,或審查過的提交已消失 — /closeout 稱此為更強的分歧訊號,且此非較小的審查);戳記等於 HEAD(沒有新的東西可以設定範圍);以及戳記之後的合併提交(戳記後的差異將包含基礎分支本身的變更,這些變更並非此分支上的任何人所寫)。整個子任務就是整個分支,除非 range 找到記錄的起始點,而當它找到一個因摺疊或變基而被重寫的起始點時,它會在 stderr 上說明並回答 $BASE_REF,絕不重新錨定起始點。會列印此決定,以便終端機輸出和PR對已讀取的內容表述一致。

    保留了已審查目錄樹的重寫不是分歧。 當已戳記的提交不是 HEAD 的祖先,但仍存在於本地且承載與 HEAD 相同的目錄樹時,歷史改變了但內容沒有:階段四交接提供的摺疊(git rebase -i --autosquash)正好產生這種情況。區塊會列印 review scope: none 並將 SCOPE_FROM 設定為 "HEAD"。跳過階段三以及階段四中除了戳記(在 HEAD 重新戳記)和交接選單之外的所有內容。戳記認證了已審查的內容,而目錄樹雜湊正是該內容的精確表示。如果已戳記的提交在本地不可用,則無法比較目錄樹,並且重寫會採用上述的整個分支回退。

    在差異重新執行時,差異是主體,而目錄樹仍然可讀。 向每個階段三的審查者簡報戳記後提交聲稱要關閉的發現 — 此 session 中先前的輸出、如果循環模式寫入了記錄簿則有記錄簿(根據 references/state-backend.md § 審查記錄的儲存位置),或提交訊息本身 — 並附上 /fix-findings 的判定結果(如果有的話),以及 bash "$SC" profile 的輸出(逐字逐句),這是階段三合約提供給每個審查者的內容。每個提交的第一個問題是其修復是否關閉了其發現在其命名的所有位置上的問題,這是「修復完整性」維度;第二個問題是修復是否引入了新的問題,這是「回歸」維度。

…

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

chrislacey89/closeout

Primary pipeline tail step after /pre-merge, when a reviewed PR is ready to land and you want the workspace back to a clean base. Use to merge, switch off the worktree before removing it, prune the merged branch, pull base, and verify a clean end state. Triggers on 'close out', 'merge and clean up', 'get back to a clean main/base', 'tear down the worktree'. Not for capturing lessons (that's /compound), or closing the PRD and any other open slices and research-artifact hygiene (that's the cleanup prose in SYSTEM-OVERVIEW Step 9).

chrislacey89/compound

Primary pipeline knowledge-capture step that closes the compounding loop. Capture a durable lesson in docs/solutions/ onto the open PR before /closeout merges (the default), or post-merge when the lesson only surfaces during/after merge. Use so future /research and /write-a-prd runs improve. Not for trivial edits with no reusable project-level learning, and not for the merge/teardown itself (that's /closeout).

chrislacey89/excavate

User-invoked side-route for onboarding to a legacy repo with no docs/solutions/, research archive, or written decisions. Mines the last 12 months of git history and, when gh is available, PR bodies and review threads, into ranked hotspots, cited recovered decisions, and a who-to-ask map, then offers a PR of docs/solutions/ entries. Not for repos that already compound, not for judging people, not model-invoked.

chrislacey89/execute

Primary pipeline execution step after /prd-to-issues or for clearly scoped implementation work. Use to build, verify, and commit a concrete slice, delegating to /tdd for backend work and behavior-heavy frontend logic when red-green-refactor will reduce risk. Not for shaping or pre-merge review.

相關技能