エージェントを3体並行で走らせ、それぞれが別の課題を片付けてくれた。ここまでは快適です。問題はその次に来ます。3つのブランチをどの順で、何を確認しながら本筋へ取り込むのか。ここを場当たりでやると、個々のエージェントが正しくても、統合した瞬間に壊れます。
Antigravity 2.0 で複数エージェントの並行実行が現実的になりましたが、成果物をどう束ねるかは利用者側の設計に委ねられています。私自身、個人開発で複数サイトの改修を並行エージェントに任せ始めたとき、最初に痛い目を見たのが、まさにこの統合の工程でした。各々は通っているのに、まとめると CI が落ちる。原因を一つずつ手で追う羽目になりました。
その反省から、統合の前に必ず通す「差分レビューゲート」を組みました。並行作業で生じる衝突を、統合の前に機械的に炙り出す仕組みです。
並行で起きる3つの衝突を先に知る
統合を設計する前に、並行作業で何が衝突するのかを把握しておくと、ゲートの形が決まります。私が繰り返し踏んだのは次の3つです。
衝突の種類 典型的な症状 検出の起点
重複編集 同じファイルを2体が別方針で変更 変更ファイルの集合の積
相互依存 片方が消した関数を他方が呼ぶ シンボルの追加・削除の突合
テスト退行 単体では通るが統合で落ちる 統合ブランチでの一括テスト
重要なのは、これら3つが個々のブランチを見ているだけでは見えないことです。どれも複数の差分を突き合わせて初めて表面化します。だからレビューゲートは、ブランチ単位ではなく差分の関係を見る設計にします。
第1ゲート: 重複編集を機械的に弾く
最初のゲートは、同じファイルを複数のエージェントが触っていないかの確認です。重複があった場合、自動マージは諦めて人手に回します。
#!/usr/bin/env bash
# overlap-gate.sh base branch1 branch2 ...
# 複数ブランチが共通で変更したファイルを検出する
set -euo pipefail
base = " $1 " ; shift
declare -A counts
for br in " $@ " ; do
for f in $( git diff --name-only " $base .. $br " ); do
counts[ " $f " ] = $(( ${counts[ " $f " ] :- 0} + 1 ))
done
done
overlap = 0
for f in "${ ! counts [ @ ]}" ; do
if [ "${ counts [ $f ]}" -ge 2 ]; then
echo "⚠️ 重複編集: $f (${ counts [ $f ]} ブランチが変更)"
overlap = 1
fi
done
[ " $overlap " -eq 0 ] && echo "✅ 重複なし" || echo "→ 重複ファイルは人手レビューへ回す"
重複が見つかったファイルは、どちらの変更を採るかを人間が決める対象です。私は重複が1件でもあれば、そのファイル群だけは自動統合の対象から外すことを推奨します。機械が善意で両方をマージすると、矛盾した状態が残りやすいからです。
第2ゲート: シンボルの相互依存を突き合わせる
重複がなくても、片方が削除した関数を他方が呼んでいれば、統合後にビルドが壊れます。これは変更ファイルが別々でも起きるので、ファイル名の比較では捕まりません。シンボルの増減を突き合わせます。
# あるブランチで削除されたトップレベル関数名を抽出
git diff " $base .. $branchA " \
| grep '^-' | grep -oE 'function [a-zA-Z0-9_]+' \
| awk '{print $2}' | sort -u > /tmp/removed_syms.txt
# 別ブランチで新たに呼ばれている箇所と突合
while read -r sym ; do
if git diff " $base .. $branchB " | grep -q "^+.*\b${ sym }\b" ; then
echo "🔴 依存衝突: $branchA が削除した ${ sym } を $branchB が使用"
fi
done < /tmp/removed_syms.txt
この突合で「片方の削除」と「他方の追加利用」が交差する箇所が出たら、統合順を工夫しても解決しません。どちらかのエージェントに再作業を依頼するのが本筋です。私の経験では、相互依存の衝突は全体の2割ほどでしたが、見逃すと統合後のデバッグが最も長引く厄介な種類でした。
第3ゲート: 統合ブランチで一括テスト
最後に、ここまでのゲートを通った差分を一時的な統合ブランチへ集め、そこでテストを一括で走らせます。単体で通ることと、まとめて通ることは別問題だからです。
# 一時統合ブランチを作り、全ブランチを順に取り込んでテスト
git switch -c integration/tmp " $base "
for br in " $@ " ; do
git merge --no-ff --no-edit " $br " || { echo "🔴 マージ衝突: $br " ; exit 1 ; }
done
npm test || { echo "🔴 統合テスト退行" ; exit 1 ; }
echo "✅ 統合ブランチが通過"
統合ブランチはあくまで検証用の使い捨てです。ここでテストが通って初めて、本筋への取り込みを判断します。落とし穴として、この統合ブランチをそのまま本筋にしてしまうと、検証の過程で生じた中間コミットが履歴に混ざります。私は統合ブランチでの結果を確認したうえで、本筋へは改めてクリーンに取り込み直すようにしています。
規模でレビュー経路を振り分ける
3つのゲートをすべて人手で見るのは現実的ではありません。差分の規模で、自動で進めてよいものと人手で見るべきものを振り分けます。
# 変更行数でレビュー経路を判定する
lines = $( git diff --shortstat " $base .. $br " \
| grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' )
if [ "${ lines :- 0 }" -le 50 ]; then
echo " $br : 小規模(自動マージ候補)"
else
echo " $br : 大規模(人手レビュー必須)"
fi
私のしきい値は変更50行です。これ未満で3ゲートを通ったものは自動マージの候補に乗せ、超えるものは必ず差分を読みます。この振り分けで、本当に目を通すべき差分にだけ時間を使えるようになりました。経験上、人手レビューを要する差分は全体の3割ほどに収まります。残りの7割を自動に回せると、並行実行の旨味がようやく手元に残ります。
統合の質が並行の価値を決める
並行でエージェントを走らせること自体は、もう難しくありません。価値の差がつくのは、出てきた成果をどう束ねるかの設計です。重複・相互依存・テスト退行という3つの衝突を、統合の前に機械的に弾くゲートを持っているかどうかで、並行実行が時短になるか、デバッグの山を生むかが分かれます。
Dolice Labs で複数サイトの改修を並行で回すようになって、私が一番手を入れたのは、賢いプロンプトよりも、この地味な統合ゲートのほうでした。並行の速さを台無しにしないために、まずは第1ゲートの重複検出から導入してみていただければと思います。