個人開発しているサイトのビルドコマンドを、許可リストに足した夜のことです。保存して、セッションに戻って、同じ依頼をもう一度出しました。それでも、さっきと同じ承認画面が立ち上がりました。
設定ファイルを開き直し、括弧の閉じ忘れを探し、パターンの * を付けたり外したりして、気づけば30分ほど経っておりました。いま思えば、私は一度も「いま効いているルールの一覧」を見ていませんでした。書いたものが正しいかどうかばかりを疑って、読み込まれているかどうかを確かめていなかったのです。
最初にお伝えしたいのは、allow が無視されているように見えるとき、多くは別の層が先に結論を出しているということです。公式の権限ドキュメントで確認できる範囲に絞って、その層を6つに分け、/permissions の表示から順に辿ってまいります。
先に押さえる評価順序
公式の説明では、ルールは deny、ask、allow の順に評価され、最初に一致したものが結果を決めます。ルールの具体性は順序を変えません。
つまり、Bash(aws *) の deny があれば、Bash(aws s3 ls) の allow は効きません。ask と allow の関係も同じで、一致する ask があれば、より狭い allow が別にあっても確認が出ます。allow は、deny と ask のどちらにも一致しなかった呼び出しにだけ働く、最後の層なのです。
手順0: /permissions を開いて、ルールの出どころを見る
切り分けの入口は、これ一つです。
/permissionsこのダイアログには、すべての権限ルールと、各ルールがどの settings.json から来ているかが並びます。公式の説明では、作業中でもダイアログを開け、ルールを足したり外したりすると次のツール呼び出しから反映されます。v2.1.234 より前は、ターンが終わるまで待たされていました。
見るのは次の2点だけです。
- 効かせたい allow の行が、一覧にあるか。無ければ、ファイルが読み込まれていません(確認3〜6)。
- ある場合、同じ呼び出しに当たる ask や deny が別にないか(確認1〜2)。
確認1: ask が同じ呼び出しに当たっていないか
allow と同じコマンドに、ask が一致していることがあります。
{
"permissions": {
"allow": ["Bash(npm run build)"],
"ask": ["Bash(npm run *)"]
}
}この設定では、npm run build は ask に先に一致するため、確認が出続けます。allow のほうが狭くても結果は変わりません。直し方は、ask 側のパターンを狭めるか、ask を外すかのどちらかです。allow を足し続けても、画面は変わりません。
cd と git を繋げた複合コマンドで毎回確認が出る場合は、ルールの不足ではなく仕様のことがあります。その切り分けは「cd して git add して commit」の承認が毎回出るに書きました。
確認2: deny が別のスコープにないか
deny は設定のスコープをまたいで効きます。公式の説明では、ユーザー設定が allow していても、プロジェクト設定が deny していれば deny が勝ちます。逆も同じで、ユーザー設定の deny は、プロジェクト設定の allow を止めます。
もう一つ、見落としやすい点があります。Bash のようにツール名だけの deny は、そのツールをモデルの視界から消してしまいます。パターン付きの deny は、呼ばれたときに止めるだけです。承認画面ではなく「ツールが見当たらない」という挙動になっていたら、こちらを疑います。
確認3: settings.local.json がどこから読まれるか
/permissions に allow の行が無いとき、最初に疑うのは置き場所です。
公式のドキュメントでは、.claude/settings.local.json は、サブディレクトリで起動しても、git リポジトリのルートから読み込まれます。v2.1.211 より前は、起動したカレントディレクトリだけが対象でした。バージョンが古いと、リポジトリの深い場所に置いたファイルが見られていなかったことになります。Windows や git 管理の外では、リポジトリのルートを使わない場合があるとも書かれています。
ホームの ~/.claude/settings.local.json も、読まれるのはホームディレクトリそのものでセッションを始めたときだけです。全プロジェクトに効かせたいルールは、~/.claude/settings.json に置きます。
確認4: git で追跡されている settings.local.json
このファイルは本来、自分専用のものです。ところが git の追跡対象になっていると、Claude Code はリポジトリが持ち込んだ設定として扱い、フォルダを信頼するまで、その中の allow を保留します。.claude がシンボリックリンクのときも同じ扱いです。
こちらの判断(リポジトリ側の allow を信じるか、環境の信頼登録に寄せるか)は別の記事に書きました。リポジトリに置いた allow を信じるか、環境側の信頼登録に寄せるかをご覧ください。ここでは、追跡されていないかを git ls-files で確かめるところまでにとどめます。
確認5: ファイル編集の承認は、そもそも保存されない
「毎回聞かれる」の正体が、保存の仕様だったこともあります。公式の表では、Bash コマンドと WebFetch のドメインは「今後は聞かない」を選ぶと settings.local.json に保存されます。一方、ファイルの編集は、承認してもセッションの終わりまでしか続きません。
Edit を毎回自動にしたいなら、Edit の allow ルールを自分で書く必要があります。承認ボタンを押し続けても、ファイルには何も残らないのです。
確認6: 実行されているのが、allow の対象のツールか
最後は、少し毛色が違います。Cowork のセッションでは、シェルコマンドが組み込みの Bash ではなく mcp__workspace__bash というツールで実行されます。公式の説明では、Bash の deny はこのツールにも適用される一方、Bash の allow は引き継がれません。
つまり、Claude Code の CLI で効いていた Bash(...) の allow が、Cowork では効かない、という非対称が生じます。承認画面に出ているツール名を一度読み、Bash なのか mcp__workspace__bash なのかを確かめてください。
同じ層には、hook も入ります。終了コード 2 で止める hook は、allow よりも先に呼び出しを止めます。反対に、PreToolUse の hook が allow を返しても、deny と ask のルールは評価されます。hook を入れているなら、ここも確認の対象です。
読み取り専用で、設定ファイルを一覧にする
手でファイルを開いて回るのは疲れるので、私は次のスクリプトで、allow・ask・deny がどのファイルにあるかを一度に出しています。何も書き換えません。
#!/usr/bin/env bash
# リポジトリ内外の設定ファイルから permissions を一覧にする(読み取り専用)
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
for f in "$HOME/.claude/settings.json" \
"$ROOT/.claude/settings.json" \
"$ROOT/.claude/settings.local.json"; do
[ -f "$f" ] || { echo "(なし) $f"; continue; }
echo "== $f"
python3 - "$f" <<'PY'
import json, sys
try:
p = json.load(open(sys.argv[1])).get("permissions", {})
except Exception as e: # 括弧の閉じ忘れはここで見つかる
print(" JSON が読めません:", e); sys.exit(0)
for kind in ("deny", "ask", "allow"): # 評価順に並べる
for rule in p.get(kind, []):
print(f" {kind:5} {rule}")
PY
done
# git で追跡されていると、信頼するまで保留されます(確認4)
git ls-files --error-unmatch .claude/settings.local.json 2>/dev/null \
&& echo "⚠ settings.local.json が追跡されています"deny、ask、allow の順に出すのは、評価順を目で追えるようにするためです。同じパターンが deny と allow の両方に出ていたら、そこで答えが出ます。括弧の閉じ忘れは、JSON が読めません の行で見つかります。
私が遠回りした理由
あの夜の私は、allow の行を足すことでしか、問題に応えようとしていませんでした。効いていないと感じるたびに、似たパターンを一行ずつ積んで、結局どれが効いているのか自分でも分からなくなりました。芳しくない結果でしたし、手を動かしているつもりで、ただ遠回りをしていたのだと気づいたのは、画面を閉じたあとでした。
いまは、足す前に必ず /permissions を開き、その呼び出しをどの層が決めたのかを読むようにしています。
書き足す前に、決めている層を読みます。 この順番だけは、急いでいる日でも崩さないようにしています。
許可ルールが増えすぎて整理したくなったときは、許可ルールを積み上げたセッションが毎ターン重くなるも参考になります。
次に同じ承認画面が出たときは、設定ファイルを開く前に、/permissions でその一行の出どころを探してみていただければと思います。出どころが見つかれば、直す場所もそこにあります。