本文へスキップ
もと @moto_ai_jp

第5部 ループ · 第12回 / 全20回 · 15分くらい

人がいない時間に回すための、5つの部品

第4世代、ループです。第3世代で作った「1回分の仕組み」を、人がいない時間に回します。この回では、最低限そろえておく5つの部品を、macOS の launchd と Claude Code の画面なしのモードを例に見ていきます。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20

履修 0 / 20 回

この回を読み終えると、できるようになること

  1. 起動・重ならないための鍵・何度動かしても同じ結果・権限の絞り込み・記録の5つを説明できる
  2. Mac がスリープしていたとき、定時のジョブがどう動くかを説明し、それに合わせた作りにできる
  3. 画面なしで Claude Code を動かすときの、道具の許可と記録の残し方を書ける

専門用語は、初めて出たところで括弧に短い説明を付けています。説明が長くなるものには ※番号を付け、ページの終わりの用語メモにまとめました。

1部品1:起動。予定時刻は「早くてもこの時刻」と読む

決まった時刻に動かす係です。Mac なら launchd、サーバーなら cron(Linux などで定時にコマンドを動かす仕組み)、あるいはクラウドの定期実行を使います。

Apple のドキュメントによると、launchd の StartCalendarInterval(毎日何時、のような指定)は、Mac がスリープしていて予定時刻を過ぎた場合、起きたときに動きます。電源が切れていた場合は動きません(cron は、スリープ中の予定を飛ばします)。

つまり、予定時刻は「その時刻ちょうど」ではなく「早くてもその時刻」です。自分の環境では、日曜20時の週の振り返りが、4週続けて月曜の朝に動いていました。そして「今週」を動いた時刻から決めていたので、終わった週ではなく、始まったばかりの週を集計していました。「いつの分か」は、動いた時刻ではなく、対象の期間で決めます。

決まった時刻以外のきっかけで動かすこともできます。たとえば StartOnMount を使うと、ディスクをつないだときに動きます。持ち歩く MacBook のバックアップを、外付けのディスクをつないだときに取る、といった使い方です。

L1見て、報告するだけ何も変えないここから始めるL2下書き → 人が承認確定するのは人外に出すものはここまでL3納品まで自動便利だが危ない信頼が貯まってから右に行くほど便利になり、同時に危なくなる
必ず L1 から始め、報告が正しいと確かめてから次へ進みます。

2画面なしで Claude Code を動かす

定時実行の中でAIを使うときは、画面を開かないモードで動かします。Claude Code では claude -p "頼みごと" です。ドキュメントによると、--allowedTools で使ってよい道具を先に許可し、--output-format で出力の形(JSON など)を指定できます。成功すれば終了コード0、失敗すれば0以外で終わります。

誰も見ていない時間に動くので、ここで渡す道具が、そのまま「AIにできることの全部」になります。これが部品4の話につながります。

3部品2:重ならないための鍵(ロック)

前の回が終わらないうちに次の回が始まると、同じ作業が2重に走ります。スリープから起きた直後は、たまっていた予定がまとめて動くので、特に起きやすくなります。

動き始めにロック※1を取り、取れなければ何もせずに終わります。macOS には Linux の flock が無いので、「フォルダを作れた人だけが先へ進める」という mkdir の性質を使うのが手軽です。ロックを外し忘れて、二度と動かなくなる事故も多いので、終わるときに必ず外れる書き方にします(下の例の trap)。

#!/bin/zsh
# ロック:mkdir は、すでにあれば失敗する。作れた人だけが先へ進める
LOCK=/tmp/daily-report.lock
mkdir "$LOCK" 2>/dev/null || { echo "前の回が実行中"; exit 0; }
trap 'rmdir "$LOCK"' EXIT   # どう終わっても鍵を外す

claude -p "今日の日報を reports/ に作って" \
  --allowedTools "Read,Write" \
  --output-format json > "logs/$(date +%F).json"

読み方:|| は「左が失敗したら右を実行」、trap … EXIT は「スクリプトがどう終わっても、最後にこれを実行」という意味です。

4部品3:何度動かしても、結果が同じになる作り

同じ回を2回動かしても、結果が1回分と同じになる性質を、冪等性※2と呼びます。たとえば、

  • 「今日の分がもうあれば作らない」
  • 上書きではなく、対象の期間を名前に入れて作る(2026-W41.md)
  • 対象の期間を引数で渡せるようにする(--week 2026-W41)

こうしておくと、失敗したらもう一度動かせばいい状態になります。遅れて動いても、2回動いても、あとから作り直しても、同じ名前で同じものができます。運用がぐっと楽になります。

5部品4:権限を絞る

人がいない時間に動くAIには、その作業に必要な道具だけを渡します。日報を書くだけなら、ファイルの読み書きだけ。送信や削除ができる道具は渡しません。

ただし、絞りすぎても困ります。自分の環境では、AIの定期タスクがファイルを書くたびに許可を待って止まり、29時間そのままになっていたことがありました。必要な操作は先に許可し、危ない操作は渡さない、の両方を決めておきます。

6部品5:記録。成功・失敗・何もしなかった、を分ける

何時に動き、何をして、どう終わったか。成功も失敗も残します。記録がないと、壊れたときに「いつから壊れていたか」がわかりません。

大事なのは、「成功した」と「何もしなかった」を分けて記録することです。入力が無かった、送り先が無かった、今日はもう済んでいた。こうした回を「成功」と記録すると、第1回で見た「気づかないうちに壊れる」がそのまま起きます。自分の環境では、記録に done(実際に動いた)・skipped(何もしなかった、理由つき)・failed の3つを分けて書くようにしました。次の回の見張りは、この記録を読んで成り立ちます。

誤解よくある思い違い

毎日2時に登録したのだから、毎日2時に動く

launchd は、スリープ中に過ぎた予定を起きたときに動かします。電源が切れていれば動きません。「いつの分か」は、動いた時刻ではなく対象の期間で決めます。

ロックを取れば、重なりの問題は解決する

ロックを外し忘れると、二度と動かなくなります。どう終わっても外れる書き方と、古いロックに気づく見張りがセットで要ります。

演習手を動かして確かめる

演習1

「毎日23時に、その日の作業ログをまとめる」ジョブがあります。Mac を22時に閉じて、翌朝8時に開いたとき、何が起きるでしょうか。どう直せば正しくなりますか。

答えを見る

launchd は、起きた翌朝8時にジョブを動かします。「その日」を動いた時刻から決めていると、翌日(まだ始まったばかりの日)をまとめてしまい、前の日の分は作られません。

直し方は、対象の日を「動いた時刻」ではなく「まとめていない最後の日」で決めることです。たとえば、まだまとめていない日を記録から探してそれを対象にする、または「23時より前に動いたら前日の分」と決めます。対象の日を引数で渡せるようにしておけば、取りこぼした日をあとから作り直すこともできます。

演習2

上の例のスクリプトで、trap 'rmdir "$LOCK"' EXIT の行を消すと、何が起きるでしょうか。

答えを見る

スクリプトが途中で失敗して終わったとき(claude がエラーになった、など)に、ロックのフォルダが残ります。次の回からは毎回「前の回が実行中」と判断して、何もせずに終わり続けます。しかも終了コードは0なので、成功のように見えます。気づかないうちに壊れる、典型的な形です。

自分は、定時実行の登録ファイルを、リンクではなく実体のファイルで置くようにしています。リンク先の原本が壊れたとき、登録ごと巻き込まれて、バックアップが16日止まったことがあったからです。

用語メモ本文の ※番号の説明

  1. ※1ロック 同じ処理が同時に2つ走らないようにするための鍵。最初に鍵を取れたものだけが先に進み、取れなかったものは待つか、あきらめて終わる。 本文へ戻る ↩
  2. ※2冪等性(べきとうせい) 同じ操作を何回くり返しても、1回行ったときと同じ結果になる性質。定時実行の仕組みにこの性質があると、失敗したときにもう一度動かすだけで済む。 本文へ戻る ↩

出典確かめた資料(2026年10月時点)

読めたらチェック

この回:0 / 3