この記事はシリーズ「自律運用の土台を 1 本まるごと読む: claude-code-repository-base 全解剖」の第 10 回(全 10 回)です。
Claude Code に毎回同じ指示をしなくて済むように、ルール・フック・スキル・ツールを一式にまとめて公開している自作リポジトリ kai-kou/claude-code-repository-base(MIT)を、作った本人が解説する連載です。設計の意図だけでなく、実際に動かして確かめた結果(自分でも気づいていなかった穴を含む)をそのまま載せます。掲載する実行結果と数値はすべて各回の執筆時点で採取し直し、検証したコミット SHA を各回の冒頭に記します。
自分のリポジトリで再現したい方へ: 同じリポジトリを「どう入れて、どう回して、どう追随するか」の手順書として書いた Zenn Book を公開しています(有料 500 円・試し読みあり)。
シリーズ全体の目次
- 第 1 回 Claude Code に毎回同じ指示をしなくて済むように、運用の土台をリポジトリ 1 本にまとめた
- 第 2 回 git push origin main | tee log で保護が素通りしていたので、コマンド分割で塞ぎ直した
- 第 3 回 ベースを別リポジトリへ配る 2 経路を dry-run で動かす(apply-to-repo.sh と bootstrap.sh)
- 第 4 回 Stop フックを 5 本並べたら最初の 1 本しか読まれなかったので、ルーター 1 本に集約した
- 第 5 回 コンテキスト圧縮で作業が消えるのを、圧縮前後の二段 WIP コミットで防いだ
- 第 6 回 sandbox.enabled を true にしてもクラウドでは bwrap が無く、許可リスト外へ素通りだった
- 第 7 回 「確認してよいですか」を 6 種類に限定したら、それ以外は全部自律実行になった
- 第 8 回 ルールと教訓を増やし続けないために、常駐バイト予算と「昇格=物理削除」を機械強制した
- 第 9 回 複数エージェントが同時に書くホワイトボードを、個別ファイル+単一集約者で壊れなくした
- 第 10 回 10 回分を読み終えたら、自分のリポジトリに最初に持ち込む 3 つはどれか(この記事)
はじめに
この回は、第 1 回(入口)と対になる出口の回です。第 1 回で「全部読む必要はありません」と書きましたが、それは読む側の話でした。持ち込む側でも同じで、9 回分の仕組みを全部まとめて自分のリポジトリへ入れると、どれが効いていて、どれが邪魔をしているのかが分からなくなります。では、まず 1 つ目に何を入れればよいのでしょうか。
対象読者は、連載を通読または拾い読みして、自分のリポジトリへの導入の優先順位を決めたい方です。
最初にお断りしておきます。この回では新しいコマンドの実行も数値の採取も行いません。各回の結論と数値は、その回の本文に載せた値を「第 N 回で確認した通り」という形で出典(記事へのリンク)付きで再掲します。数値の検証時点のコミットは、各回の冒頭に書いてあります。
全 10 回の一覧(持ち帰れるもの・保証レベル・導入コスト)
| 回 | タイトル | 持ち帰れるもの | 保証レベル | 導入コスト |
|---|---|---|---|---|
| 1 | 運用の土台をリポジトリ 1 本にまとめた | 🔒 と 📋 を分けて読む枠組み | 枠組み | なし(読むだけ) |
| 2 | git push origin main | tee log で保護が素通りしていた | セグメント分割+解釈不能は block の push ガード | 🔒 | 低(フック 2 本+登録 1 行) |
| 3 | ベースを別リポジトリへ配る 2 経路 | 祖先 SHA つきの再同期(apply-to-repo.sh) |
🔒 | 中(初回 227 件の配置) |
| 4 | Stop フックをルーター 1 本に集約した | 登録口とメッセージの集約点を 1 つにする形 | 🔒 | 中(サブフック 5 本) |
| 5 | 圧縮前後の二段 WIP コミット | 消えうるイベントの前後両方に保全を置く形 | 🔒(クラウドのみ) | 中(57 行+75 行・発火条件あり) |
| 6 | sandbox.enabled でもクラウドでは素通りだった | 設定ではなく効いている層で判断する習慣 | 🔒 は deny とフック。sandbox はクラウドで無効 | 低(確認は 2 コマンド) |
| 7 | 「確認してよいですか」を 6 種類に限定した | 確認境界 A-1〜A-6 と、2 軸で候補を落とす手順 | 📋(A-1 だけ第 2 回のフックで 🔒) | 低(ルール 1 本) |
| 8 | 常駐バイト予算と「昇格=物理削除」 | 足す摩擦と減らす経路の両方を機械に持たせる形 | 🔒 | 中(ツール 2 本+基準値の取り直し) |
| 9 | ホワイトボードを個別ファイル+単一集約者で | 同じファイルの書き手を常に 1 人にする設計 | 🔒(ファイル構成で回避) | 低〜中(議論型を使う場合のみ必要) |
| 10 | この記事 | 最初の 3 点と、その選び方 | — | — |
保証レベルの列は、README の区分に沿って本回で私が割り振ったものです。導入コストは、各回の本文に出てくるファイル数・行数・前提条件から相対的に付けた目安で、作業時間を測ったものではありません。
表の「保証レベル」列は README の 🔒 と 📋 の 2 段階
第 1 回で紹介した通り、README では導入して得られるものを 2 段階に分けて書いています。
- 🔒 機械的に強制されるもの: フックやスクリプトが実際にブロックする。指示を忘れても事故が起きない
- 📋 運用ルールとして定義されるもの: Claude がルールを読んで従う前提のもの。コードは強制しない
表を見ると、10 回のうち 📋 が主役なのは第 7 回だけで、残りは 🔒 の仕組みです。ただ、🔒 の回も条件つきのものが多く、第 5 回はクラウド実行環境でしか発火せず、第 6 回は設定に書いてあっても効いていない層がありました。🔒 という記号は「機械が判定する」ことを示すだけで、どの環境でも効くことまでは示していません。
最小コストで持ち込む 3 点を選ぶ基準
選ぶときの基準は 3 つにしました。
- 依存する周辺ツールが少ない: 他のフックやツールが揃っていないと動かない、という状態にならない
- 1 ファイル(または数ファイル)で完結する: 入れたものと効いたものの対応が追える
- 効果がその場で確認できる: 入れた直後に、効いているかどうかを自分の目で見られる
この 3 つを優先したのは、最初の 1 つで「入れたのに効いているか分からない」状態を作ると、次を入れる判断の根拠がなくなるからです。表の導入コストが「低」の行から、この基準に合うものを 3 つ選びました。
3 点に順位は付けていません。自分のリポジトリで困っていることに近いものから 1 つ選んでください。
1. main への直接 push を止めるフック(第 2 回)
.claude/hooks/pre-git-push-check.sh は、コマンド文字列を || && ; | や do / done などでセグメントに分け、セグメントごとに push 先を判定します。変数展開や eval を含んで静的に読めないものは block 側に倒します(第 2 回)。base では settings.json の PreToolUse に pre-tool-use-router.sh を 1 本だけ登録し、git と push の両方が単語として現れたコマンドをこのフックへ回しています。
3 点の 1 つ目にしたのは、基準 3 を最もはっきり満たすからです。--self-test を付けて起動すると 50 ケースを流して期待値と突き合わせるので、入れた直後に効いているかを確かめられます。第 2 回で確認した通り、作業ブランチ上では 50 件すべて通り、main 上では git push --tags の 1 件だけが落ちて 49 件通過になります。clone 直後は main にいるので、作業ブランチに切り替えてから回してください。
注意点が 1 つあります。ルーターには、第 6 回で見た秘密ファイルへのアクセス検査(_sensitive_file_access)も同居しています。ルーターごと持ち込むと push ガード以外の検査も有効になるため、どの検査が動くかは持ち込む側で確かめてから使ってください。
2. 確認してよい場面を閉じた表にする(第 7 回)
docs/rules/user-confirmation-minimization.md は、ユーザーに確認してよい場面を A-1〜A-6 の 6 行に閉じたルール文書です。main への直接 push、取り消し困難な公開の即時手動実行、品質ゲート致命的 NG 時の続行、サーキットブレーカー発動後の続行、新規マイルストーンの追加、アカウント・課金設定の変更の 6 つで、それ以外は自律実行とします(第 7 回)。
ファイル 1 本で完結し、他のツールに依存しない点で基準 1 と 2 を満たします。base ではこのファイルを全セッション常駐の Hot 層に入れています(第 8 回で引用した ESSENTIAL_RULES の 13 本の 1 つ)。持ち込むときに書き換えが要るのは A-2 で、第 7 回で書いた通り、この行は配布先の公開手段に合わせて具体化する前提です。
3 点のうち、基準 3 を最も弱くしか満たさないのがこの項目です。📋 なので、効いているかを機械が判定してはくれません。確認できるのは、確認を求められたその場で「この確認は 6 行のどれに当たるか」を自分で照らし合わせることだけです。通知の @mention まで絞りたい場合は、第 7 回で扱った分類器 tools/triage_notification.py を併せて入れると、--self-test(第 7 回の時点で 28 ケースすべて通過)で動作を確かめられます。
真似る価値があるのは 6 という数ではありません。第 7 回の結論の通り、候補を「取り消せない結果に進むか」「ユーザー個人のアカウント権限が要るか」の 2 軸に 1 つずつ当てて落とす手順のほうです。
3. 実行環境で何が効いているかを 2 行で確かめる(第 6 回)
3 つ目はファイルではなく、確かめ方です。which bwrap と、許可リスト外のドメインへの curl の 2 行で、自分の実行環境でサンドボックスの許可リストが効いているかが分かります(第 6 回)。
第 6 回で確認した通り、base の settings.json は sandbox.enabled: true で許可リストは 12 ドメインですが、執筆セッションのクラウド実行環境には bwrap が無く、許可リスト外の https://example.com に HTTP_STATUS:200 で届きました。クラウドで実際に効いていた防御は、セッションコンテナの隔離・permissions.deny・PreToolUse フックの 3 層です。
インストールするものが何もないので、基準 1〜3 をすべて満たします。1 と 2 を持ち込む前に「いま自分の環境で何が止めてくれているのか」を知っておくと、どの層を足すべきかを判断しやすくなります。クラウドで許可リストが効かなかったとしても、それを理由に許可リストを外さないでください。第 6 回で書いた通り、ローカルや配布先の環境では同じ設定が防御として働きます。
最初の 3 点から外したもの
残りの回を外した理由も書いておきます。どれも価値が低いからではなく、基準のどれかに引っかかったからです。
- Stop フックのルーター(第 4 回): サブフックが 5 本あり、第 4 回の sandbox では 1 回の Stop で 3 本が同時に鳴って WIP コミットが 1 つ増えました。副作用があるので、空のリポジトリで一度流してから入れるのが安全です
-
圧縮前後の WIP コミット(第 5 回):
CLAUDE_CODE_REMOTE=trueかつmain/master以外のブランチでしか動きません。クラウド運用でない方には効きません -
常駐バイト予算と教訓の上限(第 8 回): 基準値 86,683B や上限 350 行 / 15 件は base の規模で決めた値です。第 8 回で書いた通り、自分の常駐ルールを
wc -cで測り直して基準を決める手間が先に要ります - 議論ホワイトボード(第 9 回): Python 1 本で完結しますが、要るのは複数エージェントに互いの意見を読ませる議論型を使う場合だけです
3 点より多く入れたくなったら、個別にコピーするより第 3 回の scripts/apply-to-repo.sh を使ってください。--dry-run は 1 ファイルも書き換えずに配置プランだけを出します。第 3 回で確認した通り、初回は 227 件の配置になり、一度適用して祖先の SHA を記録したあとは、ベース側が動いていないファイル 214 件に触れずに済みました(第 3 回)。
フォローアップ: 第 2 回の疑陽性のその後
第 2 回では、pre-git-push-check.sh --self-test の git push --tags のケースが、チェックアウト中のブランチが main のときだけ落ちることを切り分けました。期待値は allow ですが、フラグだけの push は引数なし push と同じ経路で現在ブランチを見るため、main 上では block になります。第 2 回の本文には、当時の状態をこう書きました。
いま言えるのはここまでです。挙動は再現して切り分けましたが、自己テスト側の前提漏れはこの記事の時点ではまだ直していません。見つけた、という段階です。
この回の執筆環境からは base リポジトリの Issue と PR を参照できなかったため、修正 PR が出ているか、マージされているかは確認できていません。未確認なので、修正済みとは書きません。ここで言う確認は、新しいコマンドの実行ではなく GitHub 上の状態を見ることです。現在の状態は、base リポジトリの Issue と PR を push --tags や self-test で検索すると確かめられます。
修正されていない場合でも、これは過剰ブロック側の失敗で、保護の素通りではありません。第 2 回で書いた通り、タグを送りたいときは git push origin refs/tags/v1.0.0 のように送るものを明示すれば通せます。
10 回を並べて見えた共通点: 書いた本人が気づかないずれ
1 回ずつ読んでいたときには見えなかったことが、並べると 1 つあります。動かして確かめた第 2〜9 回の 8 回のうち 7 回で、ドキュメント・コメント・テストのどれかと実際の挙動のずれが見つかっていました。私が各回の本文から数えた件数です。
| 回 | 見つかったずれ |
|---|---|
| 2 | 自己テストの tags ケースだけ、ブランチ依存の前提がテストコードに書かれていない |
| 4 | Stop ルーター冒頭のコメントが「3 つの Stop フック」のままだが、実際の呼び出しは 5 本になっている |
| 5 | フックのコメントが「注入されるのは 3 イベントのみ」と書くが、公式の Hooks reference では例外イベントが増えていた |
| 6 |
security-posture-controls.md が bypassPermissions を true と書くが、settings.json に該当行が見つからない |
| 7 | 通知分類器の判定順が、ドキュメントの要約とコードで逆になっている |
| 8 | 予算推移表の最新列が #504 で止まり、#648 の再校正はログにしか載っていない |
| 9 | ファイル名の時刻を <ns> と書いているが、生成コードは秒単位 |
どれも書いたのは私自身で、設計した本人が読み直しても気づかず、動かして初めて分かったものです。持ち込む方に伝えたいのはここで、記事やドキュメントに「機械的に強制される」と書いてあっても、その保証は自分の環境で 1 回回すまでは仮説として扱ってください。3 点を選ぶ基準に「効果がその場で確認できる」を入れたのも、このためです。
表記を割らないための索引(docs/CONTEXT.md)
ずれの中には、挙動ではなく言葉のずれもあります。base では 既約境界外リスト という語が 4 通りの表記に分かれてしまったことがあり(#490)、その対策として docs/CONTEXT.md という索引を置いています。
この索引は定義を持ちません。定義は正本のルール文書に置いたままにし、索引には正規の表記と、使わない表記(_Avoid_)だけを並べます。新しい運用語彙を書く前に grep して既存の表記に揃え、表記が実際に割れた語だけを足す運用で、長くするほど良いものではないと lessons-management.md に書いてあります。定義まで索引に持たせると、索引と正本の 2 か所で定義がずれる余地が生まれるので、索引の役割を表記の統一だけに絞りました。
なお docs/CONTEXT.md は base 側だけに置いている索引で、apply-to-repo.sh では下流へ同期しません。真似る場合は、自分のリポジトリで表記が割れた語から書き始めてください。
本連載で扱わなかった gh 403 と native-fallback
第 4 回で stop-pr-check.sh がクラウドでは gh の代わりに MCP での確認を案内する、と書いたところで、gh がクラウドで使えない事情そのものは別の回に回しました。base では、Web 版で提供されていない機能を扱うために tools/native_fallback.py を置いています。probe がネイティブ機能の可用性を判定して終了コードで返し(0 なら利用可、3 ならセッション内での追加確認が必要、4 なら利用不可でフォールバックへ)、利用できなければ起動経路を Skill → Workflow → セッションツール → claude -p → 最終手段の順に 1 段ずつ降格させる設計です。この連載で扱わなかったのは、可否がツールのバージョンやプロキシの許可範囲で変わる話で、執筆時点の結論がすぐ古くなるためです。詳しくは別の記事で扱う予定です。
おわりに
冒頭の問い、最初に何を 1 つ入れればよいのかに答えると、10 回分すべてを真似る必要はありません。表を見て、自分のリポジトリの困りごとに近い行を 1 つ選んでください。取り消せない push が怖いなら第 2 回のフック、確認で無人実行が止まるなら第 7 回の境界、どの防御が効いているか分からないなら第 6 回の 2 行です。
どれを選んだ場合も、入れた直後に自分の環境で 1 回回すところまでを導入の一部にしてください。この 3 点を入れれば事故が起きなくなる、とは言えません。私自身、自分で書いた仕組みのずれに 7 回気づかされています。言えるのは、この 3 点が導入コストの低い順に並べたときに先頭に来る、というところまでです。
10 回お付き合いいただき、ありがとうございました。
関連記事
- ルールと教訓を増やし続けないために、常駐バイト予算と「昇格=物理削除」を機械強制した(連載第8回)
- 「確認してよいですか」を 6 種類に限定したら、それ以外は全部自律実行になった(連載第7回)
- Stop フックを 5 本並べたら最初の 1 本しか読まれなかったので、ルーター 1 本に集約した(連載第4回)
参考リンク
- kai-kou/claude-code-repository-base(MIT)
- 第 1 回 Claude Code に毎回同じ指示をしなくて済むように、運用の土台をリポジトリ 1 本にまとめた
- 第 2 回 git push origin main | tee log で保護が素通りしていたので、コマンド分割で塞ぎ直した
- 第 3 回 ベースを別リポジトリへ配る 2 経路を dry-run で動かす(apply-to-repo.sh と bootstrap.sh)
- 第 4 回 Stop フックを 5 本並べたら最初の 1 本しか読まれなかったので、ルーター 1 本に集約した
- 第 5 回 コンテキスト圧縮で作業が消えるのを、圧縮前後の二段 WIP コミットで防いだ
- 第 6 回 sandbox.enabled を true にしてもクラウドでは bwrap が無く、許可リスト外へ素通りだった
- 第 7 回 「確認してよいですか」を 6 種類に限定したら、それ以外は全部自律実行になった
- 第 8 回 ルールと教訓を増やし続けないために、常駐バイト予算と「昇格=物理削除」を機械強制した
- 第 9 回 複数エージェントが同時に書くホワイトボードを、個別ファイル+単一集約者で壊れなくした
シリーズの前後の記事
- ⬅️ 前の記事: 第 9 回 複数エージェントが同時に書くホワイトボードを、個別ファイル+単一集約者で壊れなくした
- ➡️ 次の記事: なし(全 10 回・この記事で完結です)
公開済みの回(8 本・どの回からでも読めます)
- 第 1 回 Claude Code に毎回同じ指示をしなくて済むように、運用の土台をリポジトリ 1 本にまとめた
- 第 2 回 git push origin main | tee log で保護が素通りしていたので、コマンド分割で塞ぎ直した
- 第 3 回 ベースを別リポジトリへ配る 2 経路を dry-run で動かす(apply-to-repo.sh と bootstrap.sh)
- 第 4 回 Stop フックを 5 本並べたら最初の 1 本しか読まれなかったので、ルーター 1 本に集約した
- 第 5 回 コンテキスト圧縮で作業が消えるのを、圧縮前後の二段 WIP コミットで防いだ
- 第 6 回 sandbox.enabled を true にしてもクラウドでは bwrap が無く、許可リスト外へ素通りだった
- 第 7 回 「確認してよいですか」を 6 種類に限定したら、それ以外は全部自律実行になった
- 第 8 回 ルールと教訓を増やし続けないために、常駐バイト予算と「昇格=物理削除」を機械強制した
自分のリポジトリで再現したい方は、同じリポジトリを「どう入れて、どう回して、どう追随するか」の手順書として書いた Zenn Book(有料 500 円・試し読みあり)へどうぞ。