Claude Codeの定期実行が確認ダイアログで止まる原因と対処|permissionsのallow・deny・askと許可リストの設計

Claude Codeの定期実行が確認ダイアログで止まる原因と対処

こんにちは、おうどんです🍜

朝8時。 Claude Code の定期タスクが、元気に動き出す。

資料を読む。 下書きを書く。 コマンドを1つ実行しようとして――

確認係さん「このコマンドを実行してもよろしいですか?」

……。

店長(おうどん)は、まだ布団の中です。

確認係さん「よろしいですか?」

確認係さん「……よろしいですか?」

……返事をする人が、この家に1人もいない。

(場面は説明用の架空のものです)

実はおうどん、Claude Code の定期タスクで、毎朝このブログの記事の下書きを作らせています。 この記事も、その定期タスクが書いた下書きです。 そのタスクの指示書には「使ってよいコマンドの形」がずらっと並んでいます。 理由はシンプルで、確認ダイアログが1回出たら、そこで朝が終わるからです。

今回は、Claude Code の定期実行(無人実行)が確認ダイアログで止まる理由と、止めないための許可リスト(permissions)の書き方をまとめます。

イメージはうどん屋さんです。

  • Claude=見習いの店員さん
  • 確認ダイアログ=確認係さん(店長に「これやっていいですか?」と聞きに行く係)
  • settings.json の permissions=お店の張り紙(聞かずにやっていいこと・絶対ダメなこと)

張り紙に書いてあることは、聞かずにやる。 書いていないことは、店長に聞きに行く。 店長が寝ていたら……確認係さんは、伝票を持ったまま立ち尽くします。

目次

結論:まずはこれだけ

止まる理由を一言でまとめると、許可されていない操作に出会うと、確認係さんが店長の返事を待つからです。

  • 手動確認のモード(Manual、設定値は default)で動くタスクは、許可のない操作で止まり、返事が来るまで待つ
  • タスクを作ったら、まず「今すぐ実行」(Run now)で1回見守り、出てきた確認に「常に許可」で答える
  • 恒久的には、~/.claude/settings.json の permissions.allow に、タスクが使うコマンドを書いておく
  • タスクの指示書(プロンプト)にも、同じコマンドの形だけを使うように書く

全部「常に許可」にしておけば、二度と止まらないのでは?

止まらなくなります。 そして、見習いさんが店の金庫を開けるときも、誰にも聞かなくなります。

じゃあ確認係さんをクビにすれば……

クビにする方法(bypassPermissions モード)もあるにはあります。 ただし公式ドキュメントは、コンテナや VM(仮想マシン)のような隔離された環境でだけ使うように、とはっきり書いています。 自宅の PC で確認係さんをクビにするのは、店の鍵を開けっぱなしで寝るのと同じです。

なぜ止まる? 確認ダイアログは「エラー」ではなく「質問」

確認係さんは壊れて止まっているわけではありません。ちゃんと質問して、返事を待っているんです。

Claude Code の権限のドキュメントには、手動確認のモードで、どの種類の操作に確認が出るかが表になっています。

操作の種類例確認が出るか
読み取り専用ファイルを読む、Grep出ない(作業フォルダと追加フォルダの中なら)
Bash コマンドシェルの実行出る(組み込みの読み取り専用コマンドを除く)
ファイルの変更ファイルの編集・書き込み出る
Web の取得WebFetch出る(あらかじめ許可されたドキュメント系ドメインを除く)
Web 検索WebSearch出る

(公式ドキュメントの表を、おうどんが日本語にまとめたものです)

つまり、読むだけなら黙ってやる。 書く・動かす・外に出る、のどれかをやろうとすると、確認係さんが出てきます。

そして Desktop の定期タスクのドキュメントには、こう書かれています。

  • タスクごとに権限モードを設定できる
  • 手動確認のモードで動くタスクが、許可のない操作をしようとすると、承認するまで実行が止まる(stall)
  • そのセッションはサイドバーに開いたまま残るので、あとから答えられる

確認係さん「店長が起きるまで待ちます」

見習いさん「その間、ぼくは何を?」

確認係さん「待ちます」

……2人で並んで、ただ待っている。 でも、これは設計どおりの動きなんです。 勝手にやらずに待つのが、確認係さんの仕事ですから。

待ってる間に、うどん茹でておいてくれたらいいのに。

茹でていいかどうかも、たぶん聞きに来ます。

権限モードの違い:6つの「確認係さんの働き方」

モードは、確認係さんの働き方の違いです。 権限モードのドキュメントの表をまとめると、こうなります。

モード聞かずにやること向いている場面
default(Manual)読み取りだけ1つずつ自分で確認したいとき
acceptEdits読み取り、ファイル編集、mkdir・mv・cp などのよく使うファイル操作見ながら直していくとき
plan読み取り(auto モードが使えるなら、判定係が承認したコマンドも)変更する前にコードを調べるとき
autoほぼ全部。ただし裏で安全チェックが入る長い作業、確認疲れを減らしたいとき
dontAsk読み取りと、事前に許可した操作。確認が必要なものは全部拒否固めた CI やスクリプト
bypassPermissions全部隔離したコンテナ・VM だけ

(公式ドキュメントの表を、おうどんが日本語にまとめたものです)

無人実行で大事なのは、default と dontAsk の違いです。

  • default:許可のない操作 → 確認を出して待つ
  • dontAsk:許可のない操作 → 確認を出さずに断る

ドキュメントには、dontAsk では「セッションが入力を待つことはない」と書かれています。

確認係さん(default)「店長、これいいですか?……店長?……店長ー?」

確認係さん(dontAsk)「張り紙にないので、やりません。次」

……同じ係なのに、片方は朝まで立ち尽くし、片方は3秒で帰る。

auto は、確認係さんの代わりに判定係(分類器と呼ばれる別のモデル)が「これは頼まれた作業の範囲か」を見るモードです。 使えるかどうかはプランや組織の設定、使うモデルによって変わるので、使う前に自分の環境で確認してください。

全部 dontAsk にすれば、止まらないし安全で最強では?

止まりはしません。 ただ、張り紙にない作業は全部断るので、張り紙が雑だと「何もしていない朝」ができあがります。 止まるか、断るか。張り紙(許可リスト)をきちんと書く必要があるのは、どちらも同じなんです。

対処1:「今すぐ実行」で一度見守って「常に許可」

いちばん手軽なのは、最初の1回だけ店長が起きていることです。

Desktop の定期タスクのドキュメントでは、止まるのを避ける方法として、次の手順が紹介されています。

  1. タスクを作ったら、詳細画面で「今すぐ実行」(Run now)を押す
  2. 実行を見守り、確認が出たら「常に許可」(always allow)を選ぶ
  3. 次回以降、そのタスクは同じツールを確認なしで使う

許可した内容は、タスクの詳細画面の「常に許可」(Always allowed)の欄で見直したり、取り消したりできます。

店長「今日だけ起きてるから、全部聞いて」

確認係さん「これいいですか」「これも」「これも」「あとこれも」

店長「……朝ごはん食べさせて」

……最初の1回は、確認のわんこそばです。 でも、ここで1回答えておけば、翌朝から静かになります。

ただし注意が2つあります。

  • MCP(外部ツールをつなぐ仕組み)のツールのうち、requiresUserInteraction(毎回ユーザーの操作が必要)と指定されたものは、毎回確認が出て「常に許可」を選べません。ドキュメントでも、これを呼ぶタスクは毎回止まると書かれています
  • 指示書の書き方がゆるいと、日によって違うコマンドを打ちます。昨日許可した形と、今日の形が違えば、また止まります

毎回同じコマンドを打ってくれないの?

見習いさんは、日によって包丁の持ち方を変えるタイプです。 だから指示書で「この形で打つ」と決めておくんです(中級編で詳しく)。

対処2:settings.json に allow・deny を書く

「常に許可」を積み重ねる代わりに、張り紙を先に書いておく方法です。

Desktop の定期タスクのドキュメントには、~/.claude/settings.json の allow ルールも定期タスクのセッションに適用される、と書かれています。

設定ファイルの場所と強さ

設定ファイルのドキュメントでは、強い順にこう並んでいます。

  1. 管理設定(組織が配布するもの)
  2. コマンドライン引数(その回だけ)
  3. .claude/settings.local.json(自分用・このプロジェクト)
  4. .claude/settings.json(チームで共有・このプロジェクト)
  5. ~/.claude/settings.json(自分用・全プロジェクト)

Windows では ~/.claude は %USERPROFILE%\.claude のことです。

そして、permissions.allow のようなリストは上書きではなく合算されます。 ユーザー設定の allow と、プロジェクト設定の allow は、両方とも効くわけです。

張り紙が5枚あるってこと?

店長「そう。しかも全部の張り紙を足して読む」

……壁一面が張り紙のうどん屋さん。 でも「どこかの1枚にでも書いてあれば効く」ので、自分用のルールは自分の設定ファイルに書けばよいんです。

ルールの書き方

ルールは ツール名 か ツール名(条件) の形です。

ルール意味
Bash(npm run build)npm run build というコマンドだけ(完全一致)
Bash(npm run *)npm run で始まるコマンド(npm run 単体も含む)
Bash(ls *)ls と ls -la など。lsof は含まない
Bash(ls*)スペースがないので lsof も含む
Edit(//c/work/repo/reports/**)C:\work\repo\reports\ の下のファイルの編集
Read(~/.ssh/**)ホームフォルダの .ssh の下のファイルの読み取り
WebFetch(domain:docs.python.org)docs.python.org からの取得

(権限のドキュメントの例をもとに、おうどんがまとめたものです)

ポイントは次のとおりです。

  • * は、スペースも含めて何でも入る
  • 末尾の「スペース+*」は、その * がルール内で唯一のワイルドカードなら、引数なしのコマンドにも一致する
  • :* を末尾に付けても同じ意味(Bash(ls:*) は Bash(ls *) と同じ)
  • パスのルールで // は絶対パス、~/ はホームフォルダから。/ 1つの基準は設定元で変わり、プロジェクト設定では主作業フォルダ、ユーザー設定では ~/.claude から
  • Windows のパスは、C:\Users\alice → /c/Users/alice のように変換してから照合される

無人タスク向けの設定例

毎朝レポートを作って、Git に push するタスクを想定した例です。

{
  "permissions": {
    "allow": [
      "Bash(python C:/work/tools/report.py)",
      "Bash(git -C C:/work/repo add *)",
      "Bash(git -C C:/work/repo commit *)",
      "Bash(git -C C:/work/repo push origin main)",
      "Edit(//c/work/repo/reports/**)",
      "WebFetch(domain:docs.python.org)"
    ],
    "deny": [
      "Bash(rm *)",
      "Bash(git push --force *)",
      "Read(//c/Users/*/.ssh/**)"
    ]
  }
}

(JSON として正しく読めることは、あとで出てくる check_settings.py で確認しました。Claude Code に読み込ませての動作は、今回の検証では確かめていません。パスやファイル名は説明用です)

  • 使うコマンドは、指示書で打たせる形と一字一句同じにする
  • 作業するフォルダを -C で固定しておくと、ルールを狭く書ける
  • push は origin main まで書いて完全一致にする

見習いさん「張り紙に書いてあるのは『report.py を動かす』。report2.py は?」

張り紙「書いてないです」

見習いさん「じゃあ、聞きに行きます」

……融通がきかない。 でも、その融通のなさが、無人の朝には一番の安心なんです。

だったら Bash(*) 1行で全部許可すれば、張り紙1枚で済むのでは?

済みます。 「なんでもやってよし」と書かれた張り紙の前で、見習いさんが金庫の前に立っている絵が浮かびます。

🔰 ここまで読めば今日から困らない

  • 定期タスクが止まるのは、許可のない操作で確認係さんが返事を待っているから。エラーではない
  • タスクを作ったら「今すぐ実行」で1回見守り、出た確認に「常に許可」で答える
  • ~/.claude/settings.json の permissions.allow に、タスクが使うコマンドを完全な形で書く
  • 指示書にも「このコマンドの形だけ使う」と書き、許可リストと形をそろえる

4つ。朝ごはんの前に読める長さ。

朝ごはんの前に読んで、朝ごはんのあとに1回「今すぐ実行」。 それで翌朝から、確認係さんが布団の横に立たなくなります。

起きたら確認係さんが枕元にいる生活、ホラーでは。

サイドバーに開いたまま残っているセッションは、まさにそれです。

ここから先は中級者向け。読み飛ばしてもOKです。

中級:allow に書いたのに止まる、よくある理由

「張り紙に書いたのに、まだ聞きに来る」。そんなときは、張り紙の文字と、実際の注文が1文字でもずれていないかを疑います。 以下は、権限のドキュメントに書かれている決まりから、つまずきやすいものを集めたものです。

1. && や | でつないでいる

ドキュメントによると、Claude Code はシェルの区切り(&&、||、;、|、|&、&、改行)を理解していて、区切られたコマンド1つずつがルールに一致しないと許可されません。

Bash(python build.py) を許可していても、python build.py && python deploy.py は python deploy.py の分が許可されていないので、確認が出ます。

見習いさん「かけうどんと、ついでに金庫の鍵、お願いします」

張り紙「かけうどんはOK。金庫は書いてない」

……注文を1行にまとめても、ごまかせない。 つなげず、1コマンドずつ打たせるのが確実です。

2. 引数やオプションが違う

Bash(npm run build) は完全一致なので、npm run build --watch には一致しません。 許可リストの形と、指示書で打たせる形をそろえましょう。

3. 前に環境変数を付けている

NODE_ENV=test npm test のように先頭で環境変数を設定する書き方について、ドキュメントでは、既知の安全な環境変数なら外してから照合するが、それ以外の変数が前に付いていると allow ルールには一致しない、と書かれています。 (どの変数が既知の安全なものかは、ドキュメントに一覧が載っていないので、今回は確かめていません)

環境変数を変えたいなら、それを中でやるスクリプトを1本作って、そのスクリプトを許可するほうが確実です。

4. Write(...) でパスを書いている

ファイルのルールは Edit(パス) と Read(パス) でしか照合されません。 Write(docs/**) と書いても、受け付けはされるけれど一度も使われない(起動時に警告が出る)とドキュメントに書かれています。 書き込みの許可は Edit(docs/**) で書きます。

見習いさん「張り紙に『書いてよし』とあったので、書こうとしたら」

確認係さん「うちの店、『書く』じゃなくて『編集』って書かないと読めないんです」

……方言の張り紙。

5. 守られたパス(.git・.claude)に書こうとしている

権限モードのドキュメントによると、.git や .claude などの守られたパス(protected paths)への書き込みは、bypassPermissions などの例外を除いて自動では許可されません。 permissions.allow に Edit(.claude/**) と書いても事前許可にはならず、default では確認が出て、dontAsk では拒否されます。

定期タスクに .claude の中の設定ファイルを書き換えさせる設計は、そもそも避けましょう。

6. ask ルールに一致している

ask ルールは「必ず聞く」という張り紙です。 一致したら、allow にもっと細かいルールがあっても確認が出ます。 無人のタスクで ask に一致すると、default なら止まり、dontAsk なら拒否されます。

見習いさん「張り紙に『必ず店長に聞く』って書いてあります」

店長(寝ている)「……zzz」

……聞く相手がいないのに、聞けと書いてある。 無人のタスクでは、ask を「allow か deny のどちらか」に振り分けておくのがコツです。

7. プロジェクトの settings.json を信頼していない

権限のドキュメントによると、プロジェクトの .claude/settings.json に書いた permissions.allow は、そのフォルダを信頼(workspace trust)するまで使われません。 Desktop では、定期タスクの作業フォルダを保存するときに、信頼していなければ確認されます(Desktop の定期タスクのドキュメントより)。

見習いさん「このお店の張り紙に従います!」

Claude Code「そのお店、まだ入店審査が終わってません」

……張り紙を読む前に、店の身元確認。 信頼の話は、上級編の CI のところでもう一度出てきます。

中級:判定の順番を簡略モデルで確かめる

Claude Code 本体は今回の検証環境では動かせないので、ドキュメントに書かれた決まりを、Python で小さく真似したモデルを作って、どのコマンドがどう判定されるかを確かめました。

先に断っておくと、これは説明用の簡略モデルで、Claude Code の実装ではありません。 組み込みの読み取り専用コマンド、環境変数、ラッパー(timeout など)、信頼の確認、auto モードなどは入っていません。

import re

# 説明用の簡略モデル。Claude Code の実装ではない
settings = {
    "allow": ["Bash(python build.py)", "Bash(npm test)", "Bash(git commit *)"],
    "ask": ["Bash(rm *)"],
    "deny": ["Bash(git push *)"],
}


def to_regex(pattern):
    # 末尾の「 *」だけの場合は、引数なしのコマンドにも一致させる
    if pattern.endswith(" *") and pattern.count("*") == 1:
        return re.compile(re.escape(pattern[:-2]) + r"( .*)?")
    return re.compile(".*".join(re.escape(p) for p in pattern.split("*")))


def split_compound(cmd):
    return [c.strip() for c in re.split(r"&&|\|\||;|\|", cmd) if c.strip()]


def hit(rules, sub):
    for rule in rules:
        m = re.fullmatch(r"Bash\((.*)\)", rule)
        if m and to_regex(m.group(1)).fullmatch(sub):
            return True
    return False


def decide(mode, cmd):
    subs = split_compound(cmd)
    if any(hit(settings["deny"], s) for s in subs):
        return "拒否(deny)"
    if any(hit(settings["ask"], s) for s in subs):
        return "拒否(dontAsk)" if mode == "dontAsk" else "確認待ち(ask)"
    if all(hit(settings["allow"], s) for s in subs):
        return "許可(allow)"
    return "拒否(dontAsk)" if mode == "dontAsk" else "確認待ち"


commands = [
    "python build.py",
    "python build.py --fast",
    "git commit -m fix",
    "python build.py && npm test",
    "python build.py && git push origin main",
    "git push origin main",
    "git -C . push origin main",
    "rm -rf dist",
]

print(f"{'コマンド':<40} {'default':<14} dontAsk")
for c in commands:
    print(f"{c:<42} {decide('default', c):<14} {decide('dontAsk', c)}")
コマンド                                     default        dontAsk
python build.py                            許可(allow)      許可(allow)
python build.py --fast                     確認待ち           拒否(dontAsk)
git commit -m fix                          許可(allow)      許可(allow)
python build.py && npm test                許可(allow)      許可(allow)
python build.py && git push origin main    拒否(deny)       拒否(deny)
git push origin main                       拒否(deny)       拒否(deny)
git -C . push origin main                  確認待ち           拒否(dontAsk)
rm -rf dist                                確認待ち(ask)      拒否(dontAsk)

(python -X utf8 perm_model.py で実行した結果です。Python 3.13.1。日本語の幅がそろわないため、列が少しずれています)

読み方のポイントです。

  • python build.py --fast は、完全一致のルールに合わないので default では確認待ち
  • python build.py && npm test は、2つとも許可されているので許可
  • python build.py && git push origin main は、後ろ半分が deny に一致するので全体が拒否
  • git -C . push origin main は、Bash(git push *) の deny に一致しない(上級編で詳しく)
  • rm -rf dist は ask に一致。default なら確認待ち、dontAsk なら拒否

簡略モデル「python build.py –fast は、確認待ちです」

おうどん「–fast くらい、いいじゃない」

簡略モデル「張り紙には書いてありません」

……作った本人にも融通をきかせない。 でも、本物のドキュメントの完全一致の例(Bash(npm run build) は npm run build --watch に一致しない)と同じ考え方です。

このモデルで OK だったら、本物でも OK ってこと?

違います。 本物は、読み取り専用コマンドの扱いや、守られたパスの確認など、モデルにない判定をたくさんしています。 あくまで「順番と区切りの考え方」を確かめるためのおもちゃです。

中級:settings.json の書き間違いをチェックする

張り紙そのものが破れていないかも確かめましょう。 JSON の末尾カンマ1つで、そのファイルは JSON として読めなくなります。

複数の設定ファイルを読み、JSON の壊れと、ドキュメントで注意されている書き方をチェックする小さなスクリプトです。

import json
import re
import sys

merged = {"allow": [], "ask": [], "deny": []}

for path in sys.argv[1:]:
    try:
        with open(path, encoding="utf-8") as f:
            data = json.load(f)
    except json.JSONDecodeError as e:
        print(f"[NG] {path}: JSON が壊れています -> {e}")
        continue
    print(f"[OK] {path}")
    perms = data.get("permissions", {})
    for kind in merged:
        for rule in perms.get(kind, []):
            merged[kind].append((rule, path))

print()
for kind in ("deny", "ask", "allow"):
    print(f"--- {kind}(全ファイル合算)")
    for rule, path in merged[kind]:
        print(f"  {rule}  <- {path}")

print()
for rule, path in merged["allow"]:
    m = re.fullmatch(r"Bash\((.*)\)", rule)
    if m and "*" in m.group(1).rstrip("*").rstrip(":"):
        print(f"[注意] {rule}: * が末尾以外にあります。サブコマンドより前に * があると、広く許可しすぎます")
for kind in merged:
    for rule, path in merged[kind]:
        if re.match(r"(Write|Glob|NotebookEdit|MultiEdit)\(", rule):
            print(f"[注意] {rule}: パスのルールは Edit(...) か Read(...) で書きます")
        if re.match(r"Bash\(command:", rule):
            print(f"[注意] {rule}: Bash(command:...) の形は無視されます")

試しに、わざと間違いを入れた3つのファイルを用意しました。

user_settings.json(ユーザー設定のつもり)

{
  "permissions": {
    "allow": [
      "Bash(python build.py)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}

project_settings.json(末尾カンマあり)

{
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run lint)",
    ]
  }
}

local_settings.json(* の位置と Write の書き間違いあり)

{
  "permissions": {
    "allow": [
      "Bash(git * main)",
      "Write(docs/**)"
    ],
    "ask": [
      "Bash(rm *)"
    ]
  }
}
python -X utf8 check_settings.py user_settings.json project_settings.json local_settings.json
[OK] user_settings.json
[NG] project_settings.json: JSON が壊れています -> Illegal trailing comma before end of array: line 5 column 27 (char 86)
[OK] local_settings.json

--- deny(全ファイル合算)
  Bash(git push *)  <- user_settings.json
--- ask(全ファイル合算)
  Bash(rm *)  <- local_settings.json
--- allow(全ファイル合算)
  Bash(python build.py)  <- user_settings.json
  Bash(git commit *)  <- user_settings.json
  Bash(git * main)  <- local_settings.json
  Write(docs/**)  <- local_settings.json

[注意] Bash(git * main): * が末尾以外にあります。サブコマンドより前に * があると、広く許可しすぎます
[注意] Write(docs/**): パスのルールは Edit(...) か Read(...) で書きます

(Python 3.13.1 で実行した結果です。末尾カンマのメッセージ Illegal trailing comma は、今回の検証環境の Python 3.13.1 で出たものです)

  • project_settings.json は末尾カンマで読めず、npm test の許可がリストに入っていない
  • Bash(git * main) は、ドキュメントでも「サブコマンドより前に * がある」例として起動時に警告されるとされている形。git push origin main にも一致してしまう
  • Write(docs/**) は照合に使われない

check_settings「project_settings.json、破れてます」

張り紙「最後にカンマを1個つけただけなのに……」

……カンマ1個で、張り紙がまるごと読めなくなる。 Claude Code が壊れた設定ファイルをどう扱うかまでは今回確かめていませんが、少なくとも書いたつもりのルールが効いていない可能性は疑うべきです。

本物の Claude Code で確かめるなら、/permissions を使います。 ドキュメントによると、この画面にはすべてのルールと、それぞれがどの settings.json から来たかが一覧で出ます。

スクリプトいらなくない? /permissions で見ればいいのでは?

それが一番確実です。 スクリプトは、定期タスクを動かす前に JSON の壊れを機械的に見つけたいときの、おまけの見張り番です。

中級:無人タスクの許可リストを設計する手順

ここまでの話を、手順にまとめます。

  1. タスクが使う操作を全部書き出す(読むファイル、書くファイル、打つコマンド、開く URL)
  2. コマンドは、打つ形を1つに決めて、指示書に書く(&& でつながない、前に環境変数を付けない、作業フォルダを -C などで固定する)
  3. 同じ形を permissions.allow に完全一致で書く。変わる部分だけ * にし、* はサブコマンドより後ろに置く
  4. 絶対にやらせたくないことを deny に書く
  5. 無人のタスクには ask を残さない(止まるか、dontAsk なら拒否される)
  6. 「今すぐ実行」で1回見守り、出た確認を見て、指示書か許可リストのどちらかを直す
  7. 指示書に「許可リストにない操作が必要になったら、やらずに先へ進み、報告に書く」と書いておく

7番目は、指示書が見習いさんに向けた「張り紙にないときの過ごし方」です。 張り紙にないことを見つけたとき、確認係さんを呼ぶ前に自分で判断して、飛ばしてくれるようになります。

見習いさん「張り紙にないことは、やらずにメモしておきました」

店長「……成長したね」

……確認係さんの出番が、ついに来ない朝。

7番を書けば、1〜6はいらないのでは?

指示書は、見習いさんへの「お願い」です。 次の上級編のとおり、お願いと張り紙は、効き方がまったく違います。

ここから先は上級者向け。読み飛ばしてもOKです。

上級:お願い(指示書)と張り紙(permissions)は別物

ここは誤解しやすいところです。ルールを守らせているのは Claude というモデルではなく、Claude Code というプログラムです。

権限のドキュメントには、次のような注記があります。

  • 権限ルールは、モデルではなく Claude Code が強制する
  • プロンプトや CLAUDE.md の指示は、Claude が何をしようとするかに影響するが、Claude Code が何を許可するかは変えない
  • 許可を与えたり取り消したりするには、/permissions、ルール、権限モード、PreToolUse フック(ツールを使う直前に動く自作の判定)を使う

つまり、

  • 指示書に「確認しないで」と書いても、確認は出る
  • 指示書に「この形だけ使って」と書くと、許可リストに合う形を打ってくれやすくなるので、結果として確認が減る

見習いさん「指示書に『確認なしでやってよし』と書いてありました!」

確認係さん「それ、私の張り紙じゃないので」

……見習いさんへの手紙で、確認係さんの勤務表は変えられない。 だから、無人タスクは「指示書で形を決める」と「張り紙で形を許可する」の両輪で作るんです。

じゃあ指示書に「確認係さんは休みです」って書けば……

休みにできるのは、モードを変えられる人だけです。 見習いさんが勝手にシフトを組んだら、それはもう乗っ取りです。

上級:deny → ask → allow の順番と、deny は壁ではない

判定の順番は決まっています。deny、ask、allow の順に見て、最初に一致したものが結果になります。

権限のドキュメントによると、

  • ルールの細かさは順番に影響しない。広い deny Bash(aws *) は、細かい allow Bash(aws s3 ls) にも勝つ
  • allow で deny に例外を作ることはできない
  • ask と allow の関係も同じで、ask に一致すれば、細かい allow があっても確認が出る
  • deny はどの設定ファイルにあっても効く。ユーザー設定の allow より、プロジェクト設定の deny が勝つし、その逆も同じ

ここまでは「deny は最強」という話です。 でも、同じドキュメントにはこうも書かれています。

  • Bash のルールは、Claude が書いたコマンドの文字列に対して照合する
  • 同じプログラムを別の形で呼ぶと一致しない。deny や ask は「Claude がふだん書く形」を止めるもので、プログラムの周りの安全の境界ではない

例として挙がっているのが、Bash(git push *) の deny です。

止まる止まらない
git push origin maingit -C . push origin main、git -c push.default=current push origin main、git 'push' origin main

(権限のドキュメントの表より)

簡略モデルで git -C . push origin main が確認待ちになったのも、この話です。

実は、さっきの設定例の Bash(git push --force *) の deny も、git -C C:/work/repo push --force origin main のような形には一致しません。 この設定例だけを使い、ほかに広い allow や自動承認の設定がない場合に確認・拒否できるのは、allow の push を git -C C:/work/repo push origin main の完全一致にしているからです。 --force 付きは allow にも一致しないので、default なら確認が出て、dontAsk なら拒否されます。

deny「git push は通しません!」

git -C . push「こんにちは、git です。ちょっと寄り道して push しに来ました」

deny「……どうぞ(名前が違うので)」

……張り紙を名札で読んでいる警備員。

だから守りの本命は、allow を狭く書くことです。 deny は「よくある形を止める張り紙」と考え、本当に壁が必要なら、ドキュメントが案内しているとおり次を使います。

  • サンドボックス(sandboxing):OS のレベルで、シェルコマンドのファイル・ネットワークへのアクセスを制限する
  • PreToolUse フック:コマンドの全文を、実行前に自分のロジックで検査する

deny を100個書けば、全部の形をふさげるのでは?

git の書き方の数だけ張り紙が増えていきます。 100枚目を書いているころ、見習いさんは101通り目の書き方で push しています。

上級:CI なら dontAsk、コンテナなら bypassPermissions

Desktop の定期タスクではなく、CI(自動でビルドやテストを回す仕組み)やスクリプトから claude -p(対話なしで1回だけ実行するモード)を呼ぶ場合、選び方が変わります。確認を出す相手が最初からいないからです。

権限モードのドキュメントでは、CI 向けの出発点としてこんな例が紹介されています。

claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"

(公式ドキュメントに載っている例です。今回の検証環境では実行していません)

  • dontAsk:許可リストにないものは、待たずに拒否。セッションは入力を待たない
  • dontAsk では、ask ルールに一致したものも拒否される
  • AskUserQuestion(Claude からユーザーに質問するツール)も、allow に書いてあっても拒否される

さらに -p で動かすときの注意も、権限のドキュメントに書かれています。

  • プロジェクトの .claude/settings.json の permissions.allow は、フォルダを信頼するまで使われない
  • claude -p や SDK では信頼の確認画面が出ないので、信頼していないフォルダでは、プロジェクトの allow ルールが使われず、標準エラーに警告が出る

CI「プロジェクトの張り紙、読みました!」

Claude Code「その店、まだ信頼してないので、張り紙は無効です」

……店に入る前に、店そのものを信頼するかどうかの審査がある。 CI で使うなら、許可は --allowedTools か、信頼の設定を済ませた上でのプロジェクト設定で渡します。

bypassPermissions は、ドキュメントでは「隔離されたコンテナや VM でだけ」と明記されています。 しかもこのモードでも、ask ルールに一致したものや、重要なパスへの rm などは自動では許可されない(-p ではその分が拒否になる)と書かれています。 全部を許すモードにも、最後の砦は残してあるわけです。

bypass にしても止まるなら、もう何を信じれば……

張り紙を信じましょう。 モードは「確認係さんの働き方」、張り紙は「何をしていいか」。 無人で動かすときに頼りになるのは、結局、よく書かれた張り紙です。

上級:止まる以外の「動かない」――スリープとキャッチアップ

確認ダイアログで止まる以外にも、定期タスクが朝に動いていない理由があります。そもそも起動していないパターンです。

Desktop の定期タスクのドキュメントによると、

  • ローカルの定期タスクは、デスクトップアプリが起動していて、パソコンが起きているときだけ動く
  • スリープ中に予定時刻が来ると、その回はスキップされる。設定の「Keep computer awake」で、アイドル時のスリープを防げる(ノート PC のふたを閉じたらスリープする)
  • 起動時やスリープ解除時に、過去7日間の取りこぼしを調べ、いちばん新しい1回分だけを実行する(6日分取りこぼしても1回だけ)
  • 予定時刻から数分の遅れを入れて起動する。遅れの長さはタスクごとに決まっていて毎回同じ
  • 実行履歴では、スキップされた回にカーソルを合わせると理由(スリープ中だった、前の回がまだ動いていた、など)が見られる

ドキュメントでは、朝9時のタスクが夜11時に動くこともあるので、時刻が大事なら指示書にガードを書くように、と勧めています。

見習いさん「おはようございます! 朝の仕込みを始めます!」

時計「23時です」

……キャッチアップで、夜中に朝の仕込みが始まる。 「今日の日付を確かめてから作業する」「すでに今日の分があれば中止する」のように、指示書のほうで身を守っておきましょう。

パソコンの電源が切れていても動かしたい。

その場合はクラウドで動く routines を使うよう、ドキュメントが案内しています。 ただしクラウドは手元のファイルに直接触れない(リポジトリを新しく clone して動く)ので、何をさせるかで選びます。

チェックリスト

  • タスクを作ったら「今すぐ実行」で1回見守り、出た確認をすべて見た
  • タスクが使うコマンドを、指示書と permissions.allow で一字一句そろえた
  • コマンドを &&・;・| でつながず、前に環境変数を付けない形にした
  • * は、サブコマンドより後ろにだけ置いた
  • ファイルの許可は Edit(...)・Read(...) で書いた(Write(...) ではなく)
  • 無人のタスクに ask ルールを残していない
  • deny は「よくある形を止める張り紙」と理解し、守りの本命は狭い allow にした
  • settings.json の JSON が壊れていないか確かめ、/permissions でルールと出どころを確認した
  • 指示書に「許可にない操作はやらずに報告する」「日付と既存ファイルを確認する」と書いた
  • パソコンのスリープ設定と、取りこぼしたときの動きを確認した

10個。確認係さんの確認リスト。

確認をなくすための確認が10個。 でも1回やれば、翌朝からの確認はゼロです。

……たぶん。見習いさんが新しい包丁の持ち方を覚えない限りは。

チェックリストを定期タスクにやらせればいいのでは?

確認係さん「チェックしてよろしいですか?」

……チェックのための確認で、また朝が止まる。 最初の1回は、人間が起きているうちにやりましょう。

まとめ

冒頭では、誰もいない朝に、確認係さんが「よろしいですか?」と立ち尽くしていました。

確認係さんは、何も悪くないんです。 張り紙にないことを聞きに来ただけ。 店長が、寝る前に張り紙を書いていなかっただけ。

確認係さん「朝からずっと、聞いていただけなんです」

店長「……ごめん、張り紙、書いてなかった」

……謝る相手が、画面のダイアログ。 でも、この会話ができた人は、もう半分解決しています。

  • 確認ダイアログは、エラーではなく質問。default では返事を待つ
  • 対処は「今すぐ実行で1回見守る」と「permissions.allow に完全な形で書く」
  • 指示書と許可リストで、コマンドの形をそろえる
  • 判定は deny → ask → allow。ただし deny は壁ではなく、守りの本命は狭い allow
  • 無人なら ask を残さない。CI なら dontAsk、全部許すのはコンテナの中だけ

無人の朝を守るのは、確認係さんをクビにすることではなく、寝る前に張り紙を書いておくことです。

おうどん「よし、今夜から寝る前に、張り紙を100枚書く!」

……100枚も書いたら、たぶん寝る時間がなくなります。 そして起きていれば、確認係さんに自分で答えられます。

まずは、いちばん大事な定期タスクを1つだけ選んで、「今すぐ実行」を押してみてください。 出てきた確認が、そのまま明日の張り紙の下書きです。 確認係さんと一緒に、朝のかけうどんをすする日が来ますように🍜

参考資料

  • Claude Code Docs:Configure permissions — 操作の種類ごとの確認の有無、deny → ask → allow の順番、ルールの書き方(*、完全一致、:*)、複合コマンドの分割、環境変数とラッパーの扱い、Bash ルールが一致しない形、Edit・Read のパスの書き方と Windows での変換、Write ルールが使われないこと、ルールはモデルではなく Claude Code が強制すること、設定の優先順位、プロジェクトの allow と信頼、/permissions
  • Claude Code Docs:Choose a permission mode — 6つの権限モード、dontAsk が入力を待たずに拒否すること、bypassPermissions は隔離環境だけで使うこと、どのモードでも自動許可されない操作、守られたパス、CI 向けの claude -p の例
  • Claude Code Docs:Schedule recurring tasks in Claude Code Desktop — タスクごとの権限モード、~/.claude/settings.json の allow ルールが適用されること、手動確認のモードで止まること、「今すぐ実行」と「常に許可」、requiresUserInteraction のツール、スリープ時のスキップと取りこぼしの実行、SKILL.md の場所
  • Claude Code Docs:Settings files and precedence — 設定ファイルの場所と優先順位、permissions.allow などのリストが合算されること、Windows での ~/.claude の場所

確認日:2026-09-29。


よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

おうどん|癒しと創作をたのしむ雑食クリエイター
写経アプリやクレイセラピー、優しい和風デザインがすき。
ZARDと刀剣と文字に癒されて、今はアプリ作ってます。

コメント

コメントする

目次