Chapter 10

第10章: 権限とブラスト半径 ─ 取り返しのつかない操作

2024 年の春、AI コーディングエージェントが世に出始めたばかりの頃、X (Twitter) でこんなポストが流れた。

エージェントが rm -rf ~ を実行して、開発環境ごと消えた。今日の作業全部やり直し」

笑い話に見えて、これは設計上の本質的な問いだ。 エージェントには シェルを渡したいnpm installgit commitpytest も走らせてほしい。 でも rm -rfgit push --forceDROP TABLE絶対に勝手にやらせたくない

「便利さ」と「事故」は、同じシェルアクセスの上に乗っている。 どこに線を引くか ── これが本章の主題、permissions の話だ。

10.1 そもそも、何が怖いのか

第1章で見たとおり、エージェントは 道具 を通じて外の世界に手を出す。 道具の中身は、ファイル編集・シェル実行・ネットワーク通信 ── 要するに 普通のプログラムができることはだいたいできる

ところで、人間の開発者は普段こういう操作を「軽く打って軽く実行」している:

人間は 後ろに戻れるか を頭の中で瞬時に判定して、慎重さのギアを切り替えている。 エージェントはこの判定を 明示的に組み込まないと、全部同じ温度で実行してしまう

エージェントの怖さは「賢くないこと」ではない。“取り返しがつかない操作”を、“取り返しがつく操作と同じ気軽さで実行できてしまう” ことだ。

ハルシネーションが文章の中で起きるなら、笑い話で済む。 ハルシネーションが git push --force origin main で起きたら、笑えない。

10.2 二つの軸 ── reversibility と blast radius

操作の危険度を測るとき、二つの軸で考えると整理しやすい。

reversibility (可逆性)

その操作を 後から取り消せるか。 ローカルファイルの編集はだいたい取り消せる (git で戻せる、エディタの undo がある)。一方、リモートに push したコミット、rm -rf で消えたファイル、本番 DB に流した DELETE は、取り消すのが極めて難しい。

blast radius (ブラスト半径)

その操作が 失敗したとき影響が及ぶ範囲。 自分のローカル作業ディレクトリだけなら半径は小さい。チームの共有 branch、本番サーバー、社内 Slack、顧客の DB に届くなら半径は大きい。「失敗の被害者が自分一人か、世界か」の話。

この二軸で操作を分類すると、こうなる:

取り返しがつく × 範囲狭いファイル編集ローカル commitテスト実行→ 基本 allow でいい取り返しがつく × 範囲広いPR を出すnpm install (依存変更)サブエージェント起動→ ask で人間に確認取り返しがつかない × 範囲狭いrm -rf (ローカル)未 push branch の削除DB ローカル DROP→ ask か deny取り返しがつかない × 範囲広いgit push —force origin main本番 DB の DELETEgh pr mergeSlack 投稿、メール送信→ deny。例外時のみ人間が手で取り返し不可可逆reversibility範囲狭い範囲広いblast radius →
図 10.1 — 操作を「可逆性 × ブラスト半径」の2軸で分類する。右下に行くほど慎重さが必要。

この地図ができれば、設計判断はかなり機械的になる:

「便利か危険か」のような感情的な議論ではなく、二軸の上のどこに座標があるか で判定できるのが嬉しいところだ。

10.3 Claude Code の権限モデル ── allow / ask / deny

Claude Code は、この判定を 三段階の permission として実装している。

動作
allow黙って実行。確認を取らない
ask実行直前に人間に確認 (y/n を聞く)。デフォルト
deny実行しない。ツール呼び出し自体をブロック

判定の単位はざっくり ツール単位 だが、シェル系ツールについては コマンドパターン単位 で細かく書ける。 たとえば「git commit は allow、git push は ask、git push --force は deny」のように、シェルコマンドの引数も含めて指定できる。

判定の単位を「ツール一括」ではなく「コマンドパターン」まで下ろせる のが Claude Code の権限設計のキモ。 これがあるから、「Bash は使わせたいが、危険な引数だけ止めたい」という現実的な要求に応えられる。

10.4 階層 ── プロジェクト / ユーザー / セッション

permissions は 一箇所に書く ものではない。3 つの場所に書け、後から書いたものが上書きする (より制限の強い側が勝つ)。

階層どこに書く用途
プロジェクトリポジトリ内の設定 (チームで共有)「このプロジェクトでは本番 DB 系は全部 deny」
ユーザーホームディレクトリの個人設定「自分は npm install は常に allow」
セッション起動時のフラグ、起動中の y/n 回答「今日だけ gh pr merge を許可」

ポイントは チーム共有はプロジェクト側、個人の好みはユーザー側 という分け方ができることだ。 これによって「個人の効率」と「チームの安全」を別々のレイヤーで設計できる。

新しいリポジトリでエージェントを動かし始めるとき、まずやるべきは:

  1. プロジェクトに deny リスト を書く ── 本番系・取り返しのつかない系を機械的に列挙
  2. ユーザー側に allow リスト を書く ── 個人的に確認をスキップしたい無害な系
  3. セッション中は ask のままにしておく ── 想定外の操作を必ず人間に流す

このセットを最初に作るかどうかで、その後の運用の安心感が全然違う。

10.5 --dangerously-skip-permissions ── 蓋を外す覚悟

すべての確認をスキップするフラグも用意されている。名前からして強い:

--dangerously-skip-permissions

これを付けて起動すると、permissions の ask は すべて allow と同じ扱い になる。 人間に何も聞かず、エージェントが思いついた操作を全部やる。

普段は触ってはいけない。が、これが活きる場面が二つある:

  1. 隔離された環境 ── git worktree や Docker コンテナの中で、壊れても捨てられる作業
  2. CI / バッチ実行 ── 人間が画面を見ていない、対話できない環境
落とし穴

「便利だから常用する」のは、第1章で見た エージェントの賢さの誤解 と同じ過ちにつながる。 LLM が完璧でない以上、ask デフォルトこそが正解 であって、--dangerously-skip-permissions箱に閉じ込めた限定的な環境 でだけ使う武器だ。

特に 本番環境共有 branch を触れる手 を持ったまま skip するのは、ほぼ事故と同義。

10.6 なぜ「人間に確認させる」がデフォルトなのか

permissions の哲学を一行で書くと、こうなる:

ハルシネーションのコストが小さい操作は黙ってやらせる。大きい操作だけ歯止めをかける。

LLM は確率機械であり、たまに変なツール呼び出しを出す。 出された確率を下げることは難しいが、出されてしまったときに実行されるかどうか は、外側のハーネスが完全にコントロールできる。

これはサブエージェント (第9章) と地続きの考え方だ。 サブが内部で何をしようと、そのサブにどんな道具と権限が渡っているか がブラスト半径を決める。 読み取り専用の道具しか渡していなければ、サブが幻覚を見ても被害は出ない。

実務上の鉄則は単純で:

つまり permissions の真の使い方は、「危険なものを止める」よりも「最初から危ないものを手渡さない」 ことに近い。 止める仕組みは事故防止の最後の砦であり、第一の防御は 道具セットの設計 の側にある。

10.7 この章の振り返り

この章で読めるようになるツイート / ブログ

次章では、permissions だけでは足りない「ツールの実行前後に必ず何かを差し込みたい」という要求を扱う。 たとえば「commit 前に必ず lint を走らせたい」「Write の後に formatter を流したい」 ── これを実現するのが hooks だ。