Claude Codeの自動化手段は4つある ── Routines・Desktop予定タスク・/loop・自前cronを要件で使い分ける

Claude Codeの自動化手段は4つある ── Routines・Desktop予定タスク・/loop・自前cronを要件で使い分ける Claude Code活用

はじめに

「Claude Codeで毎日/毎時これを自動でやらせたい」と思ったとき、実は選択肢が1つではないことに気づきにくい。公式ドキュメントを見ると、Anthropicは次の3つを並べて比較している。

  1. Routines(クラウド・Anthropicマネージド。マシンを閉じても動く)
  2. Desktop scheduled tasks(Claude Code Desktopアプリ内蔵。ローカルファイルに直接アクセス)
  3. /loop(CLIの対話セッション内で完結する、開いている間だけ動く一時的なスケジューリング)

だが実際にはもう1つ、ドキュメントの比較表には載らない現実解がある。

  1. OSのcron/launchdなどからclaude -p(非対話モード)を叩く自前の自動化

このブログの日次投稿自体もこの4つ目の方式(launchd + claude -p)で動いている。4つとも「Claude Codeを定期的に動かす」という同じ目的のための手段だが、「マシンを閉じてよいか」「ローカルファイルを読み書きする必要があるか」「セッションを開いたままにする前提か」で適材適所がまったく違う。混同すると「動くはずなのに数時間動かない」「思ったより粗い頻度でしか実行できない」といった事故につながる。

本記事では公式ドキュメント3ページ(Routines・Run prompts on a schedule・Desktop scheduled tasks)の内容を、4つ目の自前cron方式も加えた1本の意思決定フローに統合する。さらに/loopが内部で使うCronCreate/CronList/CronDeleteという3つのツールを、実際にこの記事を書いているClaude Codeセッション内で動かし、生の実行ログを載せる。加えて、そのツール自身が持つ説明文と公式ドキュメントのページ本文とで、ジッター(実行タイミングのずれ)の計算式が食い違っているという、両方を読み比べないと気づけない点も見つけたので正直に報告する。

執筆時点のCHANGELOG.md上の最新版はv2.1.268(2026-09-11公開)。

手順:4つの自動化手段を1本の意思決定フローに統合する

ステップ1: 比較表を作る(公式3手段+自前cron)

公式ドキュメントの”Compare scheduling options”表(出典)に、4列目として自前cron方式を追加するとこうなる。

Routines(クラウド) Desktop予定タスク /loop(CLI) 自前cron + claude -p
実行場所 Anthropicマネージドのクラウド 自分のマシン 自分のマシン 自分のマシン/サーバー
マシンを起動している必要 不要 必要 必要 必要(サーバーなら常時稼働)
セッションを開いたままにする必要 不要 不要 必要 不要
再起動をまたいで持続 する する --resumeで一部復元(例外あり) する(OS側の設定次第)
ローカルファイルへのアクセス 不可(毎回フレッシュclone) 可能 可能 可能
最小実行間隔 1時間 1分 1分 任意(cron側の設定次第)
権限確認 無し(自律実行) タスクごとに設定可 セッションの設定を継承 起動オプション次第

出典は上表の左3列がRun prompts on a schedule、右端の自前cron列は公式には存在しない独自の追加分類(このブログのdaily_post.sh + launchdでの運用に基づく)。

ステップ2: 3つの質問で選ぶ

  1. 「ローカルのファイル(gitignore対象や未コミットの変更、ローカルのみの設定ファイルなど)に直接触る必要があるか?」
    → YESならRoutinesは使えない。Routinesは毎回リポジトリをGitHubからフレッシュcloneするだけで、あなたのマシンのファイルは見えない(公式ドキュメントに明記)。Desktop予定タスクか自前cronに絞られる。
  2. 「マシンを閉じている間・スリープしている間も動く必要があるか?」
    → YESならRoutines一択。Desktop予定タスクは「アプリが起動していてマシンが起きている間だけ」動く仕様で、スリープ中は実行がスキップされる(後述)。自前cronもマシンがスリープすれば止まる。
  3. 「今開いている対話セッションの文脈(会話履歴・today’s作業ディレクトリ)を引き継いだまま、数分〜数時間だけポーリングしたいだけか?」
    → YESなら/loopが最も手軽。ただし7日で自動失効し、セッションを閉じると原則消える(後述)。

ステップ3: 実際に/loopのスケジューリング機構を動かして確かめる

/loopは内部でCronCreate/CronList/CronDeleteという3つのツールを使っている(公式ドキュメントRun prompts on a scheduleに明記)。この記事を書いている実際のClaude Codeセッション内で、この3つを直接呼び出して検証した。

> CronCreate(cron="47 9 * * *", prompt="ブログ記事用の動作確認ダミータスク。何もせず終了してよい。", recurring=true)
Scheduled recurring job 81bed083 (Every day at 9:47 AM). Session-only (not written to disk,
dies when Claude exits). Auto-expires after 7 days. Use CronDelete to cancel sooner.

> CronList()
81bed083 — Every day at 9:47 AM (recurring) [session-only]: ブログ記事用の動作確認ダミータスク。何もせず終了してよい。

> CronDelete(id="81bed083")
Cancelled job 81bed083.

> CronList()
No scheduled jobs.

期待結果: CronCreateが8桁の英数字ID(81bed083)を返し、CronListで内容が確認でき、CronDelete後は再び空になる。
成功判定: 上記4回の呼び出し結果が実際にこの通りになれば成功。今回は狙い通りの結果が得られた。8桁ID・「Session-only」・「Auto-expires after 7 days」という文言は、公式ドキュメントが説明する「セッションスコープ・7日で自動失効」という仕様(出典)と一致していた。

つまずき所1: ジッターの計算式がツール自身の説明と公式ドキュメントで食い違っている

CronCreateツール自体が持つ説明文(ツール定義に埋め込まれたテキスト。呼び出す側に見える一次情報)には、こう書かれている。

Recurring tasks fire up to 10% of their period late (max 15 min); one-shot tasks landing on :00 or :30 fire up to 90 s early.

一方、公式ドキュメントRun prompts on a scheduleの”Jitter”節にはこう書かれている。

Recurring tasks fire up to 30 minutes after the scheduled time (or up to half the interval, for tasks that run more often than hourly). … One-shot tasks scheduled for the top or bottom of the hour fire up to 90 seconds early.

具体的な数値で比較すると、毎日1回(周期1,440分)のタスクの場合、ツール説明の計算式(周期の10%、上限15分)では「最大15分」の遅延になるはずだが、ドキュメント側の説明(上限30分固定)では「最大30分」になる。同じ製品の同じ機能について、呼び出し側が実際に目にする説明文と公開ドキュメントの説明文で、遅延の上限が2倍近く違う。どちらが実装の実態と一致しているかは今回の検証(即座に作成・削除しただけ)では確認できておらず、断定はしない。ただし「正確な時刻に実行されることを前提にした設計をしない」という結論はどちらの記述でも変わらない。時刻がずれてよいように、プロンプト自体に「対象は直近24時間分」のような幅を持たせる設計にしておくのが安全側の対応になる。

つまずき所2: /loopは7日で自動失効し、セッションを閉じると原則消える

/loop(CronCreate)で作ったタスクは、セッションスコープであり、新しい会話を始めると原則クリアされる。--resume/--continueで再開すれば復元されるが、動的にペースを決める自己ペース型の/loop(間隔を指定しないモード)は--resumeしても復元されない、と公式ドキュメントに明記されている(出典)。さらに固定間隔のタスクも作成から7日で自動失効する。「先週セットアップしたはずの監視ループが、いつの間にか動かなくなっていた」という事故が起きうる設計であり、長期運用が前提の自動化には向かない。公式ドキュメントも同じ結論で、7日を超える運用にはRoutinesかDesktop予定タスクを使うよう案内している。

GitHub Issue #90883(open)は「/loopスキルの固定間隔CronCreateステップには実行保証が無く、モデルがステップ自体をスキップしうる」という報告で、7日失効とは別に「そもそも登録されないことがある」という一段手前のリスクも指摘されている。また、Issue #64744(open)は「ScheduleWakeupがCtrl+Cの後も残り続け、無人で自己再生成を繰り返して想定外のトークン消費が発生した」という報告で、/loop系の自動再開機構を無人で長時間放置するリスクの実例になっている。いずれも他ユーザーの報告であり、自分で再現確認したものではない。

つまずき所3: Desktop予定タスクの「見逃した実行」は直近1回だけキャッチアップされる

Desktop予定タスクはアプリが起動していてマシンが起きている間しか発火しない。マシンがスリープしていた場合、アプリ起動時・マシン復帰時に「過去7日間で見逃した実行が無いか」をチェックし、最新の見逃し分1回だけをキャッチアップ実行する(それより古い分は切り捨てられる)。公式ドキュメントは「毎日9時に予定していたタスクが、PCが1日中スリープしていたせいで夜11時に実行される」ことがある、と明言し、対策として「プロンプト自体にガードレールを書く」ことを勧めている。具体例としてドキュメントに載っているプロンプト文言はこうだ。

Only review today’s commits. If it’s after 5pm, skip the review and just post a summary of what was missed.

これは「スケジュールの正確性をアプリ側に期待するのではなく、プロンプト側で時刻をチェックさせる」という設計方針であり、Routinesの「同じ時刻ぴったりに動くとは限らないので範囲を持たせる」という考え方と共通している。GitHub Issue #89881(open)では「Desktop予定タスクの実行のたびにCLIサブプロセスとMCPサーバーが終了せずに残り続け、2日間で約92GBのプロセスが積み上がった」という報告がある。長時間ローカルマシンで予定タスクを回し続ける場合は、定期的にプロセス数やディスク使用量を確認したほうがよい。

つまずき所4: Routinesの/fire APIは渡したtextをそのままでは実行してくれない

RoutinesのAPIトリガーは、Webhookなどからtextフィールド付きでPOSTするとルーティンを起動できる。ただしこのtextは生のメッセージとしてClaudeに渡るのではなく、<routine-fire-payload>というタグで包まれ「信頼できない外部データ」として渡る(プロンプトインジェクション対策)。ルーティン側の保存済みプロンプトが「routine-fire-payloadブロックに書かれたアラートを調査せよ」のように明示的にそのブロックを参照しない限り、Claudeはtextの中身を単なる文脈情報として扱い、指示として実行しない。「監視ツールからwebhookでアラート内容を送ったのに、Claudeが何もアクションを取ってくれない」というつまずきは、この仕様を知らないままtextを投げているケースで起きやすい。GitHub Issue #92910(open)は「Routines(スケジュールタスク)がPENDINGのまま永久に止まり、セッションが一切実行されない」という別種の不具合報告で、API/GitHubトリガーに限らずスケジュールトリガー自体が固まるケースもあることを示している。

活用例:場面別にどれを選ぶか

ここまでの整理を、具体的な業務場面に当てはめるとこうなる(Routinesの用途例は公式ドキュメントに列挙されている実例、それ以外は本記事の整理)。

  • 毎晩のバックログ整理・週次の依存関係監査(マシンを閉じていい・ローカルファイル不要)→ Routinesのスケジュールトリガー。
  • 本番デプロイ後のスモークテスト(CI/CDから都度キックしたい)→ Routinesの API トリガー。
  • PRオープン時の自動レビュー(GitHubイベントに反応させたい)→ RoutinesのGitHubトリガー。
  • 毎朝のローカルコードレビュー・未コミットの変更を含めた朝の棚卸し(ローカルファイルに直接アクセスしたい)→ Desktop予定タスク。
  • 今動かしているデプロイ・CIの完了待ち(数分〜数時間、今のセッションの文脈のまま)→ /loop。
  • ブログの自動投稿のような、サーバー上で無期限に回し続けたい定型処理(GitHubトリガーもAPIトリガーも不要で、単に決まった時刻に決まったコマンドを叩ければ十分)→ 自前cron/launchd + claude -p。このブログの日次投稿はこの形。

コピペ用プロンプト集

1. 【無料・数秒】今のセッションに登録済みのスケジュールタスクを確認する

前提: Claude Codeの対話セッション(またはこのようなエージェントセッション)を開いていること。課金なし、外部リソース構築も不要。

今のセッションに登録されているスケジュールタスク(CronCreateで作ったもの)を
一覧表示して。無ければ「無い」とだけ答えて。

期待結果: CronListが呼ばれ、タスクが無ければNo scheduled jobs.、あれば各タスクのID・スケジュール・プロンプトが返る。
成功判定: 実行結果がその場で表示されること(API呼び出しを伴わないローカル処理のため、コストはほぼゼロ)。
活用例: 過去に自分や他のセッションが仕掛けたループを、作業開始前に棚卸しする習慣づけに。
注意点: セッションスコープのため、別セッションで作られたタスクはここには出てこない。

2. /loopで一定間隔のポーリングを仕掛ける

前提: 監視したい対象(デプロイ・CI・ビルドなど)が実際に進行中であること。

/loop 10m このリポジトリのCIが通ったか確認して、通ったら結果を教えて。
まだなら「まだ実行中」とだけ返して次の確認まで待って。

期待結果: 10分間隔のcronジョブが作成され(0/10に丸められる場合がある)、以後10分おきにCI状況を確認するプロンプトが自動で送られる。
成功判定: 直後にCronListを実行し、10分間隔のジョブが1件登録されていることを確認する。
活用例: 長めのCIやデプロイのポーリングを「毎回自分で聞き直す」手間から解放する。
注意点: 7日で自動失効し、新しい会話を始めると原則消える。1週間を超える監視には向かない。

3. 一回限りのリマインダーを仕掛ける

前提: とくになし。

1時間後に、このディレクトリのgit statusを確認して未コミットの変更が
無いかだけ教えて。

期待結果: 1回限りのCronCreate(recurring=false)が作られ、1時間後に1度だけ発火して自動的に削除される。
成功判定: CronListで発火前は1件、発火・報告後は0件になる。
活用例: 「後で忘れずに確認したいこと」を口頭指示だけで仕込める。
注意点: セッションを閉じると消えるため、長時間離席する用途には向かない。

4. プロジェクト共通のデフォルト/loopプロンプトを用意する

前提: 対象プロジェクトのルートに.claude/ディレクトリを作れること。

mkdir -p .claude
cat > .claude/loop.md << 'EOF'
release/next ブランチのPRを確認して。CIが赤ならログを取得して原因を特定し、
最小限の修正をプッシュして。新しいレビューコメントがあれば全て対応して。
すべて緑で静かなら、その旨を1行で報告して。
EOF

期待結果: 以後そのプロジェクトで引数無しの/loopを実行すると、組み込みのメンテナンスプロンプトの代わりにこのloop.mdの内容が使われる(公式ドキュメント記載の挙動)。
成功判定: /loopを実行し、応答がloop.mdに書いた指示(release/nextブランチの確認)に沿った内容になっていること。
活用例: チームで「このプロジェクトの/loopは何をするものか」を1ファイルに固定できる。
注意点: ファイルは25,000バイトを超えると切り詰められる(公式ドキュメント記載)。個別のプロンプトを渡す/loop <prompt>の場合はこのファイルは無視される。

5. Desktop予定タスクを自然言語で作る(Claude Code Desktopが必要)

前提: Claude Code Desktopアプリがインストール済みで、対象フォルダを信頼済みであること。本記事ではDesktopアプリを起動して自分の環境で確認したものではなく、公式ドキュメント記載のコマンド例として紹介する(誠実性のため明記)。

毎朝9時に、昨日マージされたPRの一覧をまとめて教えてくれるタスクを作って。
午後5時を過ぎていたらレビューはスキップして、見逃した分の概要だけ報告して。

期待結果: Desktopの「Routines」ページに毎日9時開始のローカルタスクが作成される(公式ドキュメント記載)。
成功判定: DesktopアプリのRoutines一覧に該当タスクが表示され、「Run now」で手動発火した際に指示通りの応答が返る。
活用例: ローカルファイルへのアクセスが要る朝の定型チェックを自動化する。
注意点: マシンがスリープしていた時間帯の実行は最新1回だけキャッチアップされ、古い分は切り捨てられる。プロンプト内に「対象は直近24時間」のような自衛的な条件を必ず入れる。

6. Cloud Routineを作る(claude.aiアカウントに実際のルーティンが作られる。実行注意)

前提: claude.aiアカウントでログイン済みで、Pro/Max/Team/Enterpriseいずれかのプランであること。このコマンドを実行すると実際に自分のアカウントにルーティンが作成され、以後指定した頻度で実行され続ける(日次実行上限を消費する)。 本記事ではこのコマンド自体は実行しておらず、公式ドキュメント記載のコマンド例としてのみ紹介する。

/schedule daily PR review at 9am

期待結果(公式ドキュメント記載): Claudeが対話形式でリポジトリ・プロンプト・トリガーを確認し、claude.ai/code/routinesにルーティンが保存される。
成功判定: /schedule listで作成したルーティンが表示されること。
活用例: 毎晩のバックログ整理・毎週の依存関係監査など、マシンを閉じても止めたくない定型業務。
注意点: 不要になったら必ずclaude.ai/code/routinesまたは/scheduleから削除・一時停止すること。作成したことを忘れると、日次実行上限を静かに消費し続ける。

まとめ

Claude Codeの「定期実行」は1つの機能ではなく、実行場所とライフサイクルが異なる4つの選択肢(Routines・Desktop予定タスク・/loop・自前cron)の集まりだと捉えたほうが事故が少ない。今回の検証で得られた実務上の指針は次の3つに集約できる。

  1. ローカルファイルに触る必要があるかどうかが、Routinesを使えるかどうかを最初に決める。
  2. /loopは「今開いているセッションでの数分〜数時間の一時的なポーリング」専用と割り切り、7日を超える運用や新しい会話をまたぐ運用には使わない。
  3. どの手段を選んでも「予定した時刻ぴったりに動く」ことは保証されない(ジッターやスリープからの復帰の仕様がそれぞれ異なる)。時刻のズレを前提に、プロンプト側に「対象範囲」のガードレールを書いておく。

GitHub Issueで報告されている不具合(スケジュールがPENDINGのまま止まる、プロセスが終了せず溜まり続ける、無人再生成でトークンを消費し続けるなど)はいずれも自分で再現確認したものではないが、「無人で長時間動かす自動化には、動いているかどうかを外から確認する仕組みを別途用意する」という教訓は、これまでこのブログで扱ってきた自動化ヘルスチェックの話とも一致する。

Comments

Copied title and URL