merge-briefing は何をしますか?
概要
マージは元に戻せない共有状態の変更であるため、ユーザーがPRごとに明示的に承認する必要があります。このゲートは形式的には簡単に通過でき、不正に通過させることも容易です。誠実なバージョンでは、「何も問題がない」ことを確認できる十分な幅を持つ調査が必要であり、ブロッキング所見が実際に存在するサーフェス(インラインレビュースレッド)は、一見自然に見える gh pr view --json comments,reviews 呼び出しが黙って省略するものです。
このスキルは、その手続きをオンデマンドで提供するものです。Pre-Merge Reporting ルール(ETHOS.md → Rules praxis carries)がゲートを維持します。以下のステップ4の6パートリストがレポートの形式です。以下の手順は、「PRが完了したように見える」状態から、ユーザーが一言で回答できる質問に至るまでの流れを示しています。
中核となる原則: ブリーフィングはPRの状態に関する主張です。そのため、各行はコンポーズ時(PRが作成された時点ではなく)に調査された情報に基づいています。
使用すべきタイミング
- ユーザーにマージの承認を求めようとしているとき。
- マージが要求されたが、このターンでまだブリーフィングを提示していないとき。
- 以前の質問が未回答のまま残っている、またはCI/レビューが変更された後に再質問するとき。
- ユーザーがすでに承認したクラスターの次に、コンパニオンPR、フォローアップPR、または再生成されたPRがあるとき。
鉄則
- 1つのPRに1つのブリーフィング、1つの回答。 PR Xを承認することは、Xのみを承認することです。唯一の例外は一括マージです。この場合、1ターンでPRごとに個別のブリーフィングセクション、それらを列挙する1つの質問、そして列挙されたセットを承認する回答が行われます。
- このターンの調査出力からブリーフィングを構成すること。 エントリー時の
CLEANステータスは永続的なものではありません。古いアンカーを使用すると、ユーザーはあなたが発見すべきだった競合を自分で発見することになります。 - 進行状況のシグナルでマージしないこと。 「계속」、「ok」、「진행」は作業を進めるためのものであり、共有状態の変更に対する同意ではありません。
プロセス
ステップ1:3つのサーフェスすべてを調査する
所見は3つのサーフェスに存在し、これらをすべてカバーする単一のコマンドはありません。ブリーフィングの一文を書く前に、以下の3つすべてを実行してください。
# 1 — CI
gh pr checks <N> --repo <owner>/<repo>
# 2 — 会話タイムライン + レビュー本文 + マージ可否 + プッシュされたHEAD
gh pr view <N> --repo <owner>/<repo> \
--json mergeable,mergeStateStatus,isDraft,headRefOid,reviews,comments
# 3 — インラインレビュースレッド(サーフェス#2には含まれないもの)
gh api graphql --paginate -f query='
query($owner:String!, $repo:String!, $pr:Int!, $endCursor:String) {
repository(owner:$owner, name:$repo) {
pullRequest(number:$pr) {
reviewThreads(first:100, after:$endCursor) {
pageInfo { hasNextPage endCursor }
nodes {
id isResolved isOutdated path line
comments(first:1) { nodes { author { login } body } }
}
}
}
}
}' -F owner=<owner> -F repo=<repo> -F pr=<N> \
--jq '.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved | not)
| {id, path, line, author: .comments.nodes[0].author.login,
body: .comments.nodes[0].body}'
body が投影に含まれている理由は、ラベルがスレッド自体のテキストにあり、ラベルによってステップ2で評価するためです。$endCursor を使用した --paginate は飾りではありません。2台のボットが作業したPRでは100スレッドを超えることがあり、1ページだけでは残りが黙って欠落し、「未解決のスレッドはありません」と読み取られてしまいます。
サーフェス#2も同様に100で切り捨てられ、しかも黙って行われます。 gh pr view --json comments,reviews は comments(first: 100) / reviews(first: 100) をカーソル変数なしで発行するため、どちらかのカウントが100を超えるPRでは、残りは単に存在しないことになります。いずれかのカウントが100行に達した場合は、評価の前に、そのサーフェスをページネーション対応のRESTエンドポイントを通じて再収集してください。
gu api repos/<owner>/<repo>/pulls/<N>/reviews --paginate -q '.[] | {user: .user.login, state, body}'
gu api repos/<owner>/<repo>/issues/<N>/comments --paginate -q '.[] | {user: .user.login, body}'
空の結果はまだ回答ではありません。 それは「未解決のスレッドはありません」と「クエリが間違っています」の両方を同時に意味します。この同じクエリが、スレッドがあることがわかっているPRで行を出力するのを確認するまでは、そう判断できません。そのようなPRに対して一度だけ実行してください。それが、否定の主張の根拠となるポジティブコントロールです。
フォークのクローンでは、すべての調査で --repo にフォーク名を指定する必要があります。裸の gh pr 呼び出しは親リポジトリに対して解決されます(worktree-merge-cleanup → Pre-call checklist を参照)。
CIがペンディングまたは失敗している場合は、ここで止めてください — まだ質問しないでください。検証行がまだ動いているブリーフィングは、ユーザーに推測の承認を求めていることになります。
「承認済み」と書く前に、各人の承認がどのコミットをカバーしているかを読んでください。reviewDecision: APPROVED は後のプッシュでも存続します。レビューアごとにレビューをグループ化し、それぞれの最新の APPROVED レビューを取得します(以前の承認は再承認によって置き換えられます。同じレビューアからの後の CHANGES_REQUESTED または DISMISSED は現在の承認がないことを意味します)。そのレビューの commit_id を headRefOid と比較し、その後の非マージコミット(git log --no-merges <commit_id>..<head>)をパート5で「<人間> によるレビュー未了」としてリストします(コマンドのFailure Modes行を参照)。
マージ状態は許可リストであり、「明らかに壊れていない」チェックではありません。 mergeable が MERGEABLE かつ mergeStateStatus が CLEAN または HAS_HOOKS の場合のみ質問してください。これら2つだけが、コミットステータスがパスしている状態でマージ可能であることを意味します。他のすべての値は質問を停止し、ブリーフィング内でその名前を記載します。
mergeStateStatus | 意味 | 質問してよいか? |
|---|---|---|
CLEAN | マージ可能、コミットステータスパス | はい |
HAS_HOOKS | マージ可能、パス中、プリレシーブフックあり | はい |
UNSTABLE | マージ可能だがパスしていないコミットステータスあり | いいえ — 上記のCI失敗が該当 |
BLOCKED | マージがブロックされている(保護ルール、必須レビュー) | いいえ |
BEHIND | ヘッドリファレンスが最新ではない | いいえ — まずリベース |
DIRTY | マージコミットを正常に作成できない | いいえ — 競合 |
UNKNOWN | 現在状態を判定できない | いいえ — 再ポーリング |
ドラフトステータスはこの列挙型には含まれません。これは別の isDraft フィールドです。そのため、ドラフトPRでも CLEAN と報告されることがあります。isDraft を --json リストに追加し、ドラフトはマージ状態に関係なく準備ができていないものとして扱います。
CLEAN は、ブランチがカットされた後にベースに追加されたコミットについては何も示しません。 それらをカウントし(git fetch origin してから git rev-list --count HEAD..origin/<base>)、PRも変更するファイルをリストします(git diff --name-only HEAD...origin/<base>)。共有ファイルごとに、そのベース差分からPRが変更するシンボルをgrepし、git merge-tree --write-tree HEAD origin/<base> を実行します。シンボルヒットがゼロで終了コードが 0 の場合、「意味的な重複なし」の証拠となり、パート5で引用します。マージ前のリベーストリガー(3つ以上のベースコミット、レビューラウンド終了)が発火した場合は、gh pr update-branch <N> でベースを取り込みます(マージコミット、決して書き換えではありません)。これによりCIとボットレビューが再開されます。新しいHEADで両方が完了するのを待ち、移動したHEADでアンカーを再レビューし、新しいHEADに対してステップ1〜3を再実行してから、質問する前にブリーフィングを再構築します。以前に収集した所見、マージ状態、承認カバレッジは古いHEADを説明するものです。
ステップ2:3つのサーフェスすべてのすべての所見を評価する
未解決スレッド数はブロッカー数ではなく、スレッドサーフェスだけが全所見ではありません。ステップ1で収集した各所見(インラインスレッド、会話コメント、レビュー本文)を、ラベルではなく装飾によって評価します。
| 所見の最初の行 | マージをブロックするか? |
|---|---|
issue (blocking): / suggestion (blocking): | はい — マージを待つ |
issue (non-blocking): / suggestion (non-blocking): | いいえ |
nitpick: / note: | いいえ — 設計上未解決 |
| ラベルなし | 自分で評価する — 本文を読む |
suggestion は issue よりも低いランクではありません。両方とも正確に (blocking) / (non-blocking) のいずれかを保持するため、装飾が判断のすべてであり、ラベルはそれがどのような種類の所見かを示すだけです。ラベルを読むことは、宣言されたブロッカーが「単なる提案」として通過してしまう方法です。
レビュー本文はスレッドではなく、解決できません。 これこそが、それが読まれない理由です。その中の何も開いているようには見えません。CodeRabbitは、アクション可能な所見をインラインコメントではなく、折りたたみ可能なレビュー本文セクションにファイルします。そのため、ステップ1で収集した本文を Actionable、Nitpick、outside diff、🧹 でgrepし、見つけたものを評価します。欠陥を提起する会話コメントも同様に評価されます。いずれかのサーフェスでのブロッキング所見は、インラインのものと同様に質問を停止します。単に、未解決のスレッドを残さないため、ブリーフィングの未解決項目として運ばれます。
評価が完了する前にCodeRabbitのカウントを調整します。本文内の `LINES`: ** マーカーによって所見をカウントし、レビューヘッダーに記載されている総数(例:Major comments: N)と一致させます。それらを1つの会話コメントにグループ化し、Fixed — <sha> (n) / Not fixed (n) / False positive (n) として回答します。これらのカウントの合計がその総数と等しくなければなりません。
ラベルなしのケースは例外ではなく、一般的なケースです。ボットは独自の語彙で記述します。CodeRabbitは独自の重大度行(_🎯 Functional Correctness_ | _🟠 Major_ | …)で始まり、CopilotやBugBotにはそれがありません。ボットの重大度はあなたの評価への入力として扱い、決して評価そのものとして扱ってはなりません。また、「単なるボットだから」という理由で所見をスキップする理由にもなりません。
ブロッキングと評価したスレッドには、マージ前に正当な終了方法が1つあります。修正されてクローズされるか、再評価されます(コメントを (non-blocking) に編集し、Revised — 通知を投稿します)。再評価せずにブロッキング所見をフォローアップに保留すると、すべてのラベルが非ブロッキングと読める間、ブランチはロックされたままになります。
レビューレベルの REQUEST_CHANGES をクリアすることは、別の後のステップです。 その状態はレビューに属し、複数の所見を含む場合があります。そのため、その中のすべてのブロッキング所見が修正されてクローズされるか再評価されるまで、クリアされません。1つの所見だけではクリアされません。最後の1つを再評価することでロックが解除されます。それまでは、レビューは正しくブロックし続けます。
ステップ3:アンカーの Unverified ギャップを引き継ぐ
PRの検証アンカーを開き、その Unverified リストを読みます。「머지 후 실전송으로 확인」と書かれたギャップは、何かがそれを生き延びさせない限り、PRとともに消滅します。
- 次のアクションを指定したギャップのみを表面的に引き継ぎます。次のアクションが明記されていないギャップは引き継ぎません。
- ブリーフィングの一部として、フォローアップ課題を作成するかどうかを尋ねます。
- その回答をアンカーの最終リビジョンに
Carried: #NまたはCarried: none — <사유>としてマージの前に書き込みます。 - その最終リビジョンを書き込むには
ghが必要です。GitHubサーフェスがMCPサーバーのみであるセッションでは、そのサーバーのアクティブなツールリストにコメントのupdateが公開されていない場合、コメント本文を更新できません(想定する前に確認してください)。その場合は、Carried:行をマージコミットまたはPR本文に記述し、決して2つ目のアンカーコメントには記述しないでください。anchor-comment-gate/spec.md→ Procedure for a gh-less session を参照してください。 - フォローアップを自動的にファイルすることは絶対にしないでください。新しい課題には、独自の実装アプローチのレビューが最初に必要です。
- フォローアップがコメントとして記録される場合(
Carried: <comment URL>)、それはマージ後も開いたままになる課題に配置してください。PRのRef:課題(例えば、後でリリースステップによってクローズされるハブ/ umbrella issue)に配置し、決してCloses #N課題には配置しないでください。Closes #N課題はマージ自体によってクローズされ、誰も再オープンしません。PRが開いている間に書かれたスコープ外のメモについても同様です。 - 引き継ぎ課題がマージ後のリリースステップでクローズされる課題(
/releaseでクローズされるハブ課題)である場合、リリースはClosesターゲットとまったく同じように引き継ぎを埋めます。その競合をマージレポートで明示的に述べてください(今すぐリリースして未解決項目を失うか、チェック後にリリースするか)。また、マージ後にCloses #Nターゲットも確認してください。GitHubは、PRがリポジトリのデフォルトブランチにマージされた場合のみそれをクローズするため、デフォルト以外のベース(デフォルトがdevのときにprod)へのPRは、リリースステップのためにそれを開いたままにします。
ステップ4:6パートのブリーフィングを構成する
以下の順序で6パート(このリストが正規の形式です。元々は作者のドットファイルPRワークフローテンプレートから派生しました):
- 変更内容 — PRが何を行うか、マージされた場合(およびマージされなかった場合)にユーザーにとって何が変わるかを1つの平易な文で始めます。次に、スコープ(ファイル、論理的な変更)を示します。課題タイトルを繰り返さないでください。カウントと内部的な略語で構成されたパート1は、回答の代わりに「それは何をするのですか?」という質問を招き、承認ウィンドウをリセットし、完全な再ブリーフィングを必要とします。
- 検証されたこと — 実際の出力。コマンドを引用して示します。
- 検証されなかったこと — スキップ、延期、ブロック、CIペンディング
- リスク / 爆発半径 — これが間違っていた場合に誰が影響を受けるか
- 未解決項目 — ステップ2で評価され、未解決のまま残っているすべての所見(3つのサーフェスすべて)。ステップ3からのギャップ。
- 明示的な質問 — テキスト内の独立した行として
マージを承認しますか?と記述し、その直後にステップ5のAskUserQuestion呼び出しを配置します(ツールが質問をするため、この行は残ります。ゲートがスコアリングする項目だからです)。
パート3はプレッシャーがかかる部分であり、ブリーフィングが存在する理由です。
未検証項目がマージによって開始されるまさにそのステップ(マージ後のデプロイ、CIが実行したことのないアセットビルド、1つのプラットフォームでのみ実行したマルチプラットフォームビルド)である場合、それは脚注ではなくリスクです。そのステップが失敗した場合に何が壊れ、それが誰をブロックするか(同じスタックの後のデプロイなど)とともにパート4に記載します。簿記的な価値(バージョンピン、カタログ、ドキュメント)を持つPRの場合、パート4には、マージされなかった場合に何が起こるかも1行で記載します。ユーザーは、それでもデプロイをトリガーするローリスクなPRを承認する前に、そのことを尋ねます。
パート3のアンチパターン: 「完了しました、マージしますか?」と証拠なし;スキップされたチェックを最後の余談として格下げ;PR準備時に表面化すべきだったことをマージ時に報告。
観察によってパート3の項目をクローズし、読むことではクローズしないでください。 ユーザーが未検証項目の確認を依頼した場合は、項目ごとに、その項目を解決する最も安価な調査を実行します。
[翻訳のため一部省略]