Claude Code のスケジューラで定期タスクを仕込んだのに、翌朝には止まっていた——この挙動のほとんどは不具合ではなく仕様です。止まる原因は大きく三つあり、それぞれに設計上の対策があります。本稿では、空ディレクトリから本番向けのヘルスチェックを一本通しで作り、三つの失敗モードに対策を入れ、実行先の選び方まで示します。検証日は 2026-09-07(JST)です。
止まる 3 つの原因を、仕様として押さえる
第一に、定期タスクはセッションに紐づきます。新しい会話を始めると停止し、既存の会話の中でだけ生き続けます。したがって「毎朝ターミナルを閉じて開き直す」運用は、それ自体がタスクを消す操作になります。
they live in the current conversation and stop when you start a new one
つまりタスクは現在の会話の中に存在し、新しい会話を始めると停止するという性質です。回避は --resume または --continue での再開で、期限内のタスクは復元されます。ただし後述のとおり、バックグラウンド Bash とモニタータスクは復元対象外です。
第二に、定期タスクは作成から 7 日後に自動的に期限切れになり、最後に一度だけ発火して自己削除します。7 日を超えて継続させるには、期限前にキャンセルして再作成するか、Routines か Desktop scheduled tasks を使います。
第三に、発火タイミングは正確ではありません。スケジューラーは毎秒発火予定を確認して低優先度でキューに入れますが、スケジュールされたプロンプトはターンとターンの間にだけ発火し、Claude が応答中には発火しません。さらに取りこぼしの追いかけ実行はありません。定期タスクはスケジュール時刻から最大 30 分(毎時間より高頻度なら最大でインターバルの半分)遅れて発火し、そのオフセットはタスク ID から導出されます。
本番で止めない:実行先の選び方(docs にない設計指針)
三つの実行先——/loop、Desktop scheduled tasks、Cloud——は最小間隔と前提条件が異なります。
Minimum interval 1 hour 1 minute 1 minute
左から Cloud・Desktop・/loop の最小間隔で、Cloud は 1 時間、Desktop と /loop は 1 分です。加えて Cloud は Anthropic 管理で自分のマシンも開いたセッションも不要、Desktop は自分のマシンが必要だが開いたセッションは不要、/loop はマシンとセッションの両方が必要です。
ここから導かれる私の指針です(事実ではなく設計判断として書きます)。稼働 SLA を持つタスクは Cloud か Desktop に置き、/loop と in-session の定期タスクは「作業中の一時的な自動化」に限定します。/loop を長く回したいときは、セッションをバックグラウンドに移すと /loop タスクが引き継がれ、ターミナルなしで実行を続けます。ただしプロセス自体は生きている必要があります。月単位でプロセスも気にせず回すなら Cloud が唯一の答えです。
もう一つ。ジッターと取りこぼし非追従がある以上、定期タスクを「正確な時刻に一度だけ」に依存させないことです。すべてのタスクを冪等に作り、実行の重複と欠落の両方に耐える設計にします。
通しで作る:本番ヘルスチェック(ゼロから完成まで)
作業ディレクトリを用意します。
mkdir -p prod-heartbeat/.claude && cd prod-heartbeat
.claude ディレクトリやその中のタスクファイルがシンボリックリンクだと、Claude Code はスケジュール登録の代わりにエラーを返します。着手前に実体ディレクトリであることを確認します。
test -L .claude && echo "SYMLINK: will error" || echo "real dir: OK"
成功時はこのコマンドが real dir: OK を標準出力に返します。SYMLINK: will error が出た場合は、リンクを実体ディレクトリに置き換えてから先へ進みます。
冪等なチェックスクリプトを置きます。取りこぼし非追従とジッターで発火が重複・遅延しても、同じ時間帯に二重書き込みしないよう「時(hour)」単位の鍵で去重します。
cat > check.sh <<'SH'
#!/usr/bin/env bash
set -euo pipefail
STAMP="$(date +%Y%m%d%H)" # 冪等キー(時単位)
LOG="heartbeat.log"
grep -q "^${STAMP} " "$LOG" 2>/dev/null && exit 0 # この時間は実行済み
if curl -fsS --max-time 10 https://example.com/health >/dev/null; then
echo "${STAMP} OK" >> "$LOG"
else
echo "${STAMP} FAIL" >> "$LOG"
fi
SH
chmod +x check.sh
自己ペースの /loop を使う場合の手順ファイルを置きます。内容は簡潔に保ちます(25,000 バイトを超えると切り捨てられます)。
このイテレーションで実行すること:
1. bash check.sh を実行する。
2. heartbeat.log の末尾行を確認し、直近が FAIL のときだけ簡潔に報告する。
3. 変更がなければ noop として静かに次へ回す。
毎回この手順を繰り返すこと。
権限を最小化しておきます。
{
"permissions": {
"allow": ["Bash(bash check.sh)", "Read(heartbeat.log)"]
}
}
セッションを起動し、1 時間間隔の定期タスクを作ります。
claude
会話の中で次のように依頼します。「1 時間ごとに bash check.sh を実行し、直近が FAIL のときだけ報告する定期タスクを作って」。Claude が cron ツールで登録します。登録後、「今スケジュールされているタスクを一覧して」と依頼すると、作成した定期タスクが 1 件表示されます。一覧が空のときは登録されていないので、直前のシンボリックリンク確認に戻ります。
動作確認:何が起きれば成功か
タスクが発火して 1 時間が経過するごとに、heartbeat.log に 1 行ずつ追記されます。
tail -n 3 heartbeat.log
成功していれば 2026090714 OK のように「時刻キー + OK」の行が時間ごとに 1 行増えます。ここで冪等性を検証します。同じ時間内に手動でもう一度走らせても、行は増えません。
bash check.sh && wc -l < heartbeat.log # 直前と同じ行数なら去重が効いている
行数が変わらなければ、発火の重複・遅延に耐える設計が効いています。セッションを再開しても復元されることは、いったんターミナルを閉じ、--continue で開き直して一覧に同じタスクが残ることで確認します。
claude --continue
詰まりどころ・限界・応用
第一に、取りこぼしは追いかけません。長時間リクエストの最中に発火予定を過ぎると、アイドル復帰時に一度だけ発火し、経過した回数分は実行されません。
No catch-up for missed fires.
だからこそ、上の去重ロジックの逆——「欠落検知」も要ります。運用では、heartbeat.log の最終行の時刻が想定間隔より古ければアラートする外部監視を併設します。
第二に、7 日の壁です。in-session の定期タスクは 7 日で失効するため、それより長く回すには失効前の再作成が必要です。system cron から claude --continue -p "定期ヘルスチェックが無ければ再作成して" を数日おきに叩く手はありますが、-p(ヘッドレス)は応答後に終了するため、それ自体はタスクを発火し続けません。あくまで再武装専用と割り切り、常駐はバックグラウンドセッションか Cloud/Desktop に委ねます。
第三に、復元されないものがあります。バックグラウンド Bash とモニタータスクは再開時に復元されません。再開後に手動で貼り直す前提で運用します。
第四に、上限とジッターです。1 セッションで同時に持てるスケジュール済みタスクは 50 件までです。ワンショットで正確な時刻が必要なら、:00 や :30 以外の分(例: 3 9 * * *)を選ぶとワンショットのジッターを回避できます(定期タスクの遅延は回避できません)。
第五に、環境差と非常停止です。feature-flag フェッチをオフにした環境(Amazon Bedrock・Google Cloud Agent Platform・Microsoft Foundry 等)で動的インターバルやプロンプトなし /loop を使うには Claude Code v2.1.248 以降が必要です。それ以前では、インターバルなしのプロンプトは固定 10 分で動き、プロンプトなしの /loop は usage メッセージを表示します。全タスクを止める非常停止は、環境変数 CLAUDE_CODE_DISABLE_CRON=1 の設定です。cron ツールと /loop が使えなくなり、既存のスケジュール済みタスクも発火しなくなります。障害時に暴走タスクを一括で止める手段として、起動スクリプトに組み込んでおくと安全です。