2014年から個人で iOS/Android アプリを開発し、Dolice Labs として 4 サイトを並行運用している廣川政樹です。累計 5,000 万ダウンロードを支えてきた壁紙・癒し系アプリ群のリファクタを Antigravity に移してから、地味ながら作業効率を一番奪っていたのが「エージェントが書いてくれたはずの差分が、いつのまにか一部だけ消えている」現象でした。原因はオートセーブとエージェント編集の競合で、設定を 3 か所変えるだけで止まります。具体的な再現条件と修正手順を順にまとめます。
どんな症状か
エージェントに「この関数を分割して、テストも書いて」と頼んだあと、提案された差分のうち本体ファイルだけが正しく書き換わり、新規追加されたテストファイルが空のまま保存される、あるいは新規ファイルすら作られていない、というケースです。Apply ボタンを押した直後はエディタに反映されているように見えるのに、ファイルツリーを更新したり別タブに移動して戻ると、修正が一部だけ巻き戻っているのが厄介な点です。
私の壁紙アプリのリファクタ作業では、SettingsViewController.swift を 3 ファイルに分割するタスクを 1 日に 4 回試して、4 回とも分割後の片方のファイルだけが「保存されているのに空」という状態になり、半日ほど原因を取り違えていました。
再現条件
下記の 3 つが揃ったときに発生しやすいことを確認しています。
Files: Auto SaveがafterDelay(またはonFocusChange/onWindowChange)になっている- エージェントが同一トランザクション内で「既存ファイル編集」と「新規ファイル作成」を同時に行う
- エディタの未保存タブ(ドット印付き)が 3 つ以上開いている
つまり、エディタ側が「未保存変更を一定時間後に勝手にディスクへ書き戻そう」とする動きと、エージェント側が「新規作成したファイルに連続でテキストをストリーミング書き込む」動きが、同じファイルディスクリプタを取り合うタイミングで衝突します。エディタが先に空の状態でファイルを write(0 bytes) してしまうと、エージェントは「もう存在する既存ファイル」と判断して上書きを諦めるルートに分岐するため、結果としてファイルが空のまま残ります。
原因
Antigravity のエージェントが行う編集は、内部的には「対象ファイルがダーティ(未保存状態)かどうか」を見て分岐します。具体的には次の 2 経路です。
- ダーティでない場合: 仮想ファイルシステム経由で直接ディスクへ書き込み、その後エディタへリロードを通知
- ダーティな場合: エディタの WorkspaceEdit API 経由でテキストバッファを差し替え、保存はユーザー任せ
オートセーブが有効だと、エージェントが新規ファイルを作った瞬間に VS Code 側が afterDelay カウンタを開始します。エージェントがまだストリーミングで本文を書いている最中に afterDelay が満了すると、エディタが空文字列を書き戻して「ダーティではない」状態にしてしまう。次のチャンクが届いたエージェントは前述の判断分岐で「既にディスク上にある」と認識し、衝突回避のため書き込みをスキップする、という挙動になります。
解決策(推奨順)
1. オートセーブを off にする(最も確実)
.vscode/settings.json をワークスペース直下に置き、下記を追加します。
{
"files.autoSave": "off",
"files.autoSaveDelay": 5000,
"files.autoSaveWhenNoErrors": false,
"editor.formatOnSave": false
}formatOnSave も合わせて切るのは、保存トリガーで Prettier 等が走ると、エージェントがストリーミング中のコードを「未完成」と判断したフォーマッタが半端な整形を残し、次のチャンクと競合するためです。プロジェクト単位で切れるので、Antigravity を使うリポジトリだけ無効化すれば、他のエディタでの作業には影響しません。
2. エージェント編集中だけ自動的に保存を抑制する
オートセーブを完全に切りたくない場合は、エージェントが動いている間だけ無効化するスクリプトを tasks.json に登録します。
{
"version": "2.0.0",
"tasks": [
{
"label": "agent-mode-on",
"type": "shell",
"command": "code --command 'workbench.action.toggleAutoSave'",
"presentation": { "reveal": "never" }
}
]
}これをキーボードショートカット(私は Cmd+Shift+A に割り当てています)から呼べるようにしておくと、Apply を押す直前にトグルして、エージェントが完了したらもう一度トグルするだけで済みます。完全自動化したくなりますが、手で切り替える方が「いま編集中なのは自分かエージェントか」を意識できて、結果的にミスが減りました。
3. 新規ファイル作成と既存ファイル編集を分けて依頼する
エージェントへのプロンプト側で対処する方法です。「まず XXXView.swift を新規作成してください。中身を確認してから、続けて SettingsViewController.swift を編集する指示を出します」のように 2 段階に分けると、未保存タブが同時に増えないため衝突が起きません。Pull Request を小さく刻むのと同じ発想で、エージェント編集も「1 トランザクションあたり 2 ファイルまで」に抑える運用が安定します。
予防策
長期的には下記の運用に落ち着きました。
- Antigravity を使うすべてのリポジトリの
.vscode/settings.jsonでfiles.autoSave: offを明示する(dotfiles 化して 4 プロジェクト共通化) - エージェント編集の前には未保存タブをすべて閉じる(
Cmd+K Wで一括クローズ) - エージェント完了後は
git statusを必ず目視確認し、想定したファイルがすべて変更ファイル一覧に並んでいるかをチェックする
特に 3 番目を「Apply 押下 → git status 確認」をワンセットの儀式にしてから、空ファイル問題に気づかず数時間進めてしまう事故がゼロになりました。
編集後に「空ファイル化」を検知する小さなスクリプト
Apply 押下後に目視確認するのが基本ですが、私のように 1 日 4 本の記事自動投稿パイプラインを並行で回していると、確認漏れが必ず起きます。そこで pre-commit フックに「直近変更ファイルのうち拡張子つきの空ファイルがあれば commit を止める」スクリプトを置きました。
#!/usr/bin/env bash
# .git/hooks/pre-commit
set -e
empty_files=$(git diff --cached --name-only --diff-filter=AM \
| xargs -I{} sh -c 'test -f "{}" && [ ! -s "{}" ] && echo "{}"' 2>/dev/null)
if [ -n "$empty_files" ]; then
echo "🛑 Empty file(s) staged — likely autosave/agent diff conflict:"
echo "$empty_files" | sed 's/^/ - /'
echo ""
echo "Restore from agent panel, or 'git restore --staged' and re-apply."
exit 1
fiこのフックを入れてから「空ファイルが本番にデプロイされる」事故はゼロになりました。Antigravity 側の Apply 履歴は数時間で揮発するので、フックで気づかないと再生成する以外の手段がなくなります。
似た症状で原因が違うパターン
オートセーブが原因ではないけれど、見た目が似ている症状もあります。区別の目安をまとめておきます。
- 新規ファイルが空ではなく「ファイル自体が存在しない」: エージェントが
Apply前にチャットを閉じられ、トランザクションが破棄されたケース。チャットセッションを開き直して「先ほどの差分をもう一度適用してください」と頼むと再生成される - 既存ファイルが空になる:
formatOnSaveか外部フォーマッタが「壊れたコード」と判断して全削除した可能性。git reflogではなくファイル単位でgit restore --source=HEAD~1 path/to/fileで戻す - 差分の半分だけ反映される: エディタに紐づく言語サーバ(TypeScript Server 等)がエージェント編集中に再起動した可能性。
Cmd+Shift+P→TypeScript: Restart TS Serverを実行した直後にエージェント編集を始めないこと
それぞれ対処が違うので、空ファイルかどうかを最初に切り分けると判断が早くなります。
それでも再現する場合
上記を全部やっても起きる場合は、別の競合源を疑います。私が遭遇したケースでは、Xcode の swiftformat のファイル監視デーモンが裏で動いており、エージェント書き込み直後にフォーマット → 保存を走らせていました。launchctl list | grep -i swift で常駐プロセスを洗い出し、エージェント編集中だけ停止するシェル関数を ~/.zshrc に書いて凌いでいます。エディタ側のオートセーブを切ってもファイルが空になる場合、エディタの外にもう一つ「保存しに来る誰か」が居ないか確認してみてください。