スケジュール実行で動くはずのスキルが、ログを見ると「12時にトリガーされてから何も進んでいない」状態で止まっている — この現象に最初に出会ったのは、Dolice Labs の4サイトを毎日 Claude のスケジュールタスクで運用し始めた頃でした。エラーログには何も出ず、タスクの実行ステータスは「実行中」のまま。手動で同じスキルを走らせると問題なく完了します。
原因を切り分けていって、犯人が「許可ダイアログ」だと気づくのに半月かかりました。Read・Write・Edit といったファイルツールや、request_cowork_directory のようなフォルダ選択ダイアログを呼び出すツールは、無人実行のセッションでも内部で「ユーザーの許可」を待ちます。誰も操作しない時間帯に走らせていると、永遠に応答が返ってこない、という静かな失敗です。
ここでは私が 4サイト分のコンテンツ自動生成スキルを 1ヶ月間スケジュール実行で運用しながら確立した、許可ダイアログで止まらない 3つの実装パターンをお伝えします。Claude Code のスキルだけでなく、Cowork モードや Claude Agent SDK のスケジュールタスクでも同じ考え方が使えます。
なぜ「無人実行で止まる」は最も気づきにくい失敗なのか
許可ダイアログが原因の停止は、エラーが一切残りません。スタックトレースもタイムアウトログも返ってこず、タスクは「正常に走り続けている」ように見えます。気づくのは数日後、「あれ、今週分の記事が増えていないな」と気づいて初めて、ということがしばしばです。
この失敗が厄介な理由は3つあります。
- ローカルで手動実行すると再現しません(手動実行ではダイアログに自分で OK を押せるので素通りします)
- ログに痕跡が残らず、原因切り分けにテレメトリが効きません
- 個別のツール呼び出し単位では「正しい使い方」をしているため、コードレビューで指摘しづらいです
私の場合、SKILL.md の中で Read ツールを cat $FILE の代わりに使っていただけで、24時間スケジュール実行が止まり続けたことがあります。ローカルで何度動かしても問題が出ないため、半月ほど「VM の調子が悪いのかな」と的外れな調査をしていました。
スケジュール実行で動くスキルを設計するときの原則はシンプルです。「人間に問い合わせる可能性のあるツールを 1つも使わない」 こと。これを徹底するための具体的な手段が、以下の3パターンになります。
パターン1: ファイル操作は bash の cat / sed に統一する
Claude Code のファイルツール(Read・Write・Edit)は、対話セッションでは便利ですが、無人実行では落とし穴になります。これらは内部で「ファイルアクセスの許可」を確認する場面があり、許可済みでない経路にアクセスしたタイミングでダイアログが立ち上がります。
無人実行を前提にするなら、ファイル操作はすべて bash 経由に置き換えます。私が実際に SKILL.md でやっている書き換えは次のような形です。
# ❌ 悪い例(無人実行で止まる可能性あり)
Step 2: `Read` ツールで content/config.json を読み込み、
設定値を確認してから `Write` ツールで更新する。
# ✅ 良い例(bash で完結する)
Step 2: bash で以下を実行する:
cat content/config.json | jq '.version'
# 更新は cat << 'EOF' で書き出す
cat << 'EOF' > content/config.json
{
"version": "1.2.3",
"updated_at": "2026-05-09"
}
EOF
# 部分編集は sed -i を使う
sed -i 's/"version": ".*"/"version": "1.2.4"/' content/config.jsonヒアドキュメント(<< 'EOF')を使うと、シェルの変数展開を抑えてそのまま書き出せます。バッククォートやドル記号を含むファイルでも安全に扱えるため、設定ファイル・MDX・HTML テンプレートのいずれにも応用できます。
部分編集は sed -i で十分カバーできます。Claude Code が自動で生成するスキル例では Edit ツールが多用されがちですが、無人実行用のスキルではすべて sed か awk に置き換えるのが安全です。複雑な置換が必要なら python3 -c "..." で短い Python スクリプトを書いて流し込むこともよくやります。
スキル発動の判定の仕組みについては、Claude Code で Skills が発動しないときの確認ポイント で詳しく書きましたが、無人実行でのトラブルシューティングはまた別の難しさがあります。
パターン2: パスは「実行のたびに動的検出」する
無人実行で 2番目によく踏む地雷が、ハードコードされたパスです。スケジュールタスクは実行ごとに別のセッションで走るため、ワークスペースのマウント先が変わることがあります。前回動いたパスが今回は存在しない、という状況が普通に起こります。
私が運用している SKILL.md では、ワークスペースのパスを動的に検出する 1行を必ず先頭に置いています。
# ワークスペースを毎回検出する(パスがセッションごとに変わるため)
WS="$(ls -d /sessions/*/mnt/Workspace\ Name 2>/dev/null | head -1)"
if [ -z "$WS" ]; then
echo "❌ ワークスペースがマウントされていません"
exit 1
fi
# 以降は $WS 配下のパスで安全にアクセスできる
cat "${WS}/_config/settings.json"request_cowork_directory や類似のツールを呼ぶと、ユーザーにフォルダ選択を求めるダイアログが立ち上がります。無人実行ではここで永遠に止まります。動的検出に切り替えれば、ワークスペースが既にマウントされていれば自動でパスを取得し、なければ即座にエラーで終了するため、「無音で固まる」を「明確に失敗する」に変換できます。
「無音の停止より、明確な失敗の方が運用しやすい」というのが、運用半年で得た一番大きな学びです。
GitHub からのリポジトリ操作も同じ発想で、git clone のたびに --depth 1 で浅くクローンし、作業ディレクトリは /tmp 配下の決まったパスに固定するようにしています。スケジュール実行の VM はディスク容量が限られるため、df で残量を確認してから書き込む癖もつけました。Claude Code を CI 環境で動かすときの基本姿勢としては、Claude Code を --bare フラグでヘッドレス実行する で書いた話とつながる部分があります。
パターン3: エラー時は「ユーザーへの問いかけ」を発生させない自動リカバリ
3つ目は、エラーハンドリングの設計です。無人実行のスキルでもっともやってはいけないのは、エラー発生時に「次のステップに進んでよろしいですか?」と聞くような実装です。Cowork モードで使える AskUserQuestion のようなツールは、対話セッションでは便利ですが、スケジュール実行では絶対に呼んではいけません。
私の SKILL.md では、エラー時のリカバリ手順を以下のように明文化しています。
## エラー時の自動リカバリ原則
- bash コマンド失敗時 → 別の bash コマンドで同じ目的を達成する
経路を試みる(例: `git push` 失敗 → `git pull --rebase`
後に再試行)
- リポジトリ破損 → `rm -rf $WORK_DIR` で削除して再 clone
- ディスク不足 → 古いキャッシュを段階的に削除し、それでも
足りなければログに記録して終了
- 「次のステップ」「手動で実行してください」等のユーザーへの
案内は絶対にしない(無人実行のため誰も読まない)
復旧不可な場合のみログファイルに失敗を記録して終了する。
ユーザーへの確認・許可申請・問い合わせは一切行わない。このルールを SKILL.md の冒頭に書いておくと、Claude が判断に迷うたびに「自律実行のためユーザーには聞かない」を選んでくれるようになります。逆にこのルールがないと、「念のため確認しておきます」と勝手に判断されて止まることがあります。
エラー時のリカバリ経路は、できれば 2段階用意します。1段目で復旧できなければ 2段目を試し、それでも駄目ならログに Status: FAILED と書いて終了する、という構造です。私のスケジュールタスクでは、git push が失敗したら git pull --rebase origin main で更新してから再 push、それでも駄目ならリポジトリを再 clone する、という 3段構えにしています。1ヶ月運用してみて、git push 起因で完全に失敗したケースは数えるほどしかありませんでした。
ツールごとの権限設計を本格的にやるなら、Claude Code Permission Modes の本番セキュリティ設計 で扱った設定ファイル .claude/settings.json の permissions ブロックでツール単位の許可・拒否ルールを定義する方法と組み合わせると効果的です。スケジュール実行用には Read・Write・Edit を deny リストに入れておくと、誤って呼び出した瞬間に明確なエラーで止まるようになります。
検証は必ず「スケジュール実行 1回」で行う
最後に、テストの話を 1つだけ書かせてください。新しいスキルを SKILL.md に追加した後、ローカルで手動実行してみて「動いた」だけで本番投入してはいけません。許可ダイアログ系のバグはローカルでは絶対に再現しないため、スケジュール実行で 1回試すまで安心できないというのが鉄則です。
私が運用しているのは以下の手順です。
- スキル更新 → ローカルで 1回だけ手動実行して基本動作を確認
- スケジュールタスクの「次回実行」を 5分後にセット
- 5分待って実行ログを確認 → ダイアログ起因の停止が出ていないかチェック
- ログに失敗が記録されていなければ通常の頻度に戻す
この検証フローを入れてから、本番投入後に「夜中に止まっていた」という事故がほぼゼロになりました。逆に言うと、ローカルでの手動テストは無人実行の検証にはまったくならない、ということでもあります。
まずは SKILL.md の 1行を書き換えてみる
すでに動いているスキルがある方は、SKILL.md を開いて Read または Write ツールへの言及を 1箇所だけ cat または cat << 'EOF' > file に書き換えてみてください。それだけでスケジュール実行の安定性が体感できるはずです。Skills の小さな修正を積み重ねる方法論については、Claude Code Skill Composition の設計術 でより踏み込んだ内容を扱っています。
無人実行は地味で、動いているときには誰も気づかない作業です。だからこそ、止まらない設計を一度確立しておくと、自分の時間が静かに増えていきます。お読みいただきありがとうございました。