Claude Codeにバグを1つ、Codexに小さな機能を1つ任せてその場を離れ、戻ってきたらファイルの様子がおかしい。片方のエージェントの編集が消えている。あるいは両方の編集は残っているのにビルドが失敗する。もっと厄介なのは、すべてコンパイルは通るのに、どちらのエージェントも触っていない場所でテストが落ちるケースだ。留守中に何もクラッシュはしなかった。2つのエージェントはどちらも頼んだ作業をやり遂げた。ただ、1つのファイルが2人の書き手を同時に抱え込めず、どちらが勝ったのか誰も教えてくれなかっただけだ。
本当の問題は、この沈黙そのものにある。ファイルはマージツールではない。2つのコーディングエージェントが同じディレクトリのauth.tsに時間的に重なる形で書き込むとき、ロックもキューも警告も存在しない。あるのは「最後に保存したプロセスが勝つ」という現実だけだ。この記事では、この衝突のプロセスを段階ごとにたどり、各段階で起きている失敗の正体に名前を付け、次に並行セッションを始める前に実行できるチェックリストで締めくくる。
さらに、こうした対策がたどり着く先のワークフローについても触れる。Sharklyは、あなたがすでに使っているコーディングエージェントの上に位置する、オールインワンのエージェント命令・管理プラットフォームだ。各Taskに専用の分離されたworktreeを割り当て、コンテキスト、進捗、ブロッカー、結果、そして人間によるレビューを、リクエストからリリースまで一貫して可視化する。同じ規律は素のGitだけでも手作業で組み立てられるし、この記事の大半はその方法を紹介している。最後のセクションでは、手作業での運用が割に合わなくなるタイミングについて述べる。
ロックは存在しない——最後に書いた者が勝つ
まずは最悪のパターンから見ていこう。これが最もよく起きるうえ、最も見えにくい。2つのエージェントが同じ作業ディレクトリで動いている。エージェントAがauth.tsを読み、変更を計画し、書き込みを始める。その1秒後、エージェントBが同じauth.tsを読み、自分が読んだバージョンに対して別の変更を計画し、これも書き込む。
どちらも相手の編集を読んでいない。なぜなら、読み込んだ時点でその編集はまだ保存されていなかったからだ。後から保存したほうが、最初のエージェントの作業を一切含まないバージョンでディスク上のファイルを上書きする。これがラストライト・ウィンズ(最後に書いた者が勝つ)であり、しかも沈黙のうちに起きる。コンフリクトマーカーも、エラーも、終了コードも出ない。エージェントAは成功を報告する。エージェントBも成功を報告する。そのうち片方はあなたに嘘をついているが、本人もそれに気づいていない。
ロックファイルの競合も、より高くつく場所で起きる同じ失敗だ。エージェントAがnpm installを実行してpackage-lock.jsonを書き換え、そのインストールの途中でエージェントBが別の開始状態からまた書き換える。その結果、どちらの依存関係ツリーとも一致しないロックファイルができあがり、数日後に誰か別のマシンで破綻が表面化する。共有のビルド出力、単一の開発サーバーポート、グローバルキャッシュも、すべて同じ形で失敗する。
解決策は、より賢いエージェントではない。各エージェントに自分専用の部屋を与えることだ。
エージェントごとに1つの作業コピー——沈黙の上書きが目に見えるコンフリクトになる
Gitのworktreeとは、1つのリポジトリに対する2つ目のチェックアウトコピーであり、独自のブランチを持ち、単一の.gitオブジェクトストアを共有する。各エージェントは、他人の作業途中の変更を見ることなく、編集・ビルド・テストを行える本物のディレクトリを手に入れる。
# From the main repo
git worktree add ../repo-agent-a -b agent/auth-refactor
git worktree add ../repo-agent-b -b agent/session-timeout
エージェントAは../repo-agent-aに、エージェントBは../repo-agent-bに向ければいい。これでAによるauth.tsへの未保存の編集はBからは見えず、どちらのエージェントも相手のファイルをディスク上で上書きできない。なぜなら、2人は別々のブランチ上の別々のファイルを編集しているからだ。worktreeモデルについては、Git worktrees for AI coding agentsとcoding agent worktrees: one checkout per taskでより詳しく扱っている。
ここで分離が何をしたかに注目してほしい。2つのエージェントが同じファイルを編集することを防いだわけではない。沈黙のデータ損失を、ブランチが再び統合される瞬間に目に見えるマージコンフリクトへと変えたのだ。これは実質的なグレードアップである。Gitは処理を止め、食い違いのある正確な行を提示してくれるので、両方のバージョンを完全に見ながら解決できる。目に見えるコンフリクトは、直せる問題だ。目に見えなかった上書きこそが、出荷されてしまった問題だった。
はっきり述べておくべき境界が1つある。worktreeが分離するのはファイルであって、副作用ではない。共有データベース、単一のポート、ハードコードされた出力パスは、worktreeをまたいでも依然として衝突する。これらも分離するか、それらを必要とするエージェントを直列化しよう。
目に見えないコンフリクト
ここからは、分離しても生き残る失敗パターンだ。自分のworktreeで作業するエージェントAが、getUser()をfetchUser()にリネームし、自分が持つすべての呼び出し箇所を更新する。同時に自分のworktreeで作業するエージェントBは、getUser()の呼び出し箇所を3つ新たに書く。Bがファイルを読んだとき、関数名はそうなっていたからだ。
両方のブランチはコンフリクトマーカーが一切出ずにマージされる。Gitは行単位で比較し、行ごとに見れば重なりはなかった。Aは自分のファイルを変更し、Bは新しいファイルを追加した。マージはクリーンだ。ところがその後、未定義のgetUserでビルドが壊れる。あるいはコンパイルは通り、型チェッカーが到達できなかった実行時の場所で失敗する。テキスト上は衝突がなかったので、どのツールもこれを指摘しなかった。2つの編集が食い違っていたのは、ファイルのテキストではなく、その意味だったのだ。
これが、「エージェントを分離しておけばいい」だけでは答えにならない理由である。分離は同じファイルの衝突を同じインターフェースの衝突へと先送りするだけで、2つ目の種類のほうが1つ目よりも巧みに隠れる。ここで効くレバーはツールではなく、タスクのスコープ設計だ。役立つルールを1つ挙げる。2つのエージェントのタスクを、同じファイル名や同じ関数名を口にせずに説明できないなら、それは2つではなく1つのタスクである。モジュール境界に沿って作業を分割することが、Claude CodeとCodexを1つのコードベースで一緒に動かすことがデバッグの午後と化すのを防ぐ、ほぼすべてだ。2つのタスクが本当にインターフェースを共有するなら、それらを直列化するか、各ステップの間にマージとリベースを行って、2番目のエージェントが1番目のエージェントの現実を読むようにしよう。
手作り運用がスケールしなくなる地点と、タスクボードが加えるもの
2つのエージェントと1つの共有ファイルなら、手作業で管理できる。worktreeを2つ、慎重なスコープ設計、見届けるマージが1つ。この方法は、数え上げが難しくなるまで通用する。どのエージェントがどのブランチにいるのか。どのworktreeがどのタスクに対応するのか。5つのクリーンなマージのうち、どれが共有インターフェースを静かに変えてしまったのか。どのブランチがレビュー待ちで、どれをもう見たのか。Gitのコマンド自体は決して難しくなっていない。難しくなったのはその周りの帳簿付けであり、まさにそこで同じファイルの衝突がすり抜ける。十分に読んでいないマージを承認してしまうからだ。
タスクボードは、追跡する対象そのものを変える。ターミナルの壁と切り離されたworktreeの代わりに、作業の各単位が、オーナーとブランチと状態を持つ1つのTaskになる。Sharklyでは、Taskが自動的に専用の分離されたworktreeを手に入れるので、ファイルレベルの分離は「実行し忘れないようにするコマンド」ではなくデフォルトになる。実行、ブロッカー、結果はTaskに返ってくるので、diffから意図を逆算するのではなく、宣言されたスコープに照らして変更をレビューできる。そして役割分担は固定されたままだ。エージェントは調査し、実行し、テストし、報告する。人間は方向性を定め、権限を与え、結果を受け入れる。自動化が止まるのは、チームの判断が必要とされる地点である。
この最後の点こそ、分離だけでは決して解決できないものだ。優れたツールは分離とマージを容易にするが、レビューはエージェントの数には比例しない。比例するのはあなたの注意力であり、人間によるレビューこそが並行エージェント作業における真のボトルネックである。あなたが1つを丁寧に読む間に、5つのエージェントは5つのブランチを生み出す。タスクボードはコードを読んでくれるわけではない。ただ、キューを正直に保ち、どの山にあったかを見失ったせいで、もっともらしく見える編集が流し読みされてマージされるのを防いでくれる。同じコンフリクトの理屈はリポジトリレベルでも現れ、それは同じリポジトリで複数のコーディングエージェントを動かすで扱っている。
これをworktreeと規律で組み立ててもいいし、Sharklyでワークフローを管理してもいい。すでに複数のエージェントを動かしているなら、価値があるのは、タスクと結果がターミナルに散らばるのではなく1か所に存在する状態だ。
次の並行セッションの前に実行するチェックリスト
2つのエージェントに何かを向ける前に、これを実行しよう。
- 各エージェントに専用のworktreeを与える。2つのエージェントを1つの作業ディレクトリに入れてはいけない。タスクごとに
git worktree add ../repo-<task> -b agent/<task>を実行する。parallel agents in isolated worktreesも参照。 - タスクのスコープを、同じファイル名や関数名を口にしないように設計する。どうしても必要ななら、それは1つのタスクである。直列化しよう。
- ファイルだけでなく副作用も分離する。データベース、ポート、出力パス、キャッシュを分けるか、それらを共有するエージェントを直列化しよう。
- マージの前にリベースする。2つのブランチが同じ開始コミットに対して動いていた場合、2番目のエージェントの作業が1番目の上に乗るようにする。
- マージのたびに型チェックと全テストスイートを実行する。最後の1回だけではいけない。意味上のコンフリクトはクリーンなマージを通過し、ここで失敗する。
- 各ブランチを、そのタスクのスコープに照らして1つずつレビューする。読んでいない緑のチェックマークの山こそ、目に見えないコンフリクトが出荷される場所だ。
2つのエージェントが1つのファイルを編集するのは、まれなエッジケースではない。タスクがほんの少しでも重なった瞬間に、並行作業がそう見えるだけのことだ。衝突を前提に計画し、それを目に見える形にし、何をマージするかの判断は1人が下し続けよう。



