移行で「便利になった」あとに来る判断
6月18日で Gemini CLI と Gemini Code Assist の IDE 拡張が個人利用向けに提供終了となり、後継の Antigravity CLI へ移ることになりました。Go で書き直された CLI はデスクトップ版と同じエージェントハーネスを共有し、コアの改善が使うすべての場所へ自動で反映されます。移行作業そのものは想像より軽く済みました。
問題は移行の後でした。Antigravity CLI ではバックグラウンドのスケジュール実行が使えるようになり、「これも任せられる」「あれも自動化できる」と手が伸びます。けれど、何でもスケジュールに載せた結果、ある朝エージェントが静かに失敗していたのに半日気づかなかった、という事故を私自身が起こしました。個人開発で複数アプリと4サイトを並行運用している私自身は、すでに Cowork で自動運用パイプラインを組んでいるので、CLI のスケジュール実行をそこへ無自覚に積み増すと、二重起動と無音失敗の温床になります。
ここで必要なのは「何を任せ、何を手元に残すか」の棲み分け設計です。
任せてよいタスクを分ける3つの判定軸
すべてのタスクをスケジュールに載せてよいわけではありません。私は次の3軸でふるいにかけています。
1. 可逆性 — 失敗しても巻き戻せるか
生成した下書きをファイルに書くだけ、レポートを出すだけ、といった可逆なタスクは任せて安全です。逆に、本番へのデプロイ、外部への送信、削除のように取り返しのつかない副作用を持つタスクは、人間が最終起動するべきです。
2. 観測可能性 — 失敗にすぐ気づけるか
結果が翌朝チャットに出る、ログが必ず残る、といった観測可能なタスクなら、無音失敗しても発見が早い。誰も見ない場所に黙って書き込むタスクは、壊れても気づけないので任せない方が無難です。
3. 締切感度 — 遅れが致命的か
「今日中でなくてもよい」タスクはスケジュール向きです。一方、特定の時刻に確実に走らないと困るタスクは、スケジューラの取りこぼしリスクを考えると、むしろ人間のトリガーや冪等な再実行とセットにすべきです。
3軸すべてが「安全側」に倒れるタスクだけを自動化する。これが私の基本線です。可逆で・観測可能で・締切に鈍感なら任せる。一つでも危険側なら手元に残す、という判断を機械的に下せるようにしています。
# delegate_decision.py — タスクをスケジュール委任してよいか判定
def can_delegate (task):
safe = (
task[ "reversible" ] # 巻き戻せる副作用のみ
and task[ "observable" ] # 結果がログ/通知に必ず出る
and not task[ "deadline_critical" ] # 時刻厳守でない
)
return "delegate" if safe else "keep-manual"
draft = { "reversible" : True , "observable" : True , "deadline_critical" : False }
deploy = { "reversible" : False , "observable" : True , "deadline_critical" : True }
print (can_delegate(draft)) # delegate
print (can_delegate(deploy)) # keep-manual
二重起動を防ぐ — CLI と既存スケジューラの排他ロック
棲み分けで最初に踏むのが、同じ仕事を CLI のスケジュールと既存の cron / Cowork タスクが二重に走らせてしまう事故です。たとえば記事生成を両方に設定してしまうと、同じ slug を二度作って衝突します。
これは排他ロックで防げます。実行の入口で簡単なロックファイルを取り、取れなければ黙って降りる設計にします。
#!/usr/bin/env bash
# run_once.sh — 同一ジョブの多重起動を防ぐ排他ラッパー
JOB_NAME = " $1 " ; shift
LOCK = "/tmp/agjob_${ JOB_NAME }.lock"
# flock でアトミックにロック。取れなければ即終了(エラーにしない)
exec 9> " $LOCK "
if ! flock -n 9 ; then
echo "[$( TZ = Asia/Tokyo date '+%F %T')] skip: ${ JOB_NAME } already running" \
>> " $HOME /agjob.log"
exit 0
fi
# ここで本体を実行
" $@ "
ポイントは、ロックが取れなかったときにエラーで落とさず exit 0 で静かに降りることです。スケジューラは取りこぼしを補うために重複起動しがちなので、「二度目は何もせず正常終了」が冪等な振る舞いになります。本番運用では、この冪等性がリトライ安全性そのものになります。
無音失敗を必ず捕まえるログ設計
スケジュール実行の最大の敵は、エラーで派手に落ちることではなく、何も言わずに失敗して気づかれないことです。Antigravity CLI のジョブも例外ではありません。私はすべてのスケジュールジョブを、結果を1行必ず残すラッパーで包んでいます。
#!/usr/bin/env bash
# logged_run.sh — 成否を必ず JST 1行ログに残す
JOB = " $1 " ; shift
LOGDIR = " $HOME /agjob_logs" ; mkdir -p " $LOGDIR "
STAMP = $(TZ = Asia/Tokyo date '+%F %T' )
if " $@ " ; then
echo "${ STAMP } OK ${ JOB }" >> " $LOGDIR /$( TZ = Asia/Tokyo date '+%F').log"
else
CODE = $?
echo "${ STAMP } FAIL(${ CODE }) ${ JOB }" >> " $LOGDIR /$( TZ = Asia/Tokyo date '+%F').log"
# 失敗時は翌朝必ず目に入る場所へも通知する
echo "${ STAMP } FAIL ${ JOB }" >> " $HOME /MORNING_ALERTS.txt"
fi
ここでも日付は必ず TZ=Asia/Tokyo で生成します。素の date は UTC で走るため、日本時間の深夜に実行したジョブが前日のログファイルへ書かれ、当日のログが空に見える——という取りこぼしを私は実際に踏みました。注意点として、ログの日付ずれは「失敗していないのに失敗したように見える」誤検知も生むので、集計する側も同じ JST 基準に揃える必要があります。
失敗が翌朝の MORNING_ALERTS.txt に必ず1行残る。この単純な仕組みがあるだけで、「気づいたら半日止まっていた」が「翌朝5分で気づく」に変わります。
スケジュールに載せる前に「降ろす条件」も決める
委任の設計でもう一つ大事なのが、載せる条件だけでなく「降ろす条件」を先に決めておくことです。スケジュールに一度載せたタスクは、放っておくと前提が変わっても走り続けます。たとえば参照先のファイル名が変わった、API のレスポンス形式が変わった、といった環境変化のあとも、ジョブは黙って空振りを続けます。
私は各ジョブに「3日連続で失敗、または7日間結果が空なら自動で休止フラグを立てる」という降板条件を持たせています。降板といっても削除ではなく、MORNING_ALERTS.txt に「このジョブを点検してください」と一行残して翌朝の判断に回すだけです。Dolice の運用では、この「自動で止めず、人間に判断を戻す」設計が、誤って必要なタスクを殺してしまう事故を防いでいます。
降ろす条件をあらかじめ決めておくと、スケジュールに載せること自体への心理的なハードルが下がります。「ダメなら必ず気づいて戻せる」という安全網があるからこそ、可逆で観測可能なタスクを安心して任せられるのです。
棲み分けの実例 — 私の振り分け
最終的に、私は次のように振り分けています。スケジュールに任せるのは、下書き生成・参照データの定期更新・整合性チェックといった、可逆で観測可能なタスク。手元に残すのは、本番 push の最終承認・価格変更・記事の削除といった、不可逆な判断を含むタスクです。
CLI のスケジュール実行は強力ですが、強力さは「壊れたときの静けさ」と裏表です。可逆性・観測可能性・締切感度の3軸でふるいにかけ、二重起動を排他ロックで止め、無音失敗をログで必ず捕まえる。この3点を移行直後に組んでおくことを、私は強くお勧めします。便利さに任せて全部を載せる前に、降ろすべきものを決めておくこと。それが長く安定して回し続けるための設計だと考えています。