複数のリポジトリを一つのエディタで開いていると、エージェントに「このリポジトリだけを触って」と伝えるのが、思いのほか難しいことに気づきます。同じ作業ディレクトリの上で二つのエージェントが動けば、片方の書き込みがもう片方の前提を崩します。私自身、個人開発で4つのサイトを並行運用しているので、この問題は毎日のものでした。
Antigravity 2.0 の projects と git worktree を組み合わせると、この「作業空間が混ざる」問題を構造で解けます。論理的な単位(project)と物理的な単位(worktree)を一致させ、エージェントごとに閉じた砂場を与える、という発想です。この記事は、その構成を実作業の手触りとともに整理したものです。
構成の全体像をひとことで
詳細に入る前に、狙いを一言で言えば「論理(project)と物理(worktree)と権限(ロック)の三つを、リポジトリ単位で揃える」ことです。
エージェントが認識する作業単位、ファイルが実際に置かれるディレクトリ、そして同時書き込みを止めるロックの範囲。この三つがずれていると、並行運用は静かに壊れます。逆に揃っていれば、エージェントを増やしても破綻しません。以降は、この三層を順に組み立てていきます。
なぜ worktree なのか
git worktree は、一つのリポジトリから複数の作業ツリーを切り出す仕組みです。ブランチごとに別ディレクトリを持てるため、main を触りながら別の作業ツリーで実験する、といったことがブランチ切り替えなしにできます。
エージェント運用では、これが効きます。ブランチを跨ぐたびにファイルが書き換わる環境では、並行で動くエージェントの足元が安定しません。worktree を切れば、各エージェントは自分専用のディレクトリだけを見ます。
# 一つのリポジトリから作業ツリーを切り出す
cd ~/repos/antigravitylab.net
git worktree add ../wt/antigravity-feature-a feature-a
git worktree add ../wt/antigravity-feature-b feature-b
# 切り出したツリーの一覧を確認
git worktree list
これで feature-a と feature-b は別々のディレクトリに存在します。片方でファイルを書いても、もう片方は無関係です。共有されるのは .git の実体だけで、作業ファイルは完全に分かれます。
projects と worktree を一致させる
Antigravity 2.0 の project は、エージェントが認識する作業の論理単位です。ここで肝心なのは、一つの project に一つの worktree を割り当て、両者を一対一に保つことです。
私はこの対応を、命名規則で固定しています。
- リポジトリ名と機能名から worktree のパスを決める(例:
wt/antigravity-feature-a)
- 同じ名前で Antigravity の project を作る
- project のルートを、その worktree のディレクトリに固定する
この三手順を崩さない限り、「どの project がどのディレクトリを触るか」が一意に決まります。逆に、一つの project から複数の worktree を見せると、エージェントは判断を迫られ、どこに書くべきか曖昧になります。一対一を守ることが、並行運用の前提です。
複数エージェントの衝突を避ける
worktree で作業ファイルは分離できますが、共有される .git を経由した衝突は残ります。代表的なのが、複数のエージェントが同時に git を叩いたときのインデックスロックです。本番運用で最も踏みやすい落とし穴であり、確実に回避しておきたい点です。
# 各 worktree でコミットを安全に行うラッパー
safe_commit() {
local wt="$1" msg="$2"
cd "$wt" || return 1
# 同一リポジトリへの並行 git 操作を直列化する簡易ロック
local lock="$(git rev-parse --git-common-dir)/agent.lock"
exec 9>"$lock"
flock 9 # 他のエージェントが終わるまで待つ
git add -A && git commit -m "$msg"
flock -u 9
}
flock で .git 共通ディレクトリ単位のロックを取り、同一リポジトリへの並行コミットを直列化しています。worktree が別でも .git は共有なので、ここを直列化しないと、稀にインデックスが壊れます。私はこれを入れる前、複数エージェントを同時に走らせた日に一度だけインデックス破損に遭い、それ以来この形に落ち着きました。
ポイントは、ロックの範囲を「リポジトリ単位」に絞ることです。全 worktree をまとめてロックすると並行性が失われます。git rev-parse --git-common-dir で共通ディレクトリを特定し、そのリポジトリに属する操作だけを直列化すれば、別リポジトリのエージェントは止まりません。
スケジュール実行と接続する
Antigravity 2.0 はバックグラウンドのスケジュール実行を持ちます。私はこれを、worktree 構成の上で回しています。各スケジュールタスクは、自分の project(=worktree)の中だけで完結し、終わったら成果をブランチに残します。
接続でつまずいたのは、worktree の後始末でした。タスクが作った一時 worktree を消さずに溜めると、git worktree list がすぐ膨れ、prune が必要な孤児が増えます。
# タスク終了時の後始末(孤児 worktree を掃除)
cleanup_worktree() {
local wt="$1"
git -C "$wt" worktree remove "$wt" --force 2>/dev/null
# 参照が外れた worktree のメタデータを掃除
git -C ~/repos/antigravitylab.net worktree prune
}
worktree remove で作業ツリーを消し、worktree prune で参照の外れたメタデータを掃除します。この二つを各タスクの終了処理に必ず入れておくと、.git/worktrees の肥大を防げます。地味ですが、毎日動く仕組みでは、この後始末の有無が一ヶ月後の状態を分けます。
並行運用で落ち着いた運用ルール
最後に、私がこの構成で落ち着いたルールをいくつか共有します。
- worktree は短命に保つ。タスクが終わったら消す。長生きさせると、ブランチの状態と作業ツリーの中身がずれていきます
- 一つの project に一つの worktree。これを崩すと、エージェントの書き込み先が曖昧になります
- 同一リポジトリへの git 操作はロックで直列化する。worktree が別でも
.git は共有である、という事実を忘れない
- 並行度を上げすぎない。私の場合、同一リポジトリに対しては同時2エージェントまでに抑えるのが、衝突とスループットのちょうどよい折り合いでした
私はこの構成を、個人開発での並行運用に推奨します。この構成の良さは、エージェントに「自由」を与えながら、その自由が他に漏れない点にあります。閉じた砂場の中でなら、エージェントは思い切り動けます。漏れない設計こそが、安心して任せられる範囲を広げてくれる——並行運用を続けて、私はそう考えるようになりました。
まずは一つのリポジトリに二つの worktree を切り、別々の作業を同時に走らせてみてください。混ざらないことの安心感が、設計の価値を一番よく教えてくれます。