コードを書かないSE日誌

SE歴26年。コードを書かずにClaude Codeで8体のAIエージェントチームを動かしています。AIエージェント・自動化・GTDの実験日誌。

Claude Code スケジュール実行で踏んだ6つの罠と対処法(Remote Trigger実運用録)

この記事は「Claude Codeのスケジュール枠は3つだけ——ディスパッチャー方式で何タスクでも回す設計」の続編です。前記事でディスパッチャーを組んで運用に入ったところ、複数のトラブルに遭遇しました。同じ轍を踏む人を減らしたくて書きます。

朝起きたらタスクが止まっていた

ある朝、Daily Dispatcher が動いていないことに気づいた。前日まで正常だったのに、ログが残っていない。

claude.ai/code/scheduled を開いてトリガーの設定画面を見ると、プロンプトの events フィールドがまるごと空になっていた。

「何もしていないのに壊れた」——そのパターンを6件経験した。以下はその記録と対処法だ。

落とし穴1: API部分更新でプロンプトが消える

何が起きたか

Daily Dispatcher のプロンプト(events フィールド)が消えた。session_contextoutcomes は残っているのに、プロンプト本体だけが抜け落ちていた。

推定原因

Remote Trigger API でトリガーを更新する際、job_config の一部だけを送ると、APIがそのフィールドを deep merge ではなくオブジェクト置換 で処理する模様だ。events を含めずに job_config.ccr を送った結果、events フィールドがまるごと落ちた。

[注] これは実挙動から推定した仮説で、API仕様書に明記された内容ではない。

なぜ気づきにくいか

  • outcomes など他のフィールドは残っているため、一見「正常に設定されている」ように見える
  • プロンプトが空でもセッションは「やることがない」として正常終了する。Web画面上では「実行成功」と表示される
  • 別トリガー(Hourly Dispatcher)は正常動作していたため、「スケジューラー全体の問題」とは気づけなかった

対処法

毎回、完全なオブジェクトを送る。部分更新は使わない。

更新スクリプトがある場合は job_config.ccr の中身を毎回フルで組み立てて送ること。「差分だけ送る」と安全そうに見えるが、フィールドが消えるリスクがある。

落とし穴2: 複数リポジトリで初期ディレクトリが不定になる

何が起きたか

トリガーの sources に2つのリポジトリ(000-partnerzenn-content)を指定していた。数日間は正常に動いていたが、ある日突然 .claude/schedules/daily.md が存在しません というエラーが出てタスクが全て失敗した。

調べると、リモートエージェントの初期ワーキングディレクトリが zenn-content になっていた。daily.md000-partner にあるため、見つからない。

なぜ数日間は気づかなかったか

初期ディレクトリがどちらになるかは保証されていない。偶然 000-partner で起動し続けていた状態が、ある日崩れた。1リポジトリのみの Hourly Dispatcher ではこの問題は発生しないため、比較対象がなくて原因特定に時間がかかった。

この問題はGitHub Issuesに報告済みだ(#47604: "Remote Trigger: initial working directory is non-deterministic with multiple source repositories")。現在もオープン。

対処法

プロンプトの冒頭に「初期化セクション」を追加して、実行環境を問わずリポジトリルートを自力で探索させる。

## 初期化(必ず最初に実行すること)

1. ls でカレントディレクトリを確認する
2. 000-partner ディレクトリを探す:
   find . -name "daily.md" -path "*schedules*" 2>/dev/null | head -5
3. 見つかったパスから cd で移動してから処理を開始する
4. 見つからない場合は「リポジトリが見つかりませんでした」とログに記録して終了する

根本的な解決はAnthropicの仕様確定待ちだが、このワークアラウンドで安定稼働できている。

落とし穴3: 実行履歴をAPIから取得できない(サイレント障害の温床)

何が問題か

Remote Trigger API には list / get / create / update / run しかない。実行履歴(runs)やセッションログを取得するエンドポイントが存在しない。

トリガーが動いたか、何が起きたかを確認するには claude.ai/code/scheduled のWeb画面を目視するしかない。ローカルCLIからはモニタリングできない。

二重の罠

落とし穴1と組み合わさると特に厄介だ。

  1. プロンプトが消える(落とし穴1)
  2. プロンプトが空なのでSlack通知プロンプトも動かない
  3. セッションは「やることがない」として正常終了する
  4. Web画面上は「成功」に見える
  5. ユーザーは気づかない

「Slack通知を仕込んでおけば大丈夫」と思っていたが、プロンプト自体が消えるとSlack通知すら動かない。完全なサイレント障害になる。

ワークアラウンド

成功時にも通知を送る設計にする。「通知が来ない=異常」というパターンを作れば、プロンプト消失にも気づける。

また、ディスパッチログを毎回 git push させておくと、ローカルから git pull してログを確認できる。Web画面を開かなくても状況を把握できるため運用が楽になる。

リモートセッションへの介入・デバッグ手段として --teleport を使ってローカルに引き取る方法もある。詳しくは「リモートセッションをローカルに引き戻す——Claude Code の新UIと teleport を使った話」を参照してほしい。

落とし穴4: outcomesのブランチ名不一致で別ブランチが生える

何が起きたか

Daily Dispatcher の outcomesbranches: ["master"] と設定していた。しかし zenn-content リポジトリのデフォルトブランチは main だった。

リモートエージェントが push しようとした際、master ブランチが存在しないため master-9nHf9 というサフィックス付きの新ブランチが自動生成された。コミットは入るが Zenn にデプロイされない。

なぜ気づきにくいか

  • リモートエージェントのログ上は「push 完了」と表示される(エラーにはならない)
  • Zenn側で「記事が公開されない」という症状だが、原因がブランチ名だとは思わない
  • 000-partner(master)と zenn-content(main)でデフォルトブランチ名が異なることを普段意識していない

対処法

outcomesbranches をリポジトリごとに正しいデフォルトブランチ名に合わせる。

"outcomes": {
  "branches": ["main"]  // zenn-content は main
}

複数リポジトリを sources に指定している場合、それぞれのデフォルトブランチ名を確認する習慣をつけること。

落とし穴5・6: リモート環境固有の罠(シークレットとOS)

落とし穴5と6は「ローカルでは動くがリモートでは動かない」という共通パターンを持つ。まとめて紹介する。

落とし穴5: シークレットがリモート環境に渡らない

クロスポストスクリプトがリモート実行時に QIITA_TOKEN 未設定でスキップされ続けていた。ローカルでは ~/Documents/qiita-content/.credentials からトークンを読み込んでいたが、リモートは fresh clone のためローカルのファイルに一切アクセスできない。

正攻法: Cloud Environment の環境変数を使う

claude.ai/code/scheduled の環境設定(Environment variables)に .env 形式でトークンを記述する。スクリプト側に変更は不要で、既存のフォールバックロジックで自動的に環境変数を優先して読む。

MCPコネクタという選択肢は現時点で使えない

スケジュール実行時に MCP コネクタのツールがロードされないバグが複数報告されている。

  • #35899: "Scheduled tasks cannot access MCP connectors until a user message warms the session"(Closed: v2.1.105で修正済み。ただし間欠的な再発の報告もあり、完全解消かは引き続き要確認)
  • #44785: "Scheduled remote triggers can't access claude.ai MCP connectors"(Closed as not planned)

44785 が「not planned」でクローズされており、MCPコネクタ経由のシークレット受け渡しは当面期待できない状況だ。#35899 は修正済みとしてクローズされたが、間欠的な再発報告もあるため過信は禁物だ。

Cowork経由でのMCP連携を検討している場合は、リモート環境での制約を踏まえた「Cowork×Claude Code 連携の正解」も参照してほしい。

落とし穴6: ローカルとリモートでOSが違う

ローカル環境は Windows、リモート環境(Remote Trigger)は 常に Linux だ。

クロスポストスクリプト内で $OSTYPE 判定して blogsync.exe(Windows)と blogsync(Linux)を切り替えていた。しかし Linux 版の blogsync バイナリをリポジトリに含めていなかった。スクリプトの OS 分岐は正しくても、バイナリが存在しなければ意味がない。

「OS分岐を書いた → 対応済み」と思い込んでいたのが盲点だった。

対策パターン

方法 コスト 備考
OS非依存な実装にする(curl + REST API) 改修必要 最も堅牢。今回採用
Linux版バイナリもリポジトリに含める 管理コスト増 バイナリサイズに注意
setup script でインストール 毎回インストールの時間 ツールが大きいと遅い

Remote Trigger で外部ツールを使う場合は「そのバイナリはLinuxで動くか」を最初に確認する習慣をつけること。

共通の教訓

6つの落とし穴を振り返ると、共通したパターンが見える。

Remote Trigger はサイレントに失敗しやすい

エラーが出ずに「成功」として終わるケースが多い。プロンプト消失、push先ブランチ間違い、スキップされたクロスポスト——どれも「処理が正常に終了した」ように見える。

「偶然動いている」状態に気づきにくい

複数リポジトリの初期ディレクトリ問題は数日間は偶然うまくいっていた。監視がなければ「壊れた瞬間」まで気づけない。

成功通知を仕込んで「通知が来ない=異常」を作る

失敗時の通知だけでなく、成功時にも Slack 通知を入れる。通知が来ない朝が「異常のシグナル」になる。プロンプト消失のように通知自体が動かないケースの検出にも有効だ。

最初の1〜2週間は毎日ログを確認する

安定稼働を確認するまでは claude.ai/code/scheduled で実行履歴を目視する。「数日間は動いていた」は「正常」ではなく「偶然」の可能性がある。

Anthropicへの提言

Remote Trigger は可能性が大きい機能だ。プロダクションレベルで安心して使えるようにするために、以下を提案したい。

モニタリング関連

  • 実行履歴APIの追加(runs エンドポイント): 実行結果をAPIから取得できないと自動モニタリングができない。CLIからログを確認できるだけで運用コストが大きく下がる
  • プロンプト空・無操作終了の区別: 現在は「何もせず正常終了」が「成功」と同じステータスになる。意図的なno-opと、プロンプト消失によるno-opを区別してほしい
  • プラットフォーム側のヘルスチェック通知: N回連続でno-op終了が続いた場合にユーザーへアラートを出す仕組み

設定・仕様関連

  • 複数リポジトリ指定時の初期ディレクトリの明示的な仕様化#47604): 決定的にするか、少なくとも「どのリポジトリになるか保証しない」と明記してほしい
  • MCPコネクタのスケジュール実行時バグの継続監視#35899): v2.1.105でクローズされたが、間欠的な再発報告もある。スケジュール実行でMCPツールが使えないと、できることが大きく制限される

ドキュメント関連

  • ローカル↔リモートの差異に関するドキュメント充実: リモートがLinux環境であること、ローカルファイルにアクセスできないこと、シークレットの渡し方——これらが一ページにまとまっているだけで多くのハマりを防げる

まとめ

6つの落とし穴をまとめると:

# 落とし穴 対処法
1 API部分更新でプロンプトが消える 毎回フルオブジェクトで送る
2 複数リポジトリで初期ディレクトリが不定 プロンプト内で自己探索する初期化セクションを入れる
3 実行履歴をAPIから取得できない 成功時も通知を送る・gitログ運用
4 outcomesのブランチ名不一致で別ブランチが生える リポジトリごとに正しいブランチ名を確認
5 シークレットがリモート環境に渡らない Cloud Environment の環境変数を使う
6 ローカルがWindowsでリモートがLinux OS非依存な実装(REST API直叩き)にする

Remote Trigger は「設定して終わり」ではなく、「設定してからが本番」だ。最初の1〜2週間は毎日ログを目視して、偶然動いている状態がないか確認することをすすめる。

同じ罠に落ちる人が一人でも減れば、この記事を書いた甲斐がある。

スケジュール実行を含むAIエージェントチームの全体設計と、つまずきながら組織を育てた実録を電子書籍にまとめています。

コードを書けない私が、AIに「チーム」を持たせるまで(序章・第1部無料)

この記事のテーマを深掘りした本

Claude Codeで作る1人エージェントチーム エンジニア向け。writer + reviewer の2体から始めるサブエージェント設計(序章無料)

シリーズ全6冊: Vol.1 作るまでVol.2 回すまでVol.3 書き続けるまでVol.4 仕組みを渡すまでVol.5 仕事を任せるまでVol.6 1人エージェントチーム