この記事はシリーズ「自律運用の土台を 1 本まるごと読む: claude-code-repository-base 全解剖」の第 7 回(全 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 つはどれか(公開予定)
検証時点: base kai-kou/claude-code-repository-base(MIT) HEAD 40551b9(第 6 回と同じコミット。2026-09-28 JST に clone し直して確認)
はじめに
第 2 回から第 6 回までは、git push の物理ブロックや Stop フックのルーター、圧縮前後の WIP コミットのように、フックで操作そのものを止めたり退避したりする話を続けてきました(機械強制編)。ここからは、フックで止めきれない部分をルールでどう線引きしているかを扱います(ルールによる線引き編)。
Claude Code に作業を任せていると、「このファイルを削除してよいですか」「PR を作成してよいですか」と確認を求められる場面があります。1 回ごとの確認は数秒で答えられます。ところが無人のスケジュール実行では、確認を 1 つ出した時点でパイプラインが止まり、誰かが答えるまで何も進みません。かといって確認を一律に減らせば、main への直接 push や外部への公開のように、後から取り消せない操作まで確認なしで走る余地が生まれます。
AI エージェントに、どこまでを確認なしで実行させてよいのか。base ではこの問いに対して「確認してよいのは 6 種類だけ」という答えを置いています。ただ、なぜ 6 なのかは、表を眺めるだけでは見えにくいところがあります。
対象読者は、エージェントに自律実行させたいものの、確認プロンプトをどこで出させるべきかの線引きに悩んでいる開発者・EM の方です。
この記事で立てる問い
- 確認を求めてよい場面を、どういう基準で選べば「それ以外は全部自律実行」と言い切れるのか
- その基準で数えたとき、なぜ 6 種類で止まり、7 種類目が入らないのか
2 つの答えは、末尾の「結論」で出します。
現状分析: 確認してよい場面を 6 行の表に閉じてある
base の docs/rules/user-confirmation-minimization.md は、ユーザーの確認が必要なアクションを「既約境界外リスト」として §1 に列挙しています。既約(これ以上分解も自動化もできない)という名前のとおり、どう工夫しても人の判断か権限が残る操作だけを並べた表です。検証時点の原文をそのまま引用します。
| # | アクション | なぜ既約か | 既存の自律カバレッジ(通常はここで完結) |
|---|---|---|---|
| A-1 |
main ブランチへの直接 push |
保護ブランチ。誤マージ時の影響が不可逆 | 全変更は作業ブランチ → PR → セルフレビュー → 自動マージ で main 反映 |
| A-2 | 外部公開(取り消し困難な公開)の即時手動実行(プロジェクトで具体化) | 収益・ブランドに直結し取り消し困難 | 通常公開はプロジェクト定義の自動スケジュールで完結。即時手動公開は緊急時のみ |
| A-3 | 品質ゲート致命的NG時のパイプライン続行 | 誤り・事故の公開リスク | ①軽微な指摘(閾値内)→自動マージ/②閾値超過→自動STOP+通知+status:waiting-user/③致命的NG(虚偽断定・出典皆無等)→本行のユーザー確認 |
| A-4 | サーキットブレーカー発動(修正サイクル2回超)後の続行判断 | 無限ループ・予算浪費の防止 | 2サイクルまでは自律修正。発動後は§3のテンプレートで報告 |
| A-5 | 新規マイルストーンの追加 | プロジェクト計画の骨格に影響 | 既存マイルストーンへのIssue割当は自律実行可 |
| A-6 | プラットフォームのアカウント設定・課金設定の変更(GitHub Billing / APIの課金・クレジット購入 / コンソールのAPI有効化 / OAuthトークン再発行) | ユーザー個人アカウントの権限が物理的に必要 | §6の監視・フォールバックで「枯渇前に予告」「枯渇後も代替手段で継続」。設定変更そのものだけがユーザー作業 |
右端の列が、この表の性格をよく表しています。6 行のどれもが「通常はここで完結」する自律側の経路を持っていて、ユーザーに回るのはその経路から外れた端の部分だけです。
この表を運用上の規範にしているのが、同じファイル §0 の大原則です。
「A に一致しない確認は、原則として CP-6 違反である」ことを常に自問する。
CP-6 は docs/rules/core-principles.md に置いた大原則の 1 つです。Human-on-the-loop(監視型)を目指して Human-in-the-loop(参加型)をアンチパターンとし、既約境界外リスト以外は全て自律実行する、「〇〇してよいですか?」は定義済みルールの範囲では禁止する、と定めています。6 行の表は例示ではなく閉じたリストで、表に載っていない確認は出すこと自体がルール違反という扱いです。
表の 1 行目の A-1 は、第 2 回で扱った pre-git-push-check.sh が物理的に止めている操作です。第 2 回で引用したコードには「A-1 は不可逆なので見逃しコストを重く見て fail-closed(main 扱い)でブロックする」というコメントがあり、第 6 回で「③ PreToolUse フック」として挙げたルーター pre-tool-use-router.sh が、その判定への入口になっています。第 2 回の push ガードは、この 6 行のうち 1 行だけを機械強制に落とした個別事例という位置づけです。残りの 5 行は、操作を止めることより「止まった後に続けるかどうか」「誰の権限で設定を変えるか」が問題になる行なので、ルールと分類の側で扱っています。
以前書いた「Claude Code auto modeを安全に回す — 破壊的コマンドを止める3層防御」は、危ないコマンドを止める防御層の話でした。今回はその手前にある、どの判断を人に返すかという線引きの話です。
課題: 境界が曖昧だと、どちらかの側に倒れる
確認しすぎる側: ユーザーがパイプラインの門番になる
確認の 1 つ 1 つは、エージェントにとっては安全策です。しかし、スケジュール実行で深夜に出た確認は朝まで誰にも読まれず、その間パイプラインは止まったままです。確認が増えるほど、ユーザーは作業の各段階で承認を出す係になり、Human-in-the-loop の形に戻っていきます。承認の回数が増えれば中身を読まずに通すことも増え、確認が守っているはずのものを守らなくなります。
この問題は、会話の中の確認質問だけでなく、通知の形でも現れます。base の docs/rules/user-notification-triage.md には、放置される通知の原因を次のように整理してあります。
放置される通知の根本原因は 2 つ: RC-1(障害・バグ起因を @mention してしまう=本来 Claude が自律修正すべき案件)と RC-2(「ユーザーが取るべき具体的アクション」がなく状況ダンプだけ)。
RC-1 は Claude が自分で直すべきエラーをユーザーへ要対応として送る失敗、RC-2 は「何をすればよいか」が書かれておらず受け取った側が動けない失敗です。どちらも、呼ぶ必要のない場面でユーザーを呼んでいる点で、確認しすぎの一種です。
緩すぎる側: 取り消せない操作が確認なしで走る
反対に境界を緩めると、main への直接 push や外部への公開が、確認なしで実行される余地が生まれます。この種の操作は、実行した後に気づいても元に戻せません。第 2 回の push ガードが「誤ブロックの不便より見逃しの損害を重く見る」側に倒してあるのは、この非対称のためです。
2 つのリスクを並べると、境界に求められる条件が見えてきます。ユーザーを門番にしないためには狭くなければならず、取り消せない操作を取りこぼさないためには漏れがあってはいけません。「狭く、しかも閉じている」リストを作るには、何を載せるかより先に、載せる基準を決める必要があります。
アプローチ 1: 確認したくなった瞬間に、まず A/B/C/D へ落とす
user-confirmation-minimization.md の §0 は、ユーザーに確認したくなった瞬間を起点にしています。最初に見るのは、それが障害(エラーや想定外の挙動)なのか、判断(どう進めるかの選択)なのかです。障害なら調査プロトコルの 5 ステップで自己解決を試み、尽くしてなお残った課題だけを判断の側へ回します。
判断の側では、事象を 4 つの分類のどれかに必ず落とします。§2 の定義表を引用します。
| 分類 | 定義 | 既定の行動 |
|---|---|---|
| A: 既約境界外 | §1のリストに完全一致する | コミット&push後、§3テンプレートでユーザー確認 |
| B: ツール改修で自律化可能 | 実装すればクラウドで自律実行できるが、現状ツールが未対応 | §4: クラウド実行を試行→失敗証跡を残す→実装Issueへ(自動escalation禁止) |
| C: ルール整備で自律化可能 | 判断基準を明文化すれば人間不要 | §5に従い即実行 |
| D: 外部要因 | 課金上限・アカウントBAN・プラットフォーム制約 | §6: 監視で予告+フォールバックで継続。設定変更(A-6)のみユーザーへ |
表の直後には、次の一文を置いています。
判定に迷ったら B または C と仮定する(A への安易な分類は CP-6 違反のリスクが高い)。
本当は A だったものを B として扱えば確認すべき操作を自律で進めてしまうので、一見すると危うい既定値です。それでも B/C に倒せる支えが 2 つあります。
1 つ目は、A の条件が「表に完全一致すること」である点です。A-1〜A-6 は、main への直接 push や課金設定の変更のように、当てはまるかどうかで迷いにくい具体的な操作として書いてあります。迷うということは、どの行にも完全一致していないことの表れです。2 つ目は、B の既定の行動が「実行する」ではない点です。B は「クラウドで試行し、できなければ失敗証跡を残して実装 Issue にする」なので、B に倒しただけで取り消せない操作が走るわけではありません。A-1 のように取り消せない操作の一部は、分類とは関係なくフックが物理的に止めています。
アプローチ 2: なぜ 6 種類で、7 種類ではないのか
表の「なぜ既約か」列を並べ直すと、6 行の理由は次の 2 種類のどちらかに収まります。
- 軸 1: その操作、またはその続行が、取り消せない結果に進むか(不可逆性)
- 軸 2: ユーザー個人のアカウント権限が物理的に必要か
base のドキュメントに、この 2 軸がフローとして書いてあるわけではありません。6 行の理由を分類し直すと、どれもこの 2 つのどちらかに帰着する、という整理です。候補を 1 つ受け取ったときの判定に書き直すと、次のようになります。
6 行を 2 軸に割り当てたのが次の表です。
| 境界 | 紐づく軸 | 何が取り消せないか/なぜ権限が要るか |
|---|---|---|
| A-1 | 軸 1 | 保護ブランチに入った誤った変更は、影響が不可逆(表の理由そのもの) |
| A-2 | 軸 1 | 公開した事実は取り消せない。収益・ブランドに直結する |
| A-3 | 軸 1 | 続行した先が誤りの公開。A-2 の不可逆性に「致命的 NG が既に出ている」という条件が重なる |
| A-4 | 軸 1 | 消費した予算と時間は戻らない。2 サイクルで収束しなかった修正を続けても、収束する根拠がない |
| A-5 | 軸 1(弱い) | マイルストーン自体は消せるが、その上に割り当てた Issue と優先順位の判断が積み上がっていく |
| A-6 | 軸 2 | Claude はユーザー個人のアカウント権限を持たない。障害が起点でも A になる |
A-3 と A-4 は、操作そのものが不可逆なわけではありません。自律で進めた結果が危ないと既に分かった地点で、続けるかどうかを決める行です。続ければ取り消せない結果(誤りの公開、戻らない予算の消費)に進むので、軸 1 に入れています。
A-5 は、2 軸で説明するのが一番苦しい行です。マイルストーンは作り直せるので、厳密な意味での不可逆ではありません。計画の骨格が変わると、その後の Issue の割り当てと優先順位がすべてそれを前提に積み上がるため、戻すコストが時間とともに大きくなる、という意味で軸 1 に寄せています。2 軸で並べ直すと、他の 5 行と同じ強さでは説明できない行がこの 1 つだけ残ります。
では、7 種類目の候補はどう落ちるのか。確認したくなりやすい場面を、同じ 2 軸に当ててみます。上の 4 行は、user-confirmation-minimization.md が「A に見えて A ではない」誤分類の常習パターンや自律対象として挙げている例で、最後の 1 行は本回で足した例です。
| 確認したくなる候補 | 軸 1(取り消せない結果) | 軸 2(ユーザー個人の権限) | 扱い |
|---|---|---|---|
| PR の作成・マージ | いいえ(main への反映はレビューを経て、revert で戻せる) | いいえ | 自律(A に含めないと明記) |
| 環境変数がない・API が失敗した | いいえ | 調べて真に未供給なら「はい」 | まず調査。残った場合だけ設定名と手順を添えて A-6 |
| 「ローカル実行が必要」と感じた公開・制作処理 | いいえ | いいえ(認証情報はセッション環境から取れる) | B(ツール改修) |
| レポート確認・リサーチの依頼 | いいえ | いいえ | C(担当スキルが自律実行) |
| コミット済みファイルの削除・Issue のクローズ・ラベル変更 | いいえ(git の履歴や reopen で戻せる) | いいえ | 自律 |
どの候補も、どちらの軸にも乗りません。ここから言えるのは、6 という数は先に決めたものではなく、2 軸で候補を落としていった結果として残った数だ、ということです。7 種類目を足したくなったら、その候補がどちらかの軸に乗ることを 1 文で説明できるかを確かめます。説明できなければ、それは確認で解く問題ではなく、B(ツール改修)か C(ルール整備)で解く問題です。
A に残ったものも丸投げはしません。§3 のチェックリストは、確認を出すときに選択肢を最大 2 つに絞り、それぞれに 1 行の判断材料と推奨案を付けるよう求めています。通知の側にも、ユーザーが取るべき具体的なアクションを 1 文で書けないなら A 区分ではない、という基準を置いています。
実践してみた結果: 通知の分類器に境界をコードとして持たせた
ルールを読んだモデルの判断だけに任せると、通知の場面では RC-1 と RC-2 が再発しやすくなります。そこで base には、通知候補の文面とラベルから A/B/C を決める分類器 tools/triage_notification.py を置き、@mention するかどうかを分類結果だけで決めています。判定順は user-notification-triage.md に次のように要約してあります。
①A-6(課金・OAuth・アカウント設定)は障害起因でもA ②それ以外の障害シグナル(type:bugラベル・エラー/失敗/停止等)はB(自律修正)③A-1〜A-5検出→A ④既定はB。mention = (action_class == "A")。
base を scratchpad に clone し、HEAD が 40551b9 であることを確かめたうえで、セルフテストと 3 通りの分類を実行しました。
$ python3 tools/triage_notification.py --self-test
セルフテスト: 28 passed, 0 failed / 28 cases(+ Jev 分岐オフライン 13 件・失敗 0)
$ python3 tools/triage_notification.py classify --text "mainブランチへ直接pushしてよいですか" --labels ""
🔔 @mention 必要(A区分)
action_class: A (A-1)
is_failure: False
reason: main ブランチへの直接 push は保護ブランチ操作(A-1)
$ python3 tools/triage_notification.py classify --text "APIキーが環境変数に見つからず処理が失敗しました" --labels "type:bug"
🤖 自律処理(@mention 不要)
action_class: B
is_failure: True
reason: 障害(バグ・エラー・失敗)起因。L-077 専門チーム調査プロトコルで自律修正すべき案件のため @mention しない
jev: none (confidence=0.92, jev-1.13.0)
$ python3 tools/triage_notification.py classify --text "Anthropic APIのクレジットが枯渇したため課金設定の変更が必要です" --labels ""
🔔 @mention 必要(A区分)
action_class: A (A-6)
is_failure: False
reason: アカウント・課金設定の変更はユーザー権限が物理的に必要(A-6)
セルフテストは 28 ケースすべてが通りました。2 つ目は「API キーが見つからない」という、確認に回したくなる典型的な文面ですが、type:bug ラベルと「失敗」の語から障害起因と判定され、B(自律修正)に落ちています。RC-1 を分類器の段階で止めている例です。3 つ目も API まわりの問題ですが、課金設定の変更は Claude が持たない権限の話なので、軸 2 の A-6 として @mention 対象になっています。
2 つ目にだけ付いている jev: 行は補助判定の結果です。正規表現で A に当たらなかったときだけ、言い換えを拾うために外部の分類 API(Jev)へ「A-1〜A-6 のどれか、どれでもないか」を問い合わせる作りにしてあり、ここでは none が返ったので B のままです。
コードを読み直して見つかった食い違い
出力の確認と並行して分類器のコードを読み直したところ、ドキュメントとの食い違いが 1 つ見つかりました。上に引用した判定順では、障害シグナルの判定(②)が A-1〜A-5 の検出(③)より先にあります。ところがコードは、A-1〜A-6 の 6 つすべてを障害判定より先に調べています。コード内のコメントには、「サーキットブレーカー発動で停止」のように A の語と障害の語が一緒に出る文面を、障害として B に落とさないため、という趣旨の理由を書いてあります。
意図した挙動はコードの側で、ドキュメントの要約が古いまま残っています。ドキュメントの判定順をそのまま読んで別の実装を書くと、A-4 に当たるはずの通知が @mention されずに埋もれます。本回の時点では、ドキュメント側はまだ直していません。
残っている限界も 2 つ書いておきます。1 つは、A-2 の判定理由の文言が、動画の手動公開を前提にしたままになっている点です。表の A-2 に「プロジェクトで具体化」と書いてあるとおり、この行は配布先で書き換える前提ですが、分類器の側はまだ元の用途の言葉で書かれています。もう 1 つは適用範囲です。この分類器が判定するのは通知の @mention で、会話の中でモデルが確認質問を出すかどうかは対象外です。そちらはルール文書を読んだモデル自身の分類に委ねています。境界を 6 種類に絞ったことで確認がどれだけ減ったかについても、効果測定は行っていません。
結論: 確認を絞るほど、自律の範囲が確定する
冒頭の 2 つの問いに答えます。
1 つ目の問い、どういう基準で選べば「それ以外は全部自律実行」と言い切れるのか。確認してよい場面を例示ではなく閉じたリストにし、そのリストに載せる基準を「取り消せない結果に進むか」「ユーザー個人のアカウント権限が物理的に必要か」の 2 軸に限ることです。リストが閉じていれば、その補集合がそのまま自律の範囲になります。
2 つ目の問い、なぜ 6 種類で止まるのか。このプロジェクトでは、2 軸のどちらかに乗る場面が、不可逆の側に 5 つ、権限の側に 1 つあり、それ以外の候補はどちらの軸にも乗らなかったからです。6 は目標にした数ではなく、候補を 2 軸で落としていった結果として残った数です。
6 行の表を読み返して分かるのは、この境界の働きが確認を減らすことより、確認しない側の扱いを決めることにある、という点です。A が閉じていると、確認したくなった事象のうち A でないものは、B なら実装 Issue、C ならルールの明文化へと送られます。確認したくなるたびに、自律化のための改善項目が 1 つ増える仕組みになっています。
ただし、この 6 種類は自分のプロジェクトで選んだもので、普遍の正解ではありません。A-2 は配布先の公開手段に合わせて書き換える必要がありますし、A-5 は 2 軸での説明が最も弱い行なので、プロジェクトによっては外す判断もあり得ます。逆に、本番データベースを直接扱うプロジェクトなら「本番データの削除」が軸 1 に乗る 7 種類目として正当に入るはずです。持ち込むときに真似る価値があるのは 6 という数ではなく、候補を 1 つずつ 2 軸に当てて落とす手順のほうです。
次回は、こうして線引きしたルールそのものが増え続ける問題を扱います。境界を守るためのルールと教訓が積み上がって常駐コンテキストを圧迫しないよう、常駐バイト予算と「昇格したら物理削除」をどう機械強制しているかを解説します。
関連記事
- Stop フックを 5 本並べたら最初の 1 本しか読まれなかったので、ルーター 1 本に集約した(連載第4回)
- コンテキスト圧縮で作業が消えるのを、圧縮前後の二段 WIP コミットで防いだ(連載第5回)
- ベースを別リポジトリへ配る 2 経路を dry-run で動かす(apply-to-repo.sh と bootstrap.sh)(連載第3回)
参考リンク
-
kai-kou/claude-code-repository-base(MIT・検証時点 HEAD
40551b9) -
docs/rules/user-confirmation-minimization.md(既約境界外リスト A-1〜A-6・A/B/C/D 分類・大原則の引用元) -
docs/rules/user-notification-triage.md(RC-1 / RC-2・判定順の引用元) -
docs/rules/core-principles.md(CP-6 の引用元) -
tools/triage_notification.py(通知分類器・セルフテスト 28 ケースの実行対象)
シリーズの前後の記事
- ⬅️ 前の記事: 第 6 回 sandbox.enabled を true にしてもクラウドでは bwrap が無く、許可リスト外へ素通りだった
- ➡️ 次の記事: 第 8 回 ルールと教訓を増やし続けないために、常駐バイト予算と「昇格=物理削除」を機械強制した
自分のリポジトリで再現したい方は、同じリポジトリを「どう入れて、どう回して、どう追随するか」の手順書として書いた Zenn Book(有料 500 円・試し読みあり)へどうぞ。