壁紙アプリの配信前チェックが、三日分そろって空のログを返しておりました。失敗を示す行はありません。終了コードも 0 です。それなのに、差分レポートだけが出てきません。
原因にたどり着いたのは、ログではなく端末のほうでした。同じ処理を手元で走らせたところ、Allow access to this URL? という一行が出て、そこで待っていたのです。無人で回っているあいだ、この一行を見ている人は誰もいませんでした。
きっかけは Antigravity CLI 1.1.28(2026年9月9日)です。この版で、外部 URL の取得は事前に許可していない限り既定で承認待ちになりました。私はその前の晩、たまたまこのチェックを Antigravity CLI 側へ移したばかりでした。版の変更と自分の移設が、同じ週に重なったわけです。
最初にお伝えしたいのは、悪かったのは版でも移設でもなく、私が二つのツールを「得意分野」で分けていたことだという点です。
「賢いほうに難しい仕事」で振り分けていた頃
長いあいだ、私は二つのエージェント CLI を能力で分けておりました。設計の相談や横断的な整理は Claude Code、決まった手順の反復は Antigravity CLI、という具合です。個人開発では手が一組しかありませんので、「難しいほうを賢いほうへ」という分け方は、そのときは合理的に見えておりました。
この分け方は、画面の前に座っているあいだは破綻しません。承認を求められれば押せばよいからです。破綻するのは、同じ作業を無人で走らせ始めたときでした。
私自身、最初のうちは自分のプロンプトを疑っておりました。指示が曖昧だから途中で迷っているのだろう、と考えて手順書を細かく書き直したのです。結果は芳しくありませんでした。何度書き直しても、止まる処理は同じ場所で止まり、通る処理は一度も止まりませんでした。
違いは難易度ではありませんでした。止まった処理はどれも、リポジトリの外へ手を伸ばしていたのです。
1.1.28 では、止まる条件が二方向へ動きました
ここが直感に反したところでした。同じ 1.1.28 で、片方の承認は止まらなくなり、もう片方は止まるようになっています。Antigravity の changelog の本文から、9月9日の項目を二つ並べます。
| 変更 | 無人実行への影響 |
headless(-p)で実装計画の承認プロンプトに当たると無期限に待っていた問題を修正し、非対話モードでは計画レビューを自動で通過するようにした | 止まらなくなった。以前は計画承認が最大の停止要因でした |
| 外部 URL の取得は、事前に許可していない限り既定で承認を求めるよう変更 | 新しく止まるようになった。URL を読む処理はすべて対象です |
リリースノートの見出しだけを追っていると、この版は「headless の最適化」に見えます。実際、応答後の終了が速くなり、1ターンあたり最大 200ms の遊びも削られました。改善の項目が並ぶなかに、既定の権限が厳しくなる一行が混ざっている、という並び方をしています。
私が三日間気づけなかったのは、「止まりにくくなった」という読み方をして、止まる場所が入れ替わったことを見落としたからでした。
線は、ワークスペースの境界に引かれています
分け方を考え直そうとして changelog を遡ったところ、もっと前の版に答えが書いてありました。1.1.20 の権限まわりの改善です。既定のレビューモードではワークスペース内の読み取りを自動で許可し、読み取りや一覧のたびに承認を求めるのをやめました。ただし、変更と外部アクセスについては確認を維持します——そういう但し書きが添えられておりました。
つまり製品の側は、ずっと前から一本の線を引いていたのです。線の位置は「難しいか、易しいか」ではなく、「ワークスペースの内か、外か」でした。
私が能力で分けていたのは、この線と直交する軸でした。直交する二本の軸で仕分けをすれば、どこかで必ず食い違いが出ます。食い違いが出た場所が、たまたま無人実行だったというだけのことです。
分ける基準は難しさではなく、ワークスペースの外へ手が伸びるかどうか。 判断の軸をこう置き直してから、振り分けの迷いはほとんど消えました。
以下、その線を実務で使うための三つの問いを書き残します。見出しはこの順に並べておりますが、実際に当てるときの優先順位は後半で組み替えますので、まずは三つの形を覚えていただければと思います。
- その作業は、ワークスペースの外に触れますか
- 止まったとき、こちらは気づけますか
- やり直しの単価は、いくらですか
問い1: その作業は、ワークスペースの外に触れますか
外に触れる作業は、私の手元では三種類に分かれました。外部 URL の取得、新しい依存の取得、そして資格情報の読み出しです。どれも承認の対象になり得ますので、無人側へ置くなら事前の許可が要ります。
タスク定義を書いたあとで、外部接触の有無を機械的に洗い出すようにしております。目視では必ず見落とすからです。
#!/usr/bin/env bash
# scan-external-reach.sh — タスク定義から「ワークスペースの外」への接触を洗い出します
# 使い方: ./scan-external-reach.sh tasks/*.md
set -euo pipefail
PATTERN_URL='https?://|curl |wget |ReadURL|fetch\('
PATTERN_DEP='npm i |npm install|pip install|brew install|go get |cargo add'
PATTERN_CRED='API_KEY|_TOKEN|_SECRET|credentials|\.env'
exit_code=0
for f in "$@"; do
hits=""
grep -qE "$PATTERN_URL" "$f" && hits="${hits} url"
grep -qE "$PATTERN_DEP" "$f" && hits="${hits} dependency"
grep -qE "$PATTERN_CRED" "$f" && hits="${hits} credential"
if [ -n "$hits" ]; then
echo "OUTSIDE ${f} →${hits}"
exit_code=1 # 無人側へ置く前に、必ず人が一度見ます
else
echo "INSIDE ${f}"
fi
done
exit "$exit_code"
# 期待する出力:
# INSIDE tasks/wallpaper-derivative-check.md
# OUTSIDE tasks/theme-diff-against-production.md → url
なぜ終了コードを 1 にしているかと申しますと、この判定を無人パイプラインの手前に置きたいからです。「外に触れるタスクが混ざっている」という事実は、エラーではなく分岐の条件です。ただ、分岐として黙って処理してしまうと、私はまた三日間気づきません。ですので、いったん止めて目を通す形にしております。
実際の振り分けは、こうなりました。壁紙アプリの派生画像を作り直して重複を検査する処理は、素材もスクリプトもリポジトリの中で完結しますので、内側です。一方、受託でお預かりしている WordPress サイトのテーマを触る作業は、本番のページを取得して差分を見る工程が入りますので、外側でした。三日間止まっていたのは、もちろん後者です。
問い2: 止まったとき、こちらは気づけますか
外側の作業でも、事前に許可を渡せば無人で走ります。問題は、許可の抜けや権限の期限切れで止まったときに気づけるかどうかです。
1.1.28 では、致命的なエラーが error: という安定したマーカー付きで stderr へ出るようになりました。これは判定に使えます。ただし同じ版で、--print-timeout の超過は例外ではなく部分出力を返して正常終了する挙動に変わっています。つまり終了コードだけを見ていると、時間切れは成功として通過します。
そこで、成否の判定を三点で取るようにしました。
#!/usr/bin/env bash
# run-unattended.sh — 終了コードだけに頼らず、無人実行の成否を三点で判定します
set -uo pipefail
TASK="$1" # 例: tasks/wallpaper-derivative-check.md
EXPECT="$2" # 期待する成果物のパス
LOG="$(mktemp)"
agy -p "$(cat "$TASK")" --print-timeout 900 > "${LOG}.out" 2> "${LOG}.err"
code=$?
# ① 終了コード(1.1.28 以降、time out は 0 で返ります)
# ② stderr の error: マーカー(1.1.28 で安定化した書式)
# ③ 期待した成果物が実在するか(承認待ちで止まった場合はここだけが落ちます)
marker=$(grep -c '^error:' "${LOG}.err" || true)
produced=0; [ -s "$EXPECT" ] && produced=1
if [ "$code" -ne 0 ] || [ "$marker" -gt 0 ] || [ "$produced" -eq 0 ]; then
echo "FAILED task=${TASK} code=${code} marker=${marker} produced=${produced}"
echo "--- stderr (tail) ---"; tail -20 "${LOG}.err"
exit 1
fi
echo "OK task=${TASK}"
成果物を一つに定めにくい処理もあります。この場合は、実行の最後に一行のサマリを書き出させ、その行の有無を成果物の代わりに見るようにしております。
三点のうち、いちばん効いたのは③でした。承認待ちで止まった実行は、終了コードも error: マーカーも何ひとつ異常を出しません。成果物が無いという事実だけが、唯一の手がかりでした。無人で回す処理には、その実行が何を作るはずだったのかを必ず書き添えるようにしています。
--print-timeout の挙動変更そのものについては、Antigravity CLI 1.1.28 で print-timeout 超過が成功終了になりました に判定の見直し方をまとめております。あわせてご覧いただければと思います。
問い3: やり直しの単価は、いくらですか
三つめは、失敗したときに戻すのがどれだけ高くつくか、という問いです。
配信前の派生画像は、作り直せば済みます。壊れても失うのは計算時間だけです。一方、お預かりしているサイトのテーマは、公開中のページがそのまま読者の目に触れます。戻す作業には、私だけでなく先方の時間も乗ります。
本番運用に入っているサイトでは、事故を回避する手立てを先に置くほうが、起きてから対処するより安く済みます。
単価の高い作業は、たとえ手順が単純でも、承認を挟む側に置いております。ここだけは効率で判断しないようにしているのです。エージェントが複数のリポジトリを触る構成では、作業ルートそのものを取り違える危険も残りますので、エージェントに複数リポジトリを触らせる前に置く、作業ルートの番人 に書いた入口の検査も併用しています。
三つの問いを一枚にすると、こうなりました。
| 問い | 答えが「はい」のとき | 預け先 |
| ワークスペースの外に触れるか | 事前許可を渡すか、対話側へ寄せる | Claude Code(隣に座る側) |
| 止まったときに気づけるか | 成果物チェックを付けたうえで無人へ | Antigravity CLI(無人側) |
| やり直しの単価が高いか | 手順が単純でも承認を挟む | Claude Code(隣に座る側) |
三つの問いを、一つのディスパッチャに固定します
頭の中の基準は、翌週には揺らぎます。私は判断をスクリプトに移しました。
#!/usr/bin/env bash
# dispatch.sh — タスクの性質から預け先を決め、そのまま実行まで行います
# tasks.tsv 形式: <task_file>\t<expected_artifact>\t<redo_cost:low|high>
set -uo pipefail
while IFS=$'\t' read -r task expect cost; do
[ -z "${task:-}" ] && continue
case "$task" in \#*) continue ;; esac
outside=0
./scan-external-reach.sh "$task" > /dev/null 2>&1 || outside=1
if [ "$cost" = "high" ]; then
route="interactive" # 問い3 が最優先です
elif [ "$outside" -eq 1 ] && [ ! -f ".agy/pre-granted-urls" ]; then
route="interactive" # 問い1: 事前許可が無い外部接触
else
route="unattended" # 問い2 の三点判定つきで無人へ
fi
case "$route" in
unattended) ./run-unattended.sh "$task" "$expect" ;;
interactive) echo "QUEUED(interactive) ${task}" >> queue/interactive.txt ;;
esac
done < tasks.tsv
# 期待する出力:
# OK task=tasks/wallpaper-derivative-check.md
# QUEUED(interactive) tasks/theme-diff-against-production.md
順番には意味があります。やり直しの単価を最初に見ているのは、外部接触が無くても手元に残したい作業があるからです。逆にしてしまうと、安全に見える単純な作業がすべて無人側へ流れます。三つの問いは並列ではなく、単価・境界・可観測性の順に効きます。
.agy/pre-granted-urls の有無を見ているのは、事前許可の設定をリポジトリの中で管理しているからです。ファイルが無いのに外へ取りに行くタスクは、承認待ちで止まる候補そのものですので、実行の前に対話側の列へ回します。
半月ほど回してみて、変わったことと変わらなかったこと
変わったのは、黙って止まる実行が出なくなったことです。止まるときは対話側の列に積まれますので、朝いちばんに列を見れば分かります。三日分の空ログを眺めることは、あれ以来ありません。
変わらなかったこともあります。判断の重い作業は、結局どちらのツールに置いても私が見ることになりました。二つのツールを使い分けても、決めるのは自分だという部分は減らないのです。むしろ、そこが減らないと分かってから、振り分けを機械に任せる気持ちになれました。
例外として、常駐させたものが一つあります。手元の機械が寝ているあいだも動いていてほしい監視だけは、remote-control でサービスとして登録しました(1.2.0・2026年9月10日でサブコマンドが入りました)。常駐の生存条件と、必要なときに遮断する経路の考え方は Remote Control をホスト機に入れる判断 に整理しております。
明日、最初に確かめる一つのこと
無人で回している処理を一つ選び、agy -p を手元の端末で同じ引数のまま走らせてみてください。承認を求める一行が出るなら、それは無人側では毎回そこで待っております。
私はこの確認を、版を上げた翌朝の習慣にしました。止まる場所は、これからも版とともに動くのだと思います。
最後まで読んでくださり、ありがとうございました。同じところで三日を失う方が一人でも減れば嬉しく思います。