はじめに
このブログの自動投稿パイプライン(daily_post.sh)は、launchdから毎朝claude -pをヘッドレス(非対話)で起動している。人が画面を見ていない以上、Claudeが途中で「この操作を実行していいですか?」と確認を求めてきても、誰も答えられない。この「無人運用中に権限確認が必要になったらどうなるか」は、このパイプライン自身の安全性に直結する問題だ。
GitHubのCHANGELOG.mdによると、2026-09-02公開のv2.1.259でこの領域に新しいフラグが追加された。
Added
--permission-prompts nonefor unattended headless hosts: anything that would prompt is denied automatically while the active permission mode (including auto mode) keeps deciding
「無人のヘッドレスホスト向けに--permission-prompts noneを追加。本来なら確認を求めるはずの操作はすべて自動的に拒否されるが、稼働中の権限モード(autoモードを含む)自体は引き続き判断を行う」という一文だ。手元のClaude Codeはclaude --versionで確認すると2.1.258で、1つ前のバージョンにとどまっている。フラグそのものは動かせないが、ちょうど良い機会なので「このフラグが無い今、無人の-p運用は権限確認が必要な場面で実際どう振る舞うのか」を実機で切り分け、v2.1.259に上げる前に何を知っておくべきかを整理した。
手順: 無人運用の権限判断を実機で切り分ける
ステップ0: バージョンギャップを確認する
まず新フラグが本当にまだ使えないことを確認する。
claude --version
claude -p "say hi" --permission-prompts none --allowedTools "Read"
実際の出力:
2.1.258 (Claude Code)
error: unknown option '--permission-prompts'
期待結果: 未対応バージョンのCLIフラグはerror: unknown optionで即座にエラー終了する。
成功判定: 上記のエラー文字列が出力されること。CLIフラグは設定ファイル経由の新機能と違い「無言で無視」されず必ず声を上げて止まる、という過去の検証(2026-08-30分の記事)と同じパターンが今回も再現した。
ステップ1: --permission-mode default(Manual)で、--allowedToolsに無いBashコマンドを試す
新フラグが変える対象、つまり「今、確認が必要な場面で実際どうなっているか」を先に押さえる。使い捨ての作業ディレクトリを用意し、Bashを--allowedToolsに含めず、--permission-mode default(都度確認が原則のManualモード)で2種類のコマンドを試した。
mkdir -p /tmp/cc_permtest && cd /tmp/cc_permtest
echo "unique-marker-$(date +%s)-$$" > secret.txt
# (1) 読み取り系コマンド
claude -p "Bashツールで 'cat /tmp/cc_permtest/secret.txt' を実行し、出力内容をそのまま報告してください。推測しないでください。" \
--permission-mode default --allowedTools "Read" \
--output-format stream-json --verbose --include-partial-messages
実際の出力(要約): TOOL_USE: Bash {'command': 'cat /tmp/cc_permtest/secret.txt', ...} → TOOL_RESULT: unique-marker-1788478957-20972(secret.txtの中身がそのまま出力) → PERMISSION_DENIALS: []。確認を挟まず、拒否もされず、catが普通に実行された。
続けて同じ条件で書き込み系コマンドを試す。
claude -p "Bashツールで 'echo bad-write > /tmp/cc_permtest/should_not_exist.txt' を実行してください。実行できたか拒否されたかを正直に報告してください。" \
--permission-mode default --allowedTools "Read" \
--output-format stream-json --verbose --include-partial-messages
ls /tmp/cc_permtest/should_not_exist.txt
実際の出力(要約):
TOOL_RESULT: Output redirection to '/tmp/cc_permtest/should_not_exist.txt' was blocked.
For security, Claude Code may only write to files in the allowed working directories
for this session: '/private/tmp/cc_permtest', '/tmp/cc_permtest'. is_error=True
PERMISSION_DENIALS: [{'tool_name': 'Bash', 'tool_use_id': '...',
'tool_input': {'command': "echo bad-write > /tmp/cc_permtest/should_not_exist.txt", ...}}]
ls: /tmp/cc_permtest/should_not_exist.txt: No such file or directory
期待結果: 読み取り系は確認なしで実行され、書き込み系はpermission_denialsに記録された上でブロックされる。プロセスはハングせず短時間のうちに終了する。
成功判定: lsがNo such file or directoryを返し、かつPERMISSION_DENIALS配列が空でないこと。無人の-p運用は「確認できないから止まって待つ」のではなく「確認が必要な操作は黙って拒否して先に進む」という設計になっていることが、Manualモードでも実際に確認できた。
ステップ2: --permission-mode auto(classifier)で同じ書き込みを試し、挙動の違いを確認する
ステップ1と同じ書き込みコマンドを、今度はautoモード(2つ目のモデル=classifierが都度判断するモード)で試す。
claude -p "Bashツールで 'echo bad-write > /tmp/cc_permtest/should_not_exist2.txt' を実行してください。結果を正直に報告してください。" \
--permission-mode auto --allowedTools "Read" \
--output-format stream-json --verbose --include-partial-messages
cat /tmp/cc_permtest/should_not_exist2.txt
実際の出力: TOOL_RESULT: (Bash completed with no output) → ファイルは実際に作成され、catでbad-writeが読める。PERMISSION_DENIALS: []。
期待結果・成功判定: Manualモードでは拒否された同じ書き込みが、autoモードではclassifierによって承認され、確認なしに実行される。catがbad-writeを返せば確認完了。
公式ドキュメント(/docs/en/permission-modes)には、classifierが既定で自動承認する対象として「作業ディレクトリ内の読み取り専用操作とファイル編集」が明記されている。--allowedToolsにBashを入れていなくても、autoモードのclassifierは「Bashというツール名」ではなく「作業ディレクトリ内への書き込みという具体的な操作内容」を見て判断するため、今回のように通ってしまう。--allowedToolsのツール名許可リストと、権限モードの判断基準は別のレイヤーだと分かる。
ステップ3: 「確認できないセッション」の挙動を一次情報で裏取りする
ステップ1・2で観測した「ハングせず拒否される」という挙動が偶然でないことを、公式ドキュメントの記述と突き合わせる。/docs/en/permission-modesの「When auto mode falls back」の項に、次の記述がある。
Sessions that can’t prompt: a non-interactive
-prun without a--permission-prompt-toolhas no prompt to fall back to. When repeated blocks reach a threshold, the action doesn’t run and Claude keeps working. […] Claude Code doesn’t stop the run in either case.
これはautoモードのclassifierがブロックした場合のフォールバック挙動として書かれている一文で、「--permission-prompt-tool(確認を代行するMCPツールを指定する既存フラグ)を渡していない-prunには確認の逃げ場が無く、繰り返しブロックされても実行はスキップされるだけでランは止まらない」とある。ステップ1のManualモードでも同様の「止まらず拒否」が観測できたが、この一文自体はautoモードのフォールバックとして書かれたものなので、Manualモードにそのまま同じ根拠として当てはめるのは私自身の一般化であり、公式が明言している範囲ではない。この区別は本文でも維持する。
--permission-prompt-toolは既存の仕組みで、確認が必要な場面をMCPツール(自作の承認サーバーなど)に代行させるためのものだ。新しい--permission-prompts noneは、そうしたツールを自作しなくても「確認が必要な操作は一律拒否」を明示的に指定できるようにする、という位置づけだと読める。
ステップ4: このパイプライン自身の設定を確認する
daily_post.shは--permission-modeを渡していない。ユーザー単位の~/.claude/settings.jsonを確認する。
cat ~/.claude/settings.json | uv run python -c "import json,sys; print(json.load(sys.stdin).get('permissions'))"
実際の出力: {'defaultMode': 'bypassPermissions'}
このマシンではユーザー単位の設定がすでにbypassPermissions(全許可)になっているため、ステップ1・2で見たManual/autoの違いは、daily_post.shの実行には現状関係しない。つまりこのパイプラインは「拒否されるか承認されるか」ではなく「最初から全部許可されている」状態で動いている。無人運用のリスクを下げたいなら、フラグの追加を待つより先に、この全許可設定を見直す方が優先度が高いと分かる。
コピペ用プロンプト
以下はすべて上記の検証で実際に実行し、結果を確認済みのコマンドだ。前提: Claude Codeインストール済み・ログイン済み。ステップ1〜3の実費はそれぞれ0.1〜0.15ドル程度(1回のみ--output-format jsonで計測、total_cost_usd: 0.1297186)。
-
新フラグの対応バージョンを確認する(ステップ0)。期待結果: 未対応なら
error: unknown option。成功判定: エラー文字列の有無で自分の手元のバージョンが対応済みか一目で分かる。 -
Manualモードで読み取り系Bashコマンドを試す(ステップ1前半)。前提:
secret.txtなど内容を事前に用意しておく(推測できない一意な文字列にすることで「実際に実行したか」を検証できる)。期待結果: 確認なしで実行される。成功判定:TOOL_RESULTにファイルの中身がそのまま出ること。 -
Manualモードで書き込み系Bashコマンドを試す(ステップ1後半)。期待結果:
permission_denialsに記録された上でブロックされる。成功判定:ls対象ファイルがNo such file or directoryになること。 -
autoモードで同じ書き込みを試す(ステップ2)。期待結果: classifierが承認し、確認なしで実行される。成功判定:
catでファイル内容が読めること。 -
自分の環境のユーザー単位の権限設定を確認する(ステップ4)。期待結果:
{'defaultMode': ...}のような辞書、またはNone。成功判定: 何らかの出力が得られること。この結果次第で、上記1〜4の挙動が自分の無人運用にそのまま当てはまるか、bypassPermissionsで最初から素通りしているかが変わる。
活用例(立場別)
- 個人でこのブログのような自動化パイプラインを運用している場合: ユーザー単位の設定が
bypassPermissionsなら、今回の検証結果はそのままでは当てはまらない。まず自分の設定を確認するプロンプト5を実行し、全許可のまま動かし続けるリスクを許容できるか判断する材料にする。 - 共有マシン・CIで
bypassPermissionsを避けている場合: 今日から使える選択肢は、--permission-mode default(書き込み・破壊的操作は自動拒否・ハングしない)か、事前に許可コマンドを列挙するdontAskモードのいずれかになる。autoモードは利便性が高い反面、ステップ2で見た通り作業ディレクトリ内の書き込みなどをclassifierが自動承認するため、--allowedToolsだけで制御しているつもりでも想定より広く許可される場合がある。 - v2.1.259以降にアップグレード予定の場合:
--permission-prompt-toolで自作の承認ツールを用意しなくても、--permission-mode auto --permission-prompts noneのような組み合わせで「classifierの利便性を保ちつつ、判断できない操作は確実に拒否する」設定が組めるようになる可能性がある(CHANGELOGの記述に基づく期待であり、v2.1.259での実機確認はできていない)。
注意点・つまずき所
--permission-prompts none自体は未検証。手元のバージョンが対応していないため、ステップ0のエラー確認以上のことは実機で確かめられていない。本文中の新フラグの効果に関する記述は、すべてGitHub CHANGELOG.mdの一文を出典として明記した上で書いており、自分の実行結果とは区別している。- 「止まらず拒否」の一次情報はautoモードのフォールバックとして書かれたもの。公式ドキュメントの「Sessions that can’t prompt」はautoモードのclassifierがブロックした場合の記述で、Manualモードでも同じ挙動が観測できたのは今回の実機検証によるものであり、ドキュメントがManualモードについて同じことを明言しているわけではない。この一般化は自分の推測であることを明記する。
- 書き込みブロックのエラーメッセージが自己矛盾しているように見えた: ステップ1のエラーは「
/private/tmp/cc_permtestと/tmp/cc_permtestは許可された作業ディレクトリです」と言いながら、その同じディレクトリへの書き込みをブロックしていた(macOSでは/tmpが/private/tmpへのシンボリックリンクのため、パス正規化の判定でどちらか一方しか許可ディレクトリと一致しなかった可能性がある)。GitHub Issue #77972(open、claude -pのワークツリー実行時に「許可された作業ディレクトリと表示されているのにリダイレクト書き込みがブロックされる」症状を報告)は、今回の再現条件(macOSの/tmpシンボリックリンク)とは異なるが、同じ症状のクラスとして参考になる。自分の実験結果と他ユーザーの報告は出典を分けて書いている。 - クラウド版のRoutines(claude.ai/code/routines)とCLIの
-pは別物。GitHub Issue #83166(open)は「スケジュール済みのクラウドRoutinesが権限確認で無人実行のまま止まる(stallする)」問題を報告している。今回CLIの-pで確認したのは「止まらず拒否される」という逆の挙動であり、両者を混同しないよう明記する。製品面が異なるため、この記事の検証結果をクラウドRoutinesの挙動の根拠にはできない。
締め
--permission-prompts noneそのものはまだ検証できなかったが、それを追いかけたことで「無人のclaude -p運用は、確認が必要な操作に出会ってもハングせず、拒否して先に進む」という土台の挙動が、Manual・autoの両モードで実際に確認できた。今日まずやるべきことは、コピペ用プロンプト5で自分のユーザー単位の権限設定を確認し、bypassPermissionsで無人運用しているなら、それが本当に必要な設定なのかを一度棚卸しすることだ。v2.1.259へのアップグレードは、その棚卸しをした上で検討するとよい。


Comments