CC for Biz
2026/05/03Claude Code
Skills活用導入・運用

launchd自動コミット × クラウドAIの状態同期問題

launchd自動コミット × クラウドAIの状態同期問題

「launchd 自動コミット」「git push 競合 解消」「Mac 自動化 git」と検索する人の多くは、ローカルで動かしている自動化処理と、クラウド上のAIエージェントの状態がずれて困っている状況に直面しているはずです。私自身、Mac上のlaunchdで毎日23:00に自動コミットを走らせていた運用に、クラウド側のAIエージェント(GitHub上のリポジトリを参照して動くRoutine)を組み合わせた瞬間、最大23時間のラグが発生する問題にぶつかりました。本記事では、launchd plistの設計、StartCalendarIntervalのarray化、git push競合をrebaseで解消する手順、そして自動化を追求した先にある限界とトレードオフまで、実装ベースで解説します。

ローカルAIとクラウドAIの「状態同期問題」とは何か

まず、なぜlaunchd × クラウドAIエージェントの組み合わせで問題が起きるのかを整理します。

ローカルAI(私の場合はMacのターミナルで動くClaude Code)は、ローカルファイルシステムを直接読み書きします。一方、クラウドAIエージェント(GitHub Actions経由のRoutine、Vercel cron上のスクリプト、リモートエージェントなど)は、ローカルではなくGitHub上のコードベースしか見ることができません。

つまり、ローカルで作業した結果がGitHubにpushされていなければ、クラウドAIは古い状態のまま動きます。これがローカルとリモートの「状態同期問題」です。

具体的にどう困るかというと、私の運用ではこんな現実が起きました。

  • 朝、Macで案件のメモやコードを編集する
  • 昼、クラウドAIエージェントに「今日の案件状況をまとめて」と指示する
  • クラウドAIはGitHub上のコードしか見られないので、昨夜23:00時点のスナップショットで返答する
  • 結果、午前中の作業がまるごと反映されていない報告が返ってくる

1日1回の自動コミットだと、最悪のケースで23時間の同期遅延が発生します。これではせっかくのクラウドAIエージェントが、最新状態を見られない「過去視点の参謀」になってしまいます。

ローカルAIとクラウドAIの状態同期問題の構造図

同期方式の選択肢を比較する

解決策の方向性は大きく3つあります。

  • 頻繁な自動コミット: launchdのスケジュールを増やし、3〜6時間ごとにpushする(私が選んだ方法)
  • イベント駆動: ファイル保存をトリガーにした即時push(fswatch + git)
  • クラウド側に処理を寄せる: Vercel cronやCloudflare Workersなどクラウド完結のジョブに移行する

結論から言うと、3つすべてに一長一短があります。私はまず一番ローコストな1番目から手をつけました。

launchd 自動コミットの基本構造

「launchd 自動コミット」で検索する方が最も知りたいのは、実際に動くplistとshellの最小構成だと思います。私の現行構成を分解して説明します。

launchdとは何か(最低限の前提)

launchdはmacOSの標準スケジューラです。LinuxのcronやsystemdのTimerに相当します。~/Library/LaunchAgents/にplistファイル(XML)を置いて、launchctl loadで登録するだけで、指定時刻にスクリプトが起動します。

cronと比べた特徴は次のとおりです。

  • plistはXML。記述量はやや多いが、構造化されていて読みやすい
  • Mac起動中のみ動く。スリープ中は実行されない
  • ただし、StartCalendarIntervalで指定した時刻にスリープしていた場合、復帰時に追走実行される(重要)
  • 標準出力・標準エラーの出力先をplistで指定できる

plistの最小構成

1日1回、23:00に実行するplistはこのようになります。

~/Library/LaunchAgents/com.example.git-autocommit.plist

  • Label: ジョブ識別子(一意になる逆DNS表記)
  • ProgramArguments: 起動するコマンドと引数(配列)
  • StartCalendarInterval: 起動時刻(dictで指定)
  • StandardOutPath / StandardErrorPath: ログ出力先

登録は次の流れです。

  • launchctl unload ~/Library/LaunchAgents/com.example.git-autocommit.plist(既存があれば)
  • launchctl load ~/Library/LaunchAgents/com.example.git-autocommit.plist
  • launchctl list | grep git-autocommit で登録確認

StartCalendarInterval と StartInterval の罠

頻度を上げる方法を調べると、最初に出てくるのがStartInterval(秒数指定)です。一見シンプルですが、私はこれを採用しませんでした。理由は次のとおりです。

  • StartIntervalは「launchd起動後N秒経過ごと」に発火する。Macが再起動すると基準時刻がリセットされる
  • つまり「毎日0時/3時/6時のような時刻基準」では動かせない
  • 運用ログを見て「いつ動いたか」を予測しづらい

そこでStartCalendarIntervalをarray化します。dictを複数並べた配列にすることで、複数の時刻指定が可能になります。

私のケースでは、3時間ごと(0時/3時/6時/9時/12時/15時/18時/21時)の8回スケジュールに変更しました。これなら時刻基準で確実に動き、Macがスリープしていても次の起床時に追走実行されます。

StartCalendarIntervalをarray化して頻度を上げる比較図

shellスクリプトの最小構成

plistから呼び出すshell側は、次のような構造にしています。

  • 対象リポジトリごとにディレクトリを切り替えてgit statusを実行
  • git status --porcelainで変更検知(出力ゼロ=クリーン)
  • 変更があればadd → commit → fetch → rebase → push の順で実行
  • 各ステップの成否をログに記録(scripts/logs/git-autocommit.logに追記)
  • 失敗カウンタを保持し、1件以上失敗したらosascriptでmacOS通知を出す

シンプルですが、これが「自動化の最小単位」です。最初は1リポジトリ・1スケジュールから始めて、徐々にリポジトリ数とスケジュール数を増やしていくのが安全です。

StartCalendarIntervalのarray化と古いskipロジックの落とし穴

頻度を上げる過程で、私は1つ大きな罠を踏みました。それは「過去に書いた最適化ロジックが、新しいスケジュールと矛盾する」という落とし穴です。

もともとのskipロジック

1日1回・23:00運用の頃、私はこういうロジックをshellに書いていました。

  • 「今日23:00以降に成功実行したマーカーファイルがあれば、再実行をskipする」
  • 復帰時の追走実行で同じ日に2回commitが走る事故を防ぐためのロジック

この最適化は、1日1回の運用では理にかなっていました。しかし3時間ごとに変更した瞬間、このskipロジックが致命的になります。

  • 0時に成功実行 → マーカー更新
  • 3時に発火 → 「今日すでに成功しているのでskip」
  • 6時、9時…全部skip
  • 結果、頻度を上げたつもりが1日1回のまま

ログを見て初めて気づきました。1週間ほど「なぜpushされない時間が偏るのか」と頭をひねった末の結論です。

教訓: 自動化スクリプトのロジックは、スケジュール仕様と密結合している

この経験から学んだのは、自動化スクリプトの中のロジックは、呼び出し側のスケジュール仕様と切り離せない関係にあるということです。スケジュールを変えるなら、スクリプト内の前提も全部見直さないと、見えないバグになります。

具体的にやったのは次の2点です。

  • マーカーファイルベースのskipロジックを削除
  • 「今走るかどうか」の判断はlaunchd側に完全委譲し、shellは呼ばれたら必ず動く設計に統一

シンプル化の鉄則どおり、判断を1箇所に集める方が長期運用では強いと改めて実感しました。

git push 競合 解消 — rebase戦略の実装

頻度を上げてもう1つ発生したのが、「git push 競合 解消」で検索される現象、いわゆるnon-fast-forward問題です。

競合が発生する仕組み

私の運用ではクラウドAIエージェント(Routine)がGitHubに直接commit・pushします。一方、ローカルのlaunchd autocommitもpushします。タイミングが重なると、こうなります。

  • 10:00 ローカルlaunchdがpushして成功
  • 11:30 クラウドAIがcommit & push(GitHubが先に進む)
  • 12:00 ローカルlaunchdがpushしようとする → 「Updates were rejected because the tip of your current branch is behind」

これがnon-fast-forwardのエラーです。「リモートが先行しているので、追いついてからpushしろ」という警告ですね。

fetch + rebase + push の順番

解消の基本は、push前にリモートの最新を取り込むことです。私はpullではなくrebaseを選びました。

  • git fetch origin <branch>
  • git rebase origin/<branch>
  • git push origin <branch>

pull --rebaseでも同じ結果になりますが、私はfetchとrebaseを明示的に分けています。理由はログの可読性です。fetchでリモートを取得した時点と、rebaseで取り込んだ時点を別ログ行に出すことで、後でトラブルシュートする際に「どこで失敗したか」が一発で分かります。

rebase失敗時のフォールバック

autocommit運用で最も怖いのは、rebaseが衝突して中途半端な状態のまま放置されることです。コンフリクトしたファイルがworking treeに残ったまま、次のautocommitが走ると、状況が雪だるま式に悪化します。

そのため、私は次の安全策を入れています。

  • rebaseの戻り値が0以外なら、即git rebase --abortで元の状態に戻す
  • 「rebase failed (conflict), skipped push」とログに記録
  • 失敗カウンタをインクリメント
  • macOS通知で「⚠ autocommit 失敗」を表示し、私が手動で介入する

rebaseは強力ですが、自動化に任せきれない領域があります。「自動でできる範囲はやる、できない範囲は人間に通知する」の境界線を明確に持っておくことが、長期運用の鍵です。

git push 競合 解消のためのfetch+rebase+pushフロー図

Mac 自動化 git の限界と現実的な落としどころ

「Mac 自動化 git」というキーワードで来た方に、最後にぜひお伝えしたいのが、launchd × クラウドAIで完全な24/7同期を作るのは不可能だという現実です。

launchdの根本的な制約

launchdは、Macが起動している間しか動きません。次のような状況では確実に同期が止まります。

  • 深夜にMacをシャットダウン or 完全スリープ → 朝まで動かない
  • 外出中にMacを閉じている → ノートPC前提の運用では4〜8時間止まる
  • 長期出張で1週間電源を入れない → 1週間まるごと同期不能

StartCalendarIntervalの追走実行は便利ですが、「現在動いていない時刻が長すぎると、追走しても遅延は埋まらない」のが現実です。

真の24/7が必要な領域はクラウドに移す

では、本当に24時間365日動かしたい処理はどうするか。私の答えは「該当部分はクラウドに移す」です。

  • Vercel cron(Hobbyプランで月100実行までは無料)
  • Cloudflare Workers Cron Triggers(無料枠が広く、ms単位の課金)
  • GitHub Actionsのscheduled workflows(OSS用途なら無料)

ただしクラウドに移すと、ローカルファイルへのアクセスができなくなる、シークレット管理が複雑になる、デバッグがしづらくなる、というコストが発生します。

「自動化のコスト」と「同期遅延のコスト」を天秤にかける

私が最終的に到達した運用方針は、次の2つの分業です。

  • ローカルでよい処理はlaunchd: 案件メモ、設計書、開発中のコード。Mac起動中に動けば十分
  • 24/7必要な処理はクラウド: 顧客向けの問い合わせ受付、ステータス監視、定時通知

完全自動化を追求するほど、構成が複雑になり、運用負荷が上がります。一方、頻度を諦めれば、launchdだけで多くのケースは十分回ります。

大事なのは「全部を一律に自動化しない」ことです。重要度・即時性・コストを案件ごとに見極めて、launchdとクラウドの境界を引く。これが現場で得たいちばんの教訓です。

関連記事

定期実行の仕組みをさらに比較したい方は、こちらの記事もあわせてどうぞ。

Claude Code定期実行の完全比較|/loop・cron・routinesの使い分け
Claude CodeClaude Code定期実行の完全比較|/loop・cron・routinesの使い分け

クラウド側のリモートエージェント運用を詳しく知りたい方はこちらです。

Claude Codeリモートエージェント活用法|/remote・/schedule・/loopの実践
Claude CodeClaude Codeリモートエージェント活用法|/remote・/schedule・/loopの実践

まとめ — launchd × クラウドAIは「秘書体験」を作れる

launchd 自動コミットを土台にして、クラウドAIエージェントが常に最新コードを参照できる状態を作ると、「常時稼働するAI秘書」体験は十分に実現できます。私の現在の運用では、3時間ごとの自動push + rebaseフォールバック + 失敗時通知の組み合わせで、業務時間中の同期遅延は最大3時間以内に収まっています。

すべてを自動化しようとするほど、運用は複雑になります。しかし「ここまでは自動、ここから先は人間が判断」という境界をきちんと引けば、launchdは中小規模の自動化において、いまだに最強の選択肢の1つです。Mac 自動化 git の入口として、ぜひ自分の業務に合った組み合わせを探してみてください。

← 記事一覧に戻る

御社の業務に合わせたClaude Code導入支援

「AIツールを導入したが、現場で使われない」を終わらせる。
業務課題のヒアリングから設計、ハンズオン実践、運用定着まで一貫して支援します。

無料AI活用診断を受けるサービス詳細を見る →
© 2025 Fyve Inc. All rights reserved.