AIエージェント開発・運用の事故録 #11
全部をOwner承認に
したら、止まった
LOW/MEDIUM/HIGH/UNKNOWNで権限と証拠を変える
公開日:2026年8月25日 著者:飯田 友広(Tomohiro Iida)
この記事の結論
Owner承認を全変更へ一律適用すると、安全性ではなく統合の停止と注意の希薄化が増えます。
LOWとMEDIUMは機械ゲートとControllerで自律統合し、HIGHだけexact-SHA Owner認可と独立FINAL_QCを要求します。
UNKNOWNはfail-closedにし、分類できなかった差分を低リスク側へ落としません。
承認待ちの件数と待機時間を可視化し、Owner判断を不可逆で損失の上限が読めない境界へ限定します。
AIエージェントへ実装を任せる範囲が広がるほど、運用の中心は「人間がどこで承認するか」へ移ります。
Netsujoでは2026年8月21日まで、mainへの統合条件として、branch名に関わらずすべてのPull Requestに対してOwnerのexact-SHA明示認可を要求していました。安全側へ倒した設計でしたが、実際に起きたのは、CIがGREENで、head同一性もfreezeもcollisionも通過した低リスクの変更まで統合が止まる状態でした。
結論を先に書きます。
承認点は増やすほど安全になるわけではありません。リスク区分ごとに、必要な権限とEvidenceを変えます。
Incident Card
INCIDENT:
main統合の条件として、全Pull RequestへOwnerのexact-SHA明示認可を要求していた
SYMPTOM:
CI GREEN、head同一性、freeze、collisionをすべて通過した低リスク変更まで、
Ownerの入力を待って統合が停止した
FALSE_ASSUMPTION:
承認点を増やすほど安全性は上がり、増やしても運用の質は劣化しない
ROOT_CAUSE:
リスク区分と権限設計が結びついておらず、単一の承認契約を全差分へ一律適用していた
IMMEDIATE_FIX:
exact-SHA認可の契約そのものは維持し、適用範囲をhigh riskへ限定する
SYSTEM_FIX:
LOW / MEDIUM / HIGH / UNKNOWNを機械判定し、区分ごとに統合経路とEvidenceを変える
REMAINING_RISK:
分類パターンの漏れ、リポジトリ構造の変化、承認在庫の不可視化Ownerが、常にcritical pathへ載った
観測できた事実は単純です。Ownerが承認文字列を書くまで、mergeできるPull Requestが一件も進みませんでした。
当時の承認契約は次の形です。Pull Request本文へ、対象PR番号と40桁のcommit SHAを組にした認可行を残します。
owner_merge_authorized(pr=<PR番号>, sha=<40桁のcommit SHA>)実際の書式は各リポジトリで決めます。ここで重要なのは書式ではなく、認可がPR単位ではなくSHA単位で効くという性質です。
この契約自体は良い性質を持ちます。PR番号だけでは、あとからcommitが積まれたときに承認対象がずれます。40桁SHAまで書けば、headが動いた瞬間に承認は自動的に失効します。
問題は適用範囲でした。この印を全Pull Requestへ要求すると、次のような変更まで同じ待ち行列へ入ります。
- ドキュメントの誤字修正
- テストの追加
- 記事本文の文言修正
- READMEの更新
これらはCIで真偽を判定でき、失敗しても本番の顧客影響がありません。それでも、Ownerが手を動かすまで統合されませんでした。
リポジトリの実装には、当時の状況が次のように記録されています。「CI GREEN・exact identity・freeze・collision等をすべて通ったlow / mediumまでOwnerが毎回critical pathになり、統合作業が大量に停止した」。
誤った前提:承認を増やせば、安全性だけが増える
承認点の追加は、導入時のコストがほぼゼロに見えます。設定を一行足すだけで、事故の可能性が一つ減るように見えます。
実際には、承認点は二つのコストを同時に生みます。
- 待ち時間 — 承認者が確認するまで、後続のすべてが止まります。
- 注意の希薄化 — 承認対象が増えるほど、一件あたりに割ける確認時間は減ります。
二つ目のほうが危険です。1日に30件の承認要求が届き、そのうち28件が誤字修正なら、残り2件の本当に危険な変更へ向ける注意も同じ粒度になります。承認が形式化した状態は、承認が無い状態より危険な場合があります。承認済みという記録だけが残るためです。
「安全側に倒した」という言い方は、コストが片側にしか存在しないときだけ正しくなります。承認設計はその条件を満たしません。
根本原因:リスク区分と権限設計が結びついていなかった
事故の原因は、Ownerが遅かったことでも、AIエージェントが信用できなかったことでもありません。変更のリスクを判定する仕組みと、誰が何を認可するかを決める仕組みが、別々に存在していたことです。
リポジトリにはリスク分類器が既にありました。にもかかわらず、統合の可否はその分類結果を参照せず、単一の承認契約だけを見ていました。分類はレポート用の情報で終わり、権限へ接続されていませんでした。
同じ構造は、AIエージェントを使わない開発でも起こります。CODEOWNERSを全ディレクトリへ設定した状態が近い例です。
その場の対処:契約は維持し、適用範囲だけを狭める
最初に行った修正は、承認契約を弱めることではありません。契約はそのまま残し、適用範囲をhigh riskへ限定することです。
| 変更前 | 変更後 |
|---|---|
| 全Pull Requestへexact-SHA認可を要求 | high riskのPull Requestだけへ要求 |
| low / mediumも人間の入力待ち | low / mediumは既存の機械ゲートで自律統合 |
| 判定できない差分の扱いが未定義 | 判定できない差分はhigh側へ倒す |
現在の実装では、Owner FINAL_QCの要否は次の一行で決まります。lowとmedium以外はすべて必要、という書き方です。
requiresOwnerFinalQc(risk):
return risk != 'low' and risk != 'medium'否定形で書いてあることには意味があります。新しい区分が増えたとき、既定でOwner承認が必要な側へ落ちます。「highのときだけ必要」と書くと、未知の値がOwner承認なしで通ります。
仕組みへの変換:4区分で権限とEvidenceを変える
現在の設計は次のとおりです。
| 区分 | 統合経路 | 必要なEvidence |
|---|---|---|
| LOW | Controllerによる自律統合 | CI GREEN、head同一性、freeze、collision不在 |
| MEDIUM | Controllerによる自律統合 | LOWと同じ機械ゲート一式 |
| HIGH | 自動統合の対象外 | exact-SHA Owner認可+独立FINAL_QC review |
| UNKNOWN | 統合しない | 分類が成立するまで進めない |
HIGHの判定は、パスのパターンで機械的に決めています。実装に列挙されているのは、workflow定義、エージェント運用契約、governance、品質ゲート設定、package定義、デプロイ設定、DBマイグレーション、認証・認可・課金・セッション・顧客データアクセスの各モジュールです。
LOWは逆に、ドキュメント、README、markdown、テストファイルのように、変更しても本番の顧客挙動が変わらないパスだけを列挙しています。
そして、どちらのパターンにも当てはまらない差分はMEDIUMになります。分類できないものをLOWへ落とさない設計です。
UNKNOWNをfail-closedにする
最も重要なのは、判定に失敗したときの既定値です。現在の実装は、次のすべてをhigh扱いにします。
- 変更ファイル一覧が空で返ってきた
- APIの応答が想定と異なる形だった
- PR番号やリポジトリ名の入力が不正だった
- 例外で分類処理が終わらなかった
いずれも OWNER_FINAL_QC_POLICY_UNVERIFIED のようなコードを付けて、requiredをtrueで返します。観測できなかったことを、安全だったことと同じに扱いません。
品質ゲートの設定ファイルにも、同じ思想が明文化されています。分類や機械検査のPASSは、実行の許可ではないという規則です。観測できないtelemetryは推測で埋めず、UNKNOWNのまま記録します。
HIGHの承認は、自己申告では成立しない
HIGHへ落ちた変更は、Owner認可があるだけでは統合されません。独立したFINAL_QC reviewを別に要求します。
review本文には次の2つが必要です。
REVIEW_STATUS: PASS
(レビュー本文へ、対象の40桁commit SHAを示すmarkerを添える)判定は、この40桁がGitHub側のreview.commit_idと一致するかどうかで行います。SHAは実装者が自由に書ける文字列ではなく、GitHubが記録する不変値と突合されます。新しいcommitを積めば、古い認可は自動的にstaleになります。
Netsujoの運用では、この判定を通ったあとも、実装・merge・deploy・本番検証を別々の状態として報告します。deploy成功だけでは完了としません。
承認の在庫を可視化する
リスク区分を導入しても、HIGHの件数がゼロにはなりません。むしろ、本当に危険な変更だけがOwnerの手元へ集まります。
そこで必要になるのは、いま何件がOwner待ちなのかを一覧で見られる状態です。Netsujoの現在の実装では、Controllerが1回の実行で最大1件だけを統合し、GitHub上のsingle writerとして直列化します。統合可能性が変わったイベントでのみ起動し、6時間ごとのscheduleは取りこぼしの回復に限定しています。
一方、承認待ちを一覧化する専用のinventoryは未実装です。現時点で運用が把握しているのは、Controllerの実行ログとPull Request一覧までです。次の項目を定期的に出力する設計を検討しています。
- HIGH判定で承認待ちのPull Request件数と待機時間
- 承認済みだが、そのあとheadが動いて失効した件数
- UNKNOWNで停止した件数と、その分類失敗コード
- LOW / MEDIUMで自律統合された件数
承認在庫が見えないと、「Ownerがボトルネックになっている」という状態を、体感でしか検知できません。件数と待機時間を出さない限り、区分の閾値が妥当かどうかも評価できません。
再利用できるチェックリスト
承認設計を見直すとき、次の順序で確認します。
- その変更が失敗したとき、不可逆か。取り消せるなら人間の事前承認は必須ではありません。
- 失敗したときの損失の大きさはどれくらいか。顧客データ・課金・認証は上限が読めません。
- その判定は機械で決定的に行えるか。行えるなら人間ではなく検査へ渡します。
- 判定に失敗したとき、既定でどちらへ倒れるか。倒れる先が安全側でなければ設計が未完成です。
- 承認対象は、承認者が実際に読める件数に収まっているか。
- 承認は、承認した瞬間の状態に束縛されているか。SHAへ束縛されていない承認は、あとから対象がずれます。
- 承認待ちの件数と待機時間を、あとから数えられるか。
3番目で人間を外せる項目が残っている限り、承認点を増やしても安全性は上がりにくくなります。
Evidence
| 内容 | 分類 | 根拠 |
|---|---|---|
| 全PRへexact-SHA認可を要求した結果、low / mediumの統合が大量に停止した | OBSERVED | リポジトリの scripts/agent-os/integration-controller.mjs の実装コメント |
| exact-SHA認可の適用範囲をhigh riskへ限定した | IMPLEMENTED | scripts/agent-os/integration-controller.mjs の ownerMergeAuthorization |
| low / medium以外はOwner FINAL_QCを必須にする | IMPLEMENTED | scripts/agent-os/owner-final-qc-policy.mjs |
| 分類・観測に失敗した場合はhigh扱いでfail-closedにする | IMPLEMENTED | scripts/agent-os/owner-final-qc-policy.mjs の各失敗コード |
| ファイルパスでLOW / MEDIUM / HIGHを機械判定する | IMPLEMENTED | scripts/agent-os/integration-controller.mjs の classifyRisk |
| 分類も機械検査のPASSも実行の許可ではない | IMPLEMENTED | .quality/codex-review-policy.json の executionAuthorization |
| ルールに一致しない差分はLOWへ落とさずMEDIUMにする | IMPLEMENTED | scripts/quality/classify-change-risk.mjs |
| FINAL_QC認可はGitHubのreview.commit_idと突合し、自己認可を許さない | IMPLEMENTED | scripts/agent-os/final-qc-authorization.mjs |
| Controllerは1回の実行で最大1件だけ統合する | IMPLEMENTED | .github/workflows/agent-integration-controller.yml |
| Workerはmergeを実行せず、統合はControllerへ集約する | IMPLEMENTED | リポジトリの AGENTS.md |
| 本番反映はworkflow_dispatchと確認文字列の入力を要求する | IMPLEMENTED | .github/workflows/deploy-signal-production.yml |
| Git連携による自動デプロイは全branchで無効 | IMPLEMENTED | vercel.json の git.deploymentEnabled |
| 承認待ち件数・待機時間を一覧化するinventory | PROPOSED | 未実装 |
| 区分導入によるリードタイム短縮の定量効果 | INFERRED | 待機時間の継続計測が未実施 |
適用限界
この設計は、次の条件を満たす環境を前提にしています。
- 変更差分がファイルパスで観測でき、リスクとパスに一定の対応がある
- CI・型検査・テストが、LOW / MEDIUM区分の変更について実質的な検出力を持つ
- 統合を担う単一のwriterが存在し、直列化できる
- 承認者が、HIGH区分の内容を実際に読んで判断できる
パスとリスクの対応が弱いリポジトリでは、分類器のパターン列挙が機能しません。その場合はMEDIUM以上へ倒す範囲を広げます。
また、Netsujoが列挙しているHIGHのパスは当社のリポジトリ構造に依存します。当社の運用目安であり、普遍的な正解ではありません。
数値についても限界があります。区分導入の前後で統合リードタイムがどれだけ変わったかは、現時点で計測していません。停止していた状態が解消したことは観測できますが、削減幅は未計測です。
残るリスク
区分を分けても、次は残ります。
- 新しいディレクトリが追加され、HIGHのパターンに載らないまま高リスクの変更が入る
- LOWに列挙したパスへ、実際には挙動を変えるコードが置かれる
- 分類器のパターン変更自体が、レビューを受けずに入る
- HIGHの件数が増え、Ownerの確認が形式化する
- 承認済みSHAのあとにcommitが積まれ、失効に気づかないまま待ち続ける
- 自律統合された変更に、CIが検出できない欠陥が含まれる
最後の項目は特に重要です。LOW / MEDIUMの自律統合は、機械検査の検出力を信頼する設計です。検出力が落ちれば、区分の安全性も落ちます。テストが主張どおりの検出力を持つかどうかは、テストの数ではなく、変異を入れて落ちるかどうかで確かめます。
まとめ
Ownerの承認は、開発を安全にする道具であって、開発を止める道具ではありません。
LOW / MEDIUM
→ 機械ゲート + Controller自律統合
HIGH
→ exact-SHA Owner認可 + 独立FINAL_QC
UNKNOWN
→ 統合しない(fail-closed)この3行を先に決めてから、どのパスがどの区分へ入るかを列挙します。順序を逆にすると、承認点だけが増えて、判断の質は上がりません。
Owner判断は、不可逆で、損失の上限が読めない境界へ限定します。それ以外は検査とControllerへ渡します。
シリーズの他の回はAIエージェント開発・運用の事故録にまとめています。
よくある質問
リスク区分を分けると、監査で説明できなくなりませんか
区分ごとの経路と必要Evidenceを事前に定義し、判定をコードに置けば、どの変更がどの経路で統合されたかを機械的に再現できます。全件を人間が承認した記録より、区分と判定根拠が残るほうが説明しやすい場合があります。
LOWと判定された変更にも、人間のレビューは必要ですか
必要かどうかは目的で変わります。Netsujoの運用では、LOW / MEDIUMもCI・型検査・テスト・各種checkを通します。人間のレビューを必須の統合条件から外しているだけで、レビュー自体を禁止していません。
承認をSHAへ束縛すると、修正のたびに再承認が必要になりませんか
必要になります。それが意図した挙動です。承認したコードと統合するコードを一致させるため、headが動いた時点で古い認可は失効します。再承認の頻度を下げたい場合は、HIGH区分の変更を小さく分けます。
UNKNOWNをfail-closedにすると、作業が止まりませんか
止まります。ただし止まった件数と理由が記録に残るため、分類のパターン不足として修正できます。UNKNOWNをLOWへ倒すと、止まらない代わりに、何が通過したかを後から特定できなくなります。
この設計はAIエージェントを使わない開発にも使えますか
考え方は共通です。判定を機械で行えるものを人間の承認から外し、不可逆で損失の大きい境界へ人間を集中させる、という構造は開発体制の種類に依存しません。ただしHIGHのパス列挙は、リポジトリごとに作り直す必要があります。
この記事の著者

飯田 友広
代表取締役
Netsujo株式会社 代表取締役。京都発のWeb3・AI実装スタートアップを2023年6月に創業。Webサイトを営業基盤として捉え、経営・営業・検索・生成AI・コンバージョン・計測を横断して課題と改善優先順位を整理する「Netsujo SIGNAL」を設計・運営。京都ビッグデータ活用プラットフォーム参画(小規模企業会員・ベンチャー)、同プラットフォーム発のワーキンググループ「Chain Up KYOTO」参画(2026-03-10)、IVS2026サイドイベント「なぜ京都でWeb3.0ビジネスなのか」を京都府庁旧議場で開催(2026-07-02・Netsujoとして主催、企画・登壇・運営/京都府デジタル政策推進課は共催)。京都美術工芸大学での講義・龍谷大学でのセミナー実績、ITコミュニティ「みやこでIT」(connpassメンバー614名・イベント167件・2019年2月から運営)運営。NPO法人NEMTUS理事、BAR KRYPTO運営。ソーシャル企業認証「S認証」認証企業(2026年2月認証・2026年4月公表)。技術領域はWeb3/ブロックチェーン/DID/NFT/生成AI/コミュニティ運営。
プロフィールを見るこの記事が向いている方
AIエージェントを開発へ組み込み、mainへの統合条件を設計している事業責任者・開発責任者
承認点を増やした結果、リリースが滞っているチーム
どの変更を人間が承認し、どの変更を機械検査へ任せるかを決めたいITエンジニア
— 壁打ち相談
読者のよくある相談
記事を読んだ後に「自分の状況だとどう判断すべきか」を整理するための壁打ち相談を受け付けています。下記のような相談例が当てはまる方は、お気軽にご連絡ください。
Q. 承認を増やしたのに、安全になった実感がない
リスク区分ごとに承認の要否を分け、機械で判定できる項目を人間の承認から外します。
Q. 低リスクの変更まで人間の入力待ちで止まっている
exact-SHA認可の契約は維持したまま、適用範囲をhigh riskへ限定します。
Q. 分類できない差分をどちらへ倒すか決まっていない
UNKNOWNをfail-closedにし、観測できなかった状態を安全と同じに扱いません。
上記いずれかが該当する場合、初回30分の壁打ち相談で論点整理に対応します。記事に書ききれない個別事情を踏まえた判断材料が必要な段階こそ、壁打ちが活きやすいフェーズです。