金曜日の午後4時に修正をプッシュした。土曜日の朝には、アラートが鳴り響いている。原因を辿ると、18時間前にマージされたプルリクエスト(PR)に行き着く。ブランチはコンパイルされ、テストもパスしていたが、PRの説明欄は空だった。紐付けられたワークアイテムもなく、承認履歴も何も残っていない。あなたは「ゴースト・マージ」を目の当たりにしており、メインブランチに反映される前に防げたはずの混乱を片付けるために、週末を潰している。

小規模なチームは、日々このリスクと隣り合わせだ。すべてのマージを監視するリリース・マネージャーがいるわけでも、独自のポリシー・エンジンを構築するプラットフォーム・チームがいるわけでもない。あるのは Azure DevOps だけだ。そして、その組み込みのブランチ・ポリシーは、融通の利かない道具だ。ドアを完全に閉ざすか、あるいは全開にするかのどちらかしかない。2人のレビュアーを必須にすれば、緊急のホットフィックスがブロックされる。ルールを緩めれば、チケットのない変更とともに、中身のない説明文が本番環境へと流れていく。中間の選択肢は、ほとんど存在しない。

そのギャップこそが、私たちが Gatekeeper を構築した理由だ。これは Azure DevOps 専用に設計された AI 搭載の PR レビュー・デスクだが、現代の開発者ツールに対して抱いている常識はすべて忘れてほしい。Docker コンテナも、サブスクリプション・プランも、クラウドへのデプロイ・パイプラインも存在しない。Gatekeeper は、たった一つの HTML ファイルだ。ブラウザで開き、4つの値を入力して、ボタンを押すだけだ。すると、ツールは次の3つのシンプルな問いに答えてくれる。この PR はチケットに紐付けられているか? 人間が実際にレビューしたか? そして、コードの品質は十分か?

すべての機能を単一の自己完結型ファイルにパッケージ化したのは、単なるギミックではない。それは、実際の運用上の悩みを解決するためだ。第一に、ホストするためのインフラも、支払うための費用もゼロだ。App Service をプロビジョニングしたり、データ転送コストを心配したりする必要はない。第二に、認証情報はマシンから外に出ることがない。Azure DevOps の個人用アクセストークンはブラウザのメモリ内にのみ保持され、ページをリフレッシュした瞬間に消滅する。漏洩する恐れのあるシークレットのデータベースも、信頼すべき OAuth サーバーも存在しない。第三に、導入が極めてスムーズだ。Wiki を使って誰かをオンボーディングする必要はない。ファイルをメールに添付するか、Slack のスレッドに投げるだけだ。受け取った人は、それを開いてすぐにレビューを開始できる。

Fact Layer: 決定論を第一に

Gatekeeper はレビューを2つの明確なレイヤーに分割しており、その分離が信頼性のバックボーンとなっている。

第一のレイヤーは、Azure DevOps REST API と直接通信する純粋な JavaScript だ。これは、変化することのない「事実」をチェックする。プルリクエストがワークアイテムに紐付いているか、いないか。レビュアーが承認の票を投じたか、投じていないか。説明文が空か、あるいは実際の文章が含まれているか。アクティブな議論が解決済みか、あるいは未解決のまま残っているか。

具体的には、Fact Layer は次の4つの項目を確認する:

  • Ticket mapping: PR は少なくとも1つのワークアイテムにリンクされているか?
  • Reviewer sign-off: 誰かが承認の投票を行ったか、それともカウントはゼロのままか?
  • Description quality: 説明が空か、あるいは単なる形式的なプレースホルダーか?
  • Open discussions: 返答を待っている未解決のコメントスレッドはないか?

これらのチェックは、ページ上に視覚的なスタンプとして表示される。大きな赤い NOT MAPPED スタンプは、無視するのが難しい。テーブルのセルにひっそりと配置された細い緑のチェックマークは、見落としやすい。私たちは早い段階で、プロセスの失敗には「声の大きさ」が必要であることを学んだ。開発者がマージを急いでいるとき、控えめな表現は通用しない。Fact Layer は、曖昧さを完全に排除するために存在する。

このレイヤーは決定論的な API レスポンスに依存しているため、その正確性は絶対的だ。もしスタンプに NO APPROVAL とあれば、誰も承認ボタンをクリックしていないことは間違いない。もし UNRESOLVED THREADS とあれば、議論はまだ継続中だ。このレイヤーは...