
Summary
この文書の要点
- AIに任せる範囲は、決定的スキャナーで検出でき、patch guardで狭い差分に閉じられる保守作業に限定します。
- GitHub Actionsは日次・週次・月次の起点とCloud Buildの非同期投入だけを担当し、判断とPR作成はCloud Build内のorchestratorに集約します。
- GitHub Issuesは承認場所ではなく、予算予約、token実費、finding fingerprint、試行、PR、失敗を残す監査台帳として使います。
- マージ可否はActionsの起動成功ではなく、GitHub AppがPR head SHAへ投稿するCloud Build最終結果のrequired statusで判定します。
Human-in-the-loopを外す対象を、先に絞ります。
人間を実行ループに入れない、という表現は誤解されやすいです。ここで外すのは、検出、診断、修正案作成、PR作成、CI判定の途中で毎回人間がボタンを押す手順です。要件変更、設計判断、セキュリティ境界の変更、本番データの扱いまでAIへ無条件に渡すという意味ではありません。
対象にしたのは、大規模Webアプリケーションと複数サービスを持つモノレポで、繰り返し発生する保守作業です。多言語リソースの漏れ、空値、日本語残存、placeholder不整合、未使用翻訳key、production debug文、依存方向違反、OpenAPIのsummaryやresponses欠落、docs driftなど、検出条件をコードで書けるものに限定します。
この絞り込みにより、AIは最初から自由に探索する存在ではなくなります。決定的スキャナーがfindingを出し、orchestratorが分類し、必要な時だけAI APIへ限定contextを渡し、patch guardが差分を検査し、CIが合否を決めます。AIの出力は便利ですが、実行ループの中心には置きません。
制約は、AIが失敗した時に止まる形で定義します。
自動保守の設計では、成功時の流れより失敗時の停止条件が重要です。AI APIは同じ入力でも出力が揺れます。コードベースは日々変わります。base branchも更新されます。外部CIやCloud Buildは非同期に動きます。この前提で、人間が途中確認しなくても危険な差分が広がらない境界を先に決めます。
patchは最大8ファイル、500変更行、180KBに制限します。認証、OAuth、Cookie、CORS、migration、secret、infra、lockfile、generated file、未提供context、削除、renameは自動修正対象外にします。この範囲を越えた時点で、AIに追加修正を頼むのではなく失敗として台帳に残します。
実行頻度も制限します。日次タスクは最大2件、週次タスクは最大4件、月次タスクは最大8件まで。同一PRへの試行は7日間で3回まで。変更fingerprintが変わらない失敗は再課金しません。自動化の上限を曖昧にすると、失敗時に費用と差分だけが増えます。
- 扱う対象は、決定的スキャナーで再現できるfindingに限定する。
- AI APIの前に、対象数、最大patch量、除外領域、予算予約のすべてを確認する。
- 危険領域に触れたpatchは、修正候補が正しそうでも破棄する。
- 同じ失敗fingerprintに対して、入力が変わらない再試行を繰り返さない。
Actionsは起点、Cloud Buildは実行制御に分けます。
全体構成では、GitHub Actionsを薄く保ちます。Actionsは日次、週次、月次のスケジュール起点と、Cloud Buildへの非同期投入だけを担当します。Actions内で診断、AI呼び出し、patch適用、PR作成まで抱えると、ログ、権限、再試行、課金制御が散らばります。
実行制御はCloud Build内のorchestratorに集約します。orchestratorは決定的スキャナー、GitHub Issue台帳、AI API、patch guard、branch作成、PR作成、status投稿を順番に制御します。これにより、非同期ジョブのID、finding fingerprint、予算予約、AI実費、patch検査結果、作成したPR、失敗理由を一つの文脈で扱えます。
この分担は、CIの見え方にも効きます。Actionsのジョブが緑になったことは、Cloud Buildの最終結果を意味しません。ActionsがCloud Buildを非同期投入して正常終了しただけでマージ可能にしてしまうと、肝心の修正検証が終わる前にPRが通る可能性があります。
- GitHub Actions: schedule、workflow_dispatch、Cloud Build非同期投入、ログarchive。
- Cloud Build orchestrator: scanner、Issue台帳、AI API、patch guard、PR作成、最終status投稿。
- GitHub Issues: 承認UIではなく、予算、fingerprint、試行、PR、失敗を追う監査台帳。
- branch protection: ActionsではなくCloud Build最終結果をrequired statusにする。
検出から修正までのデータフローを一方向にします。
基本フローは、scan、ledger、reserve、diagnose、patch、guard、PR、CI、statusです。最初に決定的スキャナーがfindingを作り、findingごとにfingerprintを計算します。次にGitHub Issuesへ台帳行を作り、AI呼び出し前に保守的な最大額を予約します。
AI APIへ渡すcontextは、findingの種類、該当ファイル、周辺コード、許可された修正方針に絞ります。大量で定型的な分類や翻訳はGeminiに寄せ、限定contextでのコード修正はOpenAI APIに寄せる分担にします。ここでは具体的なモデル名を固定せず、処理の性質に応じて役割を分けることを設計の中心に置きます。
AIがpatchを返したら、すぐにPRへ進めません。patch guardで変更ファイル数、変更行数、サイズ、禁止領域、削除やrename、generated file、lockfile変更、未提供contextへのアクセスを検査します。通過した差分だけがbranchへ適用され、PRとして作成されます。
- findingはfingerprintを持ち、同じ問題を同じ台帳行で追跡できるようにする。
- 予算予約はAI呼び出し前に行い、実費との差分は完了時に返す。
- 分類・翻訳とコード修正を同じAI呼び出しに混ぜない。
- patch guardを通過できない差分は、修正候補ではなく失敗結果として扱う。
日次は、失敗タスクだけを診断して連鎖修正します。
日次ループの目的は、すべてを毎日直すことではありません。既存PRや直近の自動保守PRについて、commit statuses、GitHub Checks、GitHub Actionsのログarchive、そこから復元した非同期Cloud Build IDを照合し、失敗しているタスクだけを診断します。
非同期CIでは、失敗の出所が複数になります。GitHub ActionsはCloud Build投入ログを持ち、Cloud Buildは最終的なbuild IDと詳細ログを持ち、PRにはChecksとcommit statusが残ります。orchestratorはこれらを照合し、失敗したbuildやcheckに対応するfindingだけを再診断します。
連鎖修正では、PR全体を作り直すのではなく、失敗したタスクに必要な追加patchだけを作ります。同一PRへの試行は7日間で3回までに制限し、同じfingerprintの失敗が続く場合は再課金せず停止します。これにより、CIが赤い時ほど無制限にAIを呼ぶ状態を避けます。
週次と月次は、対象の粒度を変えます。
週次ループでは、コードベース横断の静的監査を行います。多言語漏れ、空値、日本語残存、placeholder不整合、stale key、未使用翻訳key、production debug文、依存方向違反を対象にします。日次より広く見ますが、検出条件は決定的に保ちます。
未使用翻訳keyは、単純な文字列検索だけで削除すると危険です。動的prefixを除外し、同一fingerprintを14日間継続観測してから、日本語と英語を同時に削除します。翻訳keyは片方だけ消えると別の不整合を生むため、削除は対で扱います。
月次ループでは、本番に近い実態を監査します。稼働中Cloud Runの実OpenAPIを取得し、summary、tags、responsesの欠落を確認します。さらにfrontend shell、proxy、deployment登録、docs driftを見ます。月次は実行回数を少なくし、実環境との差分を発見する役割に寄せます。
- 週次: 多言語、placeholder、stale key、未使用key、debug文、依存方向違反を監査する。
- 未使用翻訳key: 動的prefixを除外し、14日間同じfingerprintを見てから日英同時削除する。
- 月次: 稼働中Cloud RunのOpenAPI、frontend shell、proxy、deployment登録、docs driftを監査する。
- 日次、週次、月次で、対象数と最大修正件数を分ける。
予算は、実費集計ではなく事前予約で止めます。
AI APIの費用制御を実費集計だけにすると、失敗した後に高かったことが分かるだけです。この設計では、7日間のrolling budgetを100ドルに設定し、AI呼び出し前に保守的な最大額を予約します。予約できないタスクは実行しません。
予約額は、対象finding数、最大token、想定レスポンス、再試行上限から計算します。実行後はtoken実費を台帳に戻し、予約との差分を解放します。対象が少なければAPI費用も減るため、スキャナーで対象を絞ることが費用削減にも直結します。
GitHub Issuesの台帳には、予算予約、token実費、finding fingerprint、試行回数、作成PR、失敗理由を残します。Issueは人間の承認場所ではありません。自動化が何を根拠に費用を使い、どこで止まったかを後から説明するための監査証跡です。
- 7日間のrolling budgetは100ドルに固定する。
- AI API呼び出し前に最大額を予約し、予約できなければ実行しない。
- 日次2件、週次4件、月次8件を上限にする。
- 対象findingが減れば、予約額と実費の両方が減る設計にする。
branch protectionは、最終結果だけを信じます。
非同期CIで最も危険なのは、起動したことと完了したことを混同することです。ActionsがCloud Buildを投入して緑になっても、それはCloud Buildの検証が成功したことではありません。mainのbranch protectionでは、Actionsの起動成功ではなく、Cloud Buildの最終結果をrequired statusにします。
Cloud Build完了時には、GitHub Appのinstallation tokenを使い、PR head SHAへ`cloud-build/ci` statusを投稿します。required checkはPR head SHAに付く必要があります。test-merge SHAへだけstatusを投稿すると、GitHubのrequired checkとして認識されない場合があります。
CIではPRのtest-merge SHAを検証します。これは、base branchとPR headを合わせた状態でビルドやテストを通すためです。ただし、statusの投稿先はhead SHAです。base branchが更新された場合は、strict branch protectionにより再検証が要求されます。
- 検証対象: PRのtest-merge SHA。
- status投稿先: PR head SHA。
- required status: `cloud-build/ci`。
- base更新時: strict branch protectionで再検証を強制する。
安全境界は、AIの説得力より機械的に優先します。
AIがもっともらしい修正理由を返しても、patch guardに通らない差分は採用しません。認証、OAuth、Cookie、CORS、migration、secret、infra、lockfile、generated file、未提供context、削除、renameは自動修正対象外です。ここはプロンプトでお願いするのではなく、差分検査で落とします。
この境界は、修正できる件数を減らします。しかし、人間が実行ループにいない運用では、修正能力よりも停止能力の方が重要です。自動化は、難しい変更を無理に進めるためではなく、簡単で再現性のある保守を取りこぼさないために使います。
対象外になったfindingは、台帳に残します。残す情報は、なぜ対象外か、どの禁止領域に触れたか、次に人間が見るべき観点です。自動化できなかった事実も、運用上は重要な出力です。
検証は、単体テストだけで終わらせません。
実装検証では、orchestratorやscanner周辺の15 unit testsに加え、構文、YAML、実環境OpenAPI、GitHub App認証、モデル参照、成功と失敗の両status lifecycleを確認します。特にstatus lifecycleは、成功時だけでなく失敗時にrequired statusが赤く残ることを確認しないと意味がありません。
モノレポでは、lockfileの扱いもCI全体に影響します。検証では、262 workspace規模のfrozen lockfile、全Docker build、unit testまで確認しました。未更新lockfileが残ると、対象サービスと関係ないように見える変更でもCI全体が止まります。
OpenAPI監査は、生成ファイルの存在だけでは不十分です。月次ループでは稼働中Cloud Runから実OpenAPIを取得し、summary、tags、responses、docs driftを確認します。デプロイ済み実体を見ることで、リポジトリ上の期待と本番経路の差を検出できます。
- unit tests: orchestrator、scanner、budget、status lifecycleを中心に15件。
- 構文/YAML: workflow、Cloud Build設定、生成される設定断片を検証する。
- 実環境: Cloud RunのOpenAPI、GitHub App認証、モデル参照を確認する。
- モノレポ: 262 workspaceのfrozen lockfile、全Docker build、unit testを通す。
失敗例は、設計の境界を決める材料になります。
一つ目の失敗は、非同期Actionsが先に緑になることです。ActionsがCloud Buildを投入して終了すると、GitHub上では成功に見えます。しかし、Cloud Build本体がまだ実行中であれば、検証は終わっていません。この失敗から、Actionsをrequired checkにしない設計にしました。
二つ目は、status投稿先のSHAです。CIがtest-merge SHAを検証すること自体は正しいですが、required checkとして認識させるstatusはPR head SHAに必要です。merge SHAへだけ投稿すると、検証済みなのにbranch protection上は未完了に見えることがあります。
三つ目は、未更新lockfileです。小さなpackage変更でもlockfileが追従していないと、frozen lockfileでCIが全停止します。AIにlockfileを自動修正させるのではなく、lockfileを自動修正対象外にし、検出された時点で明示的に止める方が安全です。
- 起動成功と最終成功を同じcheckにしない。
- 検証対象SHAとrequired status投稿先SHAを分けて設計する。
- lockfileは影響範囲が大きいため、自動patch対象から外す。
- 失敗を握りつぶさず、次回の停止条件として実装に戻す。
運用監査は、人間の承認より再現性を重視します。
この設計では、人間の承認を毎回挟まない代わりに、後から追える情報を厚く残します。finding fingerprint、scanner version、入力context、予算予約、token実費、AI APIの役割、patch guard結果、PR、CI結果、失敗理由を同じ台帳に残します。
承認がないから危険なのではありません。危険なのは、何を根拠に変更したのか、なぜその費用を使ったのか、どこで止まったのかを後から再現できないことです。GitHub Issuesを監査台帳として使うと、通常の開発者が日常的に見る場所に履歴が残ります。
一方で、Issueコメントを承認UIにしないことも重要です。承認待ちを設けると、実行ループは再び人間の反応速度に依存します。自動化対象は、承認なしで進められるほど狭く定義し、狭くできない対象は最初から自動修正の外に置きます。
この方式が向かない変更も明確です。
この方式は、決定的に検出できる保守作業に向いています。多言語リソース、OpenAPIメタデータ、debug文、依存方向、docs driftのように、検出条件と合格条件を書ける領域では効果があります。
逆に、要件の解釈、画面体験の判断、セキュリティ境界の変更、認証や権限の設計変更、DB migration、インフラ変更、secret管理、本番データ操作には向きません。これらは自動化できないのではなく、狭いpatchと決定的検証だけでは安全性を担保しにくい領域です。
また、CIが遅すぎる環境や、テストがほとんどない環境では、AI以前に停止条件が弱くなります。Human-in-the-loopを外すには、AIの性能より先に、失敗を検出できるCI、必要なstatusを強制するbranch protection、費用を止める予算台帳が必要です。
AIの性能ではなく、止め方が自動保守の品質を決めます。
人間を実行ループに入れないAI開発保守は、AIに広い裁量を与えるほど成立しにくくなります。成立させるには、決定的検出、狭い変更境界、AI APIの役割分担、patch guard、強制CI、branch protection、監査台帳、予算停止条件を先に作る必要があります。
AIは、分類、翻訳、限定contextでのコード修正を速くします。しかし、何を対象にするか、どこまで直してよいか、どのCI結果を信じるか、いくら使ったら止めるかは、AIではなくシステム設計で決めるべきです。
この設計の中心にある判断は単純です。AIが正しそうに見えることを信じるのではなく、AIが間違えた時に小さく止まる構造を信じる。Human-in-the-loopを外すために必要なのは、人間の代わりに判断するAIではなく、人間がいなくても危険な方向へ進まない実行基盤です。


