PR 会話
プルリクエストは、もともとコードについての会話が行われる場所です。PR 会話は、チャットブリッジが Slack をそうするのと同じように、そこを Control Center ワークスペース上のサーフェスにします。PR 上でサーバーの GitHub App ボットを @メンションする、そのレビュースレッドの中で返信する、あるいは ai-review ラベルを付けると、作業はその PR のレビュースペースで行われます — アプリ内で入力したものと同じ帰属とガードレールのもとで。コメントはメンバーシップによっても制限されますが、ラベルはそうではありません。作者が存在しないからです。ボットの回答は、質問が投げかけられたレーンで、GitHub 上に返ってきます。
この機能を形づくる判断は 3 つあり、いずれもチャットブリッジがチャットに対して下したものと同じ判断を、コードホスト向けに言い直したものです。接続がどちらの方向に張られるか、外部から届いたコメントを何が認可するか、そしてどちらの側が誰に帰属されるかです。
ポーリングがトランスポート
「ポーリングがトランスポート」というセクションGitHub がアプリに対して提供する唯一のプッシュチャネルは Webhook であり、Webhook には受信用 URL が必要です — このサーフェスが意図的に決して要求しない、まさにそのものです。そこで検出はスイープで行います。1 分おきほどに、アプリのインストールごとに 1 つの別名検索が GitHub に対して、ボット(またはその短縮スラッグ)に言及している PR とレビューラベルが付いたオープン PR を問い合わせ、すでにレビュースペースを持つ PR はコメントスレッドを読み取り、すべては前回のスイープで見たものとコメント id で差分比較されます。
設計上の帰結には名前を付けておく価値があります。
- レイテンシはイベントではなく、スイープ間隔です。 👀 の確認リアクションは 1 分ほどで付きます。緊急のものがこれに依存することはありません。
- コメントごとにちょうど 1 回。 コメント id は GitHub 全体で一意です。各 id は、対象として分類された瞬間、何かが作用するよりも前に記録されます。クラッシュによって失われうるのはコメントの処理であって、二重に発火することはありません。
- オフライン中の時間は再生されるのではなく、取り戻されます。 既読セットは永続化されるため、再起動すると停止中に届いたものが配信されます。唯一の例外は最初のスイープです。差分を取る記憶がないため、未処理のものはすべて黙って記録されます — このサーバーが存在する前に 20 件の PR にラベルを付けたオペレーターが、戻ってきたら 20 件のレビューが走っていた、ということになってはなりません。
- ラベルはイベントではなく、集合です。 検索が報告するのはラベルの_存在_であるため、ラベルが付いた PR が発火するのはただ一度きりです。ラベルを外してから付け直してもスイープには見えません。再実行の経路はメンションです。
検索はインストールアカウントごとに、そのオーナー自身のクライアントで 1 回実行されます。インストールトークンには自分のインストールのリポジトリしか見えないためです — サーバー全体で 1 つの資格情報を使うと、アプリの他のインストール配下の PR をすべて黙って見落とすことになります。
認可するのはメンバーシップであり、コメントではない
「認可するのはメンバーシップであり、コメントではない」というセクションPR コメントが運んでくるのは、作者のログイン名程度のものです — 証明されるまでは未認証の外部アイデンティティです。ボットはそれを、ワークスペースのメンバーと接続済み GitHub アカウントの逆引きインデックスに照らして解決します。このインデックスは TTL で再構築されます。サーバーの稼働中にも人がサインインしたり招待されたりするからです。
フェイルクローズドははしごのような形をとり、どの段も、拒否したそのスレッドの中で答えます。
- 未知のログイン名 — リンクされていないか、このワークスペースのメンバーでない人によってリンクされている — には、サインインして自分が所属していることを確認するよう伝えます
- 読み取り専用のメンバー(
viewerまたはguest)には、そのロールでは作業を開始できないと伝えます
2 つの未知ログインのケースは 1 つの返信を共有します。逆引きインデックスが返すのは_この_ワークスペースのメンバーだけなので、リンクされていないアカウントと、リンク済みだが非メンバーであるアカウントは同じ「該当なし」です。いずれも先へは進みません。コメントが送信者以外の誰かに帰属されることはなく、部外者の言葉が、その人が見られないワークスペースに入り込むこともありません。
ai-review ラベルには解決すべき作者がいないため、このはしごを登りません。これを付けると、すでにそのリポジトリをリンクしているワークスペースでレビューが開始されます。
意図的に 2 つのアイデンティティ
「意図的に 2 つのアイデンティティ」というセクション会話の各側は異なるアイデンティティを持ち、その違いこそがこの機能です。
- GitHub 上では、ボットはサーバーです。 ブリッジが投稿するすべてのコメント、リアクション、返信は、アプリのインストールトークンで行われます。誰もクライアントで何かをクリックしていないので、誰かのアカウントを借りることはありません — Webhook とポーリングを人の資格情報から切り離しているのと同じルールです。
- スペース内では、問いかけはあなたのものです。 メッセージは、作者が解決されたメンバーに帰属され、エージェント実行はその人の代理として実行されます。コミットにはその人が共作者として入り、スペースの自律度とアクションガードレールは、アプリ内で入力したメッセージとまったく同じように適用されます。実行からのコードホストへの書き込みは、上の箇条書きと同じルールで、あくまでアプリとして送出され、メンバーの個人トークンでは行われません。
レビューの指摘事項を GitHub に公開するかどうかは、Control Center で人が下す判断のままです。メンションはレビューを_開始_できますが、公開するのは人間だけです。
ループガードと短縮名
「ループガードと短縮名」というセクションボットアカウントが作成したものはすべて、どのゲートが走るよりも前に捨てられます — 具体的には自分たちのもの、そして [bot] サフィックスを持つすべてです。これにより、別のアプリのレビューボットがこちらのボットを呼び覚ますこともできません。これがなければ、ボット自身の回答が新しいコメントとして届き、永遠に再トリガーし続けることになります。
[bot] サフィックスは、短縮形が存在する理由でもあります。GitHub はアプリアカウントのログイン名を <slug>[bot] に固定し、そのオートコンプリートを提供せず、@slug をアプリに解決することもありません — そのため、素のスラッグを受け入れるかどうかは_私たちの_マッチャーの判断であり、それを見つけるには GitHub のメンションインデックスではなく、生のコメント本文検索レーンが必要です。このコストは滑らかにされるのではなく、明示されます。アプリのスラッグと同じ文字列を持つ人間のユーザーは、自分自身を短縮形でメンションするたびにボットに話しかけることになります。アプリ名が特徴的であれば、この衝突は仮定の話にとどまります。
意図的に提供しないもの
「意図的に提供しないもの」というセクション- 自動公開はしません。 指摘事項はレビュースペースに届き、GitHub への公開は別の、人間による操作です
- 曖昧なアイデンティティはしません。 作者はちょうど 1 人のメンバーに解決されるか、拒否されます — 推測による帰属はありません
- GitHub App の Webhook は使いません。 このサーフェスは Webhook を一切登録しないため、PR コメント用に公開する受信用 URL はありません。他の製品 Webhook(パイプライン、チケット同期)は、別の任意の設定です。
関連項目
「関連項目」というセクション- GitHub PR から Control Center に話しかける: これらすべてのハウツー
- チャットブリッジ: 同じ 3 つの判断をチャット向けに下したもの
- GitHub App を作成: ボットのアイデンティティがどこから来るか
- マルチプレイヤー — アイデンティティ、メンバーシップ、プレゼンス: ログインの解決が通るプリンシパルとロール
- ガードレール: 応答するエージェントに許可されること
- AI レビューを使う: メンションやラベルが開始するパイプライン