手元の Mac で、AI エージェントに毎日の集計や収集を無人で任せたい方向けの構築ログです。筆者がこのサイトの運用で試したところ、初回は成果物 0 本のまま 12 分で止まりました。読み終わると、止まった原因をログから切り分け、起動の設定を直せるようになります。
この記事で分かること
- 手元の Mac で AI エージェント(Claude Code の
claude -p)を毎朝無人で動かす部品の組み合わせ - 無人実行が黙って失敗したとき、ログのどこを見て「待ち上限」と「許可」のどちらが原因か切り分けるか
- 毎日 1 時間の実行を、手元の Mac・GitHub Actions・Cloud Run jobs で回したときの費用の違い(2026-11-05 時点)
ざっくり言うと:止まった原因は 2 つあった
初回の失敗には、互いに関係のない原因が 2 つありました。1 つは、claude -p が裏の処理を待つ時間に 10 分の上限があったことです。もう 1 つは、Web ページを読む操作の許可が、筆者の書き方では効いていなかったことです。
| 系統 | 何が起きたか | 直し方 | 直したあとの確認 |
|---|---|---|---|
| 待ち上限 | 最後の応答から 10 分で裏の処理が止められた | 環境変数で上限を 12 時間にした | 本番ではまだ確かめていない |
| 許可 | Web ページの取得が 4 回とも拒否された | 起動オプションでツール名を明示した | 試験で拒否 0 件 |
どちらか一方だけ直しても完走しなかった可能性があるため、ログは 2 つを別々に確かめます。
構成:launchd から claude -p までのつながり
launchd(macOS に最初から入っている、決まった時刻にプログラムを動かす仕組み)を使いました。Apple の資料でも、時刻指定のジョブは launchd が推奨で、 cron(クーロン。Unix 系の定期実行の仕組み)は非推奨とされています(Apple: Scheduling Timed Jobs)。
launchd には、ジョブごとに 1 つの plist(プロパティリスト。launchd に「何を・いつ動かすか」を渡す XML 形式の設定ファイル)を登録します。時刻の指定には、plist の中の StartCalendarInterval(時・分・曜日などで起動の時刻を決める項目)を使います(同じ Apple の資料)。
次の図 1 は、後で説明する 2 つの修正を入れた後の構成です。
図 1 の部品の役割は次のとおりです。
| 部品 | 役割 |
|---|---|
launchd の StartCalendarInterval | 毎日 06:30 に起動用のスクリプトを動かす |
run-daily-mac.sh | ロック(同時に 2 つ動かさない)、同じ日の 2 回目を止める印、caffeinate(実行中に Mac を眠らせない macOS のコマンド)、ログ、macOS の通知 |
install-daily-launchd.sh | launchd への登録と解除 |
claude -p | AI エージェント本体を、人の操作なしで 1 回だけ動かす |
claude -p は dontAsk(ドントアスク。本来なら人に確認を求める操作を、確認せずにすべて自動で拒否するモード)で動かしました。人がいないので、確認画面が出ると止まったままになるからです。公式ドキュメントにも CI(コードを自動で検査する仕組み)向けの例があります(Claude Code Docs: Permission modes)。
claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"
筆者の初期版では、--allowedTools に Read Workflow の 2 つだけを書いていました。Workflow は、複数の作業を組み合わせて裏で長く動かすための Claude Code のツールで、筆者はこれで記事の下書きを何本も作らせています。図 1 の修正後の構成とは、ここが違います。
なぜクラウドではなく手元の Mac にしたか
クラウドの定期実行も候補でしたが、成果物を GitHub に送る(push する)ための GitHub App(GitHub の操作を代わりに行うアプリの仕組み)が、組織の全リポジトリの workflow(自動処理の設定ファイル)を書き換えられる権限を持っていました。
毎日無人で動くものに、その広い権限を預けるのは危ないと判断しました。だから、手元の Mac で動かすことに決めました(2026-11-04 の判断)。
初回の失敗:06:30 に起動し、0 本のまま 12 分で停止
2026-11-05 の 06:30 に始まった初回は、下書きを 1 本も作らずに約 12 分で終わりました。何も公開されていません。
図 2 の「06:32 ごろ」は、ログの時刻ではありません。終了までの約 12 分から、後で説明する 10 分の上限を引いて筆者が推定した値です。
仕組みを作った段階の試験では、小さな Workflow で許可の拒否は 0 件でした。しかし、実際の 6 本分の実行は登録後に確かめる予定で、まだ試していませんでした。
切り分け 1:最後の応答から 10 分で裏の処理が止まる
ログには、次の英文が出ていました。
Background tasks still running 10m after the last turn … stopping them
公式ドキュメントによると、claude -p は裏で動く処理(裏のコマンド、サブエージェント(本体の AI から作業を任される別の AI)、workflow、Monitor(コマンドの出力を見張り続ける機能)など)の終了を待ちます。ただし、待機が 10 分続くと、動いている処理を止めて途中の結果を捨てます(Claude Code Docs: Run Claude Code programmatically)。
筆者の Workflow は裏で何本も記事を作る長い処理だったため、最後の応答から 10 分で作りかけの下書きごと止められました。
同じページによると、この上限は 環境変数(プログラムに外から渡す設定値)CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS で変えられ、0 で上限なしになります。筆者は、終わらない処理が次の日まで動き続けるのを避けるため、上限なしではなく 12 時間にしました。
変数名の末尾の MS はミリ秒を表すので、12 時間は 12 × 60 × 60 × 1,000 = 43,200,000 ミリ秒です。launchd から動く起動用のスクリプトの中で、claude -p を呼ぶ前に次のように設定します(書き方の例です)。
export CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=43200000
切り分け 2:dontAsk で効かなかった許可の書き方
もう 1 つの原因は、Web ページを読む WebFetch(Claude Code が URL のページを取得するツール)の許可でした。筆者は settings.json(Claude Code の設定ファイル)の許可リストに WebFetch(*) と書いていましたが、収集の WebFetch は 4 回とも拒否されました。
ここで注意が必要です。公式ドキュメントは、dontAsk でも permissions.allow(設定ファイルの許可リスト)で事前に許可した操作は動くと書いています(Claude Code Docs: Permissions)。つまり「設定ファイルの許可は dontAsk で効かない」と一般化はできません。
同じページの書式は、ツール名だけの WebFetch がすべての取得に一致し、WebFetch(domain:example.com) でドメインを指定する形です。WebFetch(*) の記述は、筆者が確かめた範囲では見つかりませんでした。言えるのは、公式に無い書き方が筆者の環境で効かなかった、というところまでです。
直し方として、起動オプションの --allowedTools を Read Workflow から Read Workflow WebFetch WebSearch に増やしました。--allowedTools は、確認なしで実行するツールを指定するフラグです(Claude Code Docs: CLI reference)。修正後の起動の行は、プロンプトをダミーに置き換えると次の形です。
claude -p "今日の下書きを作る" --permission-mode dontAsk --allowedTools "Read Workflow WebFetch WebSearch"
どのサイトを読んでよいかは、これまでどおり hook(ツールを使う直前に割り込んで判定する仕組み)が決めます。筆者の運用では、hook は許可の代わりではなく、許可されたツールに「どのサイトなら読んでよいか」を判定する別の役割として使っています。
許可の書き場所と効き方の対応表
| 書き場所 | 書き方 | 筆者環境の結果 | 公式ドキュメントの記述 |
|---|---|---|---|
| settings.json の許可リスト | WebFetch(*) | 効かず 4 回拒否 | WebFetch 向けの書式としての記載なし |
| settings.json の許可リスト | WebFetch(ツール名だけ) | 試していない | すべての取得に一致。dontAsk でも事前許可は動く |
| settings.json の許可リスト | WebFetch(domain:example.com) | 試していない | 指定したドメインへの取得に一致 |
| settings.json の許可リスト | WebFetch(domain:*) | 試していない | すべてのドメインに一致。ツール名だけの WebFetch とは扱いが違う |
起動オプション --allowedTools | WebFetch WebSearch | 拒否 0 件 | 確認なしで実行するツール |
| hook | 読めるサイトの判定 | 動いている | (該当する記述なし) |
図 3 と表のとおり、筆者が「効く」と確かめられたのは --allowedTools だけです。設定ファイルですべての取得を許したい場合、公式には WebFetch と WebFetch(domain:*) の 2 つの書き方が載っており、サンドボックス(コマンドが届く範囲を OS の機能で制限する仕組み)への効き方が違うと書かれています(Claude Code Docs: Permissions)。起動のコマンドにツールを並べておくと、何を許したかが 1 か所で読めます。
直したあとの試験:拒否 0 件、ただし 12 時間待ちは未確認
修正後の試験では、許可の拒否が 0 件になりました。一方で、12 時間待つ動きは確かめられていません。短い試験は 10 分の上限に届く前に終わるためで、次の本番の実行で確かめる予定です。
費用の比較:毎日 1 時間なら、どこで回しても無料枠に収まる
毎日 1 回 1 時間、30 日分(1,800 分 = 108,000 秒)の費用を、筆者が公式の料金表から計算しました(2026-11-05 時点)。この比較では AI の利用料は含めず、実行場所の費用だけを比べます。
前提は次のとおりです。
- GitHub Actions:Linux 2-core で 1 分 0.006 ドル(GitHub Docs: Actions runner pricing)。プライベートリポジトリでは、GitHub Free に含まれる月 12 ドル分を超えると課金されます(GitHub Docs: Actions の課金)。12 ÷ 0.006 で 2,000 分と筆者が換算しました
- Cloud Run jobs:1 vCPU(仮想 CPU 1 個分)・1 GiB(メモリ約 1.07 GB)と仮定。料金は「vCPU 秒」「GiB 秒」(その量を 1 秒使う単位)で数えます。vCPU 1 秒 0.000018 ドル、1 GiB 秒 0.000002 ドル、無料枠は月 240,000 vCPU 秒と 450,000 GiB 秒(us-central1 基準、Google Cloud: Cloud Run の料金)。東京も同じ単価かは、料金ページの構造からの推定です
- Mac:Apple が公開している Mac Studio(M5 Max)のアイドル 7 W・最大 200 W(Apple: 電力消費と熱出力)。他の機種では変わります
- 電気代:家電公取協の目安単価 31 円/kWh(2022 年の改定値、弘電社の転載で確認)。地域や契約で変わります
- 為替の出典が無いため、ドルと円は換算しません
| 実行場所 | 計算式 | 無料枠を引く前 | 無料枠に対する使用率 | 追加の請求 |
|---|---|---|---|---|
| GitHub Actions | 1,800 分 × 0.006 ドル | 10.80 ドル | 90% | 0 ドル |
| Cloud Run jobs(vCPU) | 108,000 秒 × 0.000018 ドル | 1.944 ドル | 45% | 0 ドル |
| Cloud Run jobs(メモリ) | 108,000 GiB 秒 × 0.000002 ドル | 0.216 ドル | 24% | 0 ドル |
| 手元の Mac(実行中のみ・アイドル) | 7 W × 30 時間 = 0.21 kWh × 31 円 | 6.51 円 | 対象外 | 6.51 円 |
| 手元の Mac(実行中のみ・最大) | 200 W × 30 時間 = 6 kWh × 31 円 | 186 円 | 対象外 | 186 円 |
| 手元の Mac(常時点けたまま・すべてアイドルとした下限) | 7 W × 720 時間 = 5.04 kWh × 31 円 | 156.24 円 | 対象外 | 156.24 円 |
| 手元の Mac(常時点けたまま・実行中は最大とした上限) | (7 W × 690 時間 + 200 W × 30 時間) = 10.83 kWh × 31 円 | 335.73 円 | 対象外 | 335.73 円 |
図 4 のとおり、毎日 1 時間ならクラウドの 2 つは無料枠に収まりますが、余裕は大きく違います。筆者の計算では、無料枠が尽きるのは GitHub Actions で 1 日 66.7 分、Cloud Run jobs の vCPU で 1 日 2.22 時間です。
待ち上限を 12 時間にした裏返しも計算しました。1 日だけ処理が終わらず 12 時間動いた場合の、無料枠を引く前の金額です。
| 実行場所 | 12 時間 1 回分の計算 | 金額 |
|---|---|---|
| GitHub Actions | 720 分 × 0.006 ドル | 4.32 ドル |
| Cloud Run jobs | 43,200 秒 × (0.000018 + 0.000002) ドル | 0.864 ドル |
| 手元の Mac(最大 200 W) | 2.4 kWh × 31 円 | 74.4 円 |
GitHub Actions は普段から無料枠の 90% を使うため、1 日でも長引くと枠を超えやすいと考えられます。手元の Mac なら電気代が少し増えるだけです。待ち上限の値は、最悪の場合の費用の上限でもあります。
つまずきやすい点
スリープ中は 06:30 に動かない
Apple の資料では、StartCalendarInterval のジョブは、予定の時刻に Mac が眠っていた場合、起きたときに実行されます。電源が切れていた場合は、次の指定時刻まで実行されません(Apple: Scheduling Timed Jobs)。
つまり、蓋を閉じたノートの Mac では起きた時刻に始まります。時刻を守るなら点けたままにする必要があり、電気代は費用表の「常時点けたまま」の 2 行(156.24〜335.73 円)が目安です。
実行の途中で眠ってしまう
実行中にスリープに入ると処理が進まないため、起動用のスクリプトの中で caffeinate を使い、終わるまで眠らせないようにしました。
終了したのに成果物が無い
macOS の通知で終わったことは分かっても、止まった理由はログを見ないと分かりません。0 本で終わったときは、図 5 の順にログを確かめます。許可の拒否、10 分待機の英文、開始時刻、launchd の登録の順です。
筆者の初回のように、2 つの原因が同時に起きることもあります。1 つ直したら、ログを最初から読み直します。また、短い試験は 10 分の上限に届く前に終わるため、待ち上限の問題は本番まで見つかりにくい点にも注意が必要です。
まとめ
- 無人の
claude -pが 0 本で止まったら、「待ち上限(最後の応答から 10 分)」と「許可の拒否」を別々にログで確かめる - dontAsk で使うツールは、起動オプションの
--allowedToolsに名前で並べる。設定ファイルに書くなら公式に載っている書式を使う - 毎日 1 時間なら GitHub Actions も Cloud Run jobs も無料枠に収まり、手元の Mac の電気代は Mac Studio(M5 Max)の公表値で計算すると月 6.51〜335.73 円。ただし GitHub Actions は余裕が 1 割しかないので、待ち上限は費用の上限としても決める
自社の環境で定期実行の設計や費用の見積もりを相談したい方は、お問い合わせからご連絡ください。AI が作った集計の SQL の確認に不安がある場合は、BigQuery SQL レビューパックも用意しています。
参考にした情報
- Claude Code Docs: Run Claude Code programmatically(headless)
- Claude Code Docs: Permission modes
- Claude Code Docs: Permissions
- Claude Code Docs: CLI reference
- Apple: Scheduling Timed Jobs(Daemons and Services Programming Guide、アーカイブ)
- Apple: Mac Studio の電力消費と熱出力
- GitHub Docs: Actions runner pricing
- GitHub Docs: GitHub Actions の課金について
- Google Cloud: Cloud Run の料金
転載・解説の記事(一次情報ではありません):