ガードレールを多層で組む — ブランチ保護と git worktree 運用を監査して固めた
ソロ+AI エージェント常用のモノレポで、main のブランチ保護(GitHub Ruleset)と git worktree の運用が本当に妥当かをディープリサーチ報告と突き合わせて監査した記録。報告の推奨をそのまま鵜呑みにせず、この環境(Codespaces / pnpm ハードリンク共有)に本当に効くものだけを適用し、規模が育ってから効くものは Roadmap に後回しした。多層防御(ローカル=利便性 / サーバー側=強制)の設計と、実際に入れたコードを一次情報で残します。
何が問題だったか
このリポジトリは ソロ+AI エージェント常用という、少し特殊な体制で回している。人間が
コードを書く量より、Claude Code / Copilot が書く量のほうが多い日もある。だからこそ
「エージェントが main に直接コミットする」「レビュー指摘を自分で解決してマージを通す」
といった習慣的な事故を、仕組みで止めておく必要がある。
すでに手は打ってあった——main はリポジトリ Ruleset で保護し、ローカルには
.githooks/pre-commit を置き、worktree はヘルパスクリプト(wt:new / wt:rm)で規約どおりに
作らせている。ただ、それらが本当に妥当か、そしてこの環境に固有の落とし穴を踏んで
いないかは、腰を据えて検証していなかった。
そこで、ブランチ保護と worktree 運用に絞ってディープリサーチをかけ、その報告書を 監査のチェックリストとして使い、現状と一つずつ突き合わせた。ポイントは、報告の推奨を そのまま適用しないこと。一般論として正しくても、この repo(Codespaces / pnpm の ハードリンク共有)で本当に効くものだけを入れ、いま効かないものは Roadmap に後回しした。
設計 — なぜ「多層」で組むのか
出発点にした原則はひとつ。ローカルのフックは利便性であって、セキュリティ保証ではない。
git commit --no-verify や GUI クライアント経由で簡単に迂回できる。だから防御は 2 層に分け、
役割をはっきり分離する。
- サーバー側=強制(迂回不可): GitHub Ruleset。
mainへの直 push 禁止・force push / 削除禁止・PR 必須・未解決レビュースレッドがあるとマージ不可。ここは API 側で拒否されるので、 人間だろうと AI だろうと破れない。 - ローカル=利便性(手戻り防止):
.githooks/pre-commit。mainでのコミットを コミットが積まれる前に弾き、「ブランチを切り直せ」と教える。push して初めて Ruleset に 跳ね返される、という無駄な往復をなくすためのもの。
この分離は、少人数体制のレビュー設計にも効いている。承認レビュー数は 0(実質セルフマージに
なる承認要求で速度を殺さない)だが、未解決スレッド解決は必須。機械チェックで拾えない
論理・設計の指摘だけをマージゲートにする、という割り切りだ。ただしこれには穴がある——
AI エージェントが人間の指摘スレッドを自分で Resolve してマージを通せてしまう。ここは
仕組みでなく規約で塞ぎ、リポジトリの AGENTS.md に「人手の監査コメントは人間が解決する」と明記した。
やってみたこと — 適用したもの
報告のうち、この環境に実際に効くと検証できたものだけを入れた。
1. extensions.worktreeConfig で hooksPath 上書きを予防する
いちばん効いたのはこれ。AI エージェントのツールが git worktree を自動生成するとき、共有の
.git/config の core.hooksPath を勝手に書き換え、設定したフックを丸ごと無効化する不具合が
報告されている。ここでは pre-commit フックがこの経路で死ぬと、main 直コミットのガードが
外れる。
対策として、ルートの prepare スクリプトに extensions.worktreeConfig true を足した。
"prepare": "git rev-parse --git-dir >/dev/null 2>&1 && git config extensions.worktreeConfig true && git config core.hooksPath .githooks || true",
prepare は pnpm install のたびに走る。各 worktree でも pnpm install は必ず走るので、
万一 core.hooksPath が上書きされても、次のインストールで .githooks に貼り直される
(自己修復)。worktreeConfig を立てておくのは、worktree ごとに設定を独立管理できるようにする
ための予防線。
2. worktree の pnpm install を --prefer-offline に
共有ストア(/workspaces/.pnpm-store)は worktree と同じ /workspaces ボリューム上にあるので、
依存はハードリンクで解決される。wt:new の pnpm install に --prefer-offline を足し、
レジストリの再検証を省いて新規 worktree の準備を数秒で終わらせるようにした。
後回しにしたもの(Roadmap の TODO に落とした)
報告のうち、いま入れる必要はないが規模が育つと効いてくる一手は、無理に前倒しせず Roadmap の TODO として残した。監査の価値は「入れる」判断だけでなく「今はやらない」判断にもある。
- Lefthook への移行 … いまのシェル製
pre-commitは依存ゼロで軽く、単一のガードには十分。 だがモノレポでリンター・型チェック・整形を並列で走らせたくなったら、Go 製の Lefthook がlefthook.yml一本でそれを賄える。フックが増える前に、が目安。 - Ruleset のさらなる堅牢化 …
required_signatures(署名必須)と、CI の自動修正コミットを GitHub API 経由で Verified 署名する仕組み、.github/workflows/*など壊れると痛い経路への CODEOWNERS。いずれも GitHub 側の設定変更が要るので、コードとは別レイヤーの一手として分けた。
検証
コードに触れた分は、壊れていないことを機械的に確かめた。
pnpm typecheck # 型チェック … パス
pnpm lint # ESLint … パス
git config --get extensions.worktreeConfig # → true
git config --get core.hooksPath # → .githooks
prepare の変更は「pnpm install を一度回すと worktreeConfig と hooksPath の両方が
設定される」ことを確認。wt:new / wt:rm の挙動(作成 → install → 削除 → prune)も一通り通した。
学び
- 研究報告は「チェックリスト」であって「手順書」ではない。 一般論として正しい推奨でも、 自分の環境で成り立つ前提かを一つずつ検証してから入れる。鵜呑みにしてコピペすると、この repo に 効かない設定を触って別のリスクを持ち込みかねない。今すぐ効くものだけを入れ、残りは後回しにする 切り分けそのものが監査の成果だ。
- 強制と利便性を分けると設計が澄む。 破れてはいけないもの(
main保護)はサーバー側の Ruleset に、手戻りを減らすだけのもの(コミット前の警告)はローカルフックに。役割を混同しない。 - AI 常用ならではの穴を先に塞ぐ。 hooksPath の上書き、レビュースレッドの自主解決——人間だけの チームなら起きにくい事故が、エージェントでは「習慣」として起きうる。仕組みで塞げるものは 仕組みで、塞げないものは規約で明示する。次は Roadmap に落とした Lefthook 化と Ruleset 堅牢化を、 必要になったタイミングで一つずつ倒していく。




