Parallel coding agents and git worktree
Several coding agents over one repository: why a shared directory is a wreck, a worktree and branch per stream, a queue with a concurrency limit, and safe merges.
Lesson content is currently available in Russian.
What you will learn
- →Объяснять, какие именно гонки возникают у двух агентов в одном рабочем каталоге.
- →Разворачивать и убирать worktree с веткой на каждый поток работы.
- →Проектировать оркестратор кодинг-агентов: очередь, лимит параллельности, verify-gate и мёрж.
Почему общий каталог — авария
Кодинг-агент — не chat-бот: он материализует работу в файловой системе. Он читает файлы, правит их, запускает тесты и коммитит. Пустите двух таких агентов в один рабочий каталог — и вы получите все классические гонки конкурентного доступа, только с LLM в роли обоих гонщиков.
Сценарий первый: перекрёстное чтение недописанного. Агент А переписывает модуль авторизации; агент Б в этот момент читает тот же файл, видит половину рефакторинга и строит свою правку на состоянии, которого через минуту не будет. Итог — код, который не собирается, причём каждый агент по отдельности «прав». Сценарий второй: захват чужого WIP в коммит. Агент А делает git add . и коммитит — в коммит уезжают недоделанные файлы агента Б; история превращается в фарш, а откат одной задачи откатывает половину другой. Сценарий третий: гонка на индексе и ветках — два одновременных git add/git commit/git checkout в одном каталоге дерутся за index.lock, а переключение ветки одним агентом молча меняет файлы под ногами у второго.
Заметьте: ни одна из этих аварий не лечится «промптом получше». Это свойства общего изменяемого ресурса, и решение то же, что в многопоточном программировании, — изоляция. Каждому потоку работы нужен собственный каталог и собственная ветка, а точка слияния должна быть одна и управляемая. Ровно это и даёт git worktree.
Worktree и ветка на поток
git worktree создаёт дополнительный рабочий каталог, подключённый к тому же репозиторию: общая база объектов и рефов, но свой checkout, свой индекс и своя ветка. Это дешевле полного клона (объекты не дублируются) и главное — правильнее: ветки и коммиты видны из всех worktree сразу.
# каталог и ветка под каждый поток работы
git worktree add ../wt/auth-api -b feat/auth-api HEAD
git worktree add ../wt/auth-ui -b feat/auth-ui HEAD
git worktree list
# /repo abc1234 [dev]
# /wt/auth-api abc1234 [feat/auth-api]
# /wt/auth-ui abc1234 [feat/auth-ui]
# после вливания или отказа — обязательная уборка
git worktree remove --force ../wt/auth-api
git worktree prune
Два правила, которые git защищает сам. Первое: одна ветка — один worktree; попытка сделать checkout ветки, уже занятой другим worktree, завершится ошибкой — это и есть страховка от двух агентов на одной ветке. Второе: worktree убирают командой git worktree remove, а не rm -rf; после ручного удаления каталога остаются висячие записи, которые чистит git worktree prune.
Конвенции, которые окупаются при десятках прогонов: единый корень для всех worktree (например, ~/worktrees/<repo>/ вне рабочих каталогов), имя каталога и ветки из слага задачи плюс таймстемп, и жёсткая привычка убирать worktree сразу после мёржа. Забытые worktree держат ветки занятыми и накапливаются в мусор, который потом страшно чистить.
Оркестратор поверх worktree
Изоляция каталогов решает гонки файловой системы, но не отвечает на вопросы планирования: сколько агентов пускать одновременно, кому какую задачу и что делать при отказе. Это работа оркестратора, и у него три обязательных механизма.
Первый — очередь с лимитом параллельности. Кодинг-агент — тяжёлый процесс: он гоняет typecheck, тесты и сборку, каждый экземпляр ест ядра и память. Практический потолок на рабочей машине — 2–4 одновременных агента; дальше прогоны начинают мешать друг другу через CPU, тесты флапают по таймаутам, и «параллельное» выполнение становится медленнее последовательного. Задачи сверх лимита ждут в очереди.
Второй — раскрой задач по непересекающимся областям файлов. Worktree изолирует процесс работы, но не смысл: если две задачи правят один модуль, конфликт никуда не делся — он просто отложен до мёржа. Оркестратор должен резать фронт работ так, чтобы области почти не пересекались (фронт/бэк, разные пакеты, разные фичи), а заведомо конфликтующие задачи ставить последовательно.
Третий — бюджет и таймаут на задачу. Каждому прогону — потолок в токенах, времени и итерациях; агент, упёршийся в потолок, останавливается и возвращает частичный результат с описанием затыка, а не крутится всю ночь. Отчёт агента обязан включать команду проверки и её вывод: «сделано» без зелёного прогона тестов — это не сделано, это гипотеза.
Сбор результата: verify-gate и мёрж
Финал параллельного прогона — свести N веток обратно в dev, не сломав его. Здесь дисциплина важнее ума, и порядок фиксированный.
Шаг первый: verify-gate внутри worktree. До всякого мёржа в каталоге задачи прогоняются typecheck, линт и тесты; ветка, не прошедшая ворота, в очередь мёржа не попадает — она возвращается агенту на доработку или человеку на разбор. Ворота должны быть механическими: скрипт с exit-кодом, а не «агент сказал, что всё хорошо».
Шаг второй: коммит только своих файлов, поимённо. git add . в агентном конвейере запрещён как класс: даже в изолированном worktree агент мог насорить временными файлами, а привычка тащить всё подряд рано или поздно захватит чужое. Коммит-сообщение — по Conventional Commits, со слагом задачи.
Шаг третий: последовательный мёрж. Ветки вливаются в dev по одной; после каждого мёржа dev-прогон тестов повторяется, потому что две ветки, зелёные порознь, вместе могут быть красными — классика семантических конфликтов: одна переименовала функцию, вторая добавила её вызов по старому имени. Если ветка устарела относительно dev, её сначала перебазируют и прогоняют ворота заново.
Шаг четвёртый: ревью-агент на диффе. Отдельный агент (или человек) читает git diff dev...feat/x перед мёржем — свежий контекст без истории прогона находит то, что автор-агент уже «не видит». И финальный аккорд: worktree удалён, ветка удалена, git worktree list чист. Уборка — часть definition of done.
Practice
Два агента параллельно в worktree
Возьмите учебный репозиторий с тестами и запустите двух кодинг-агентов (Claude Code, Codex CLI или аналог) на двух непересекающихся задачах — каждого в своём worktree со своей веткой. Прогоните verify-gate в обоих каталогах, влейте ветки в dev последовательно с повторным прогоном тестов после каждого мёржа, уберите worktree.
Deliverable
История команд: создание worktree, вывод git worktree list, зелёные прогоны тестов в обеих ветках и в dev после каждого мёржа, чистый git worktree list в конце.
Checkpoint
Коллега предлагает запустить трёх кодинг-агентов в общем каталоге, «потому что worktree — лишняя возня». Разубедите его конкретными сценариями аварий.
- ✓ Названы минимум две конкретные гонки: чтение недописанного, захват чужого WIP через git add ., драка за индекс/ветки.
- ✓ Объяснено, что worktree дёшев (общие объекты) и что git сам запрещает одну ветку в двух worktree.
- ✓ Упомянут verify-gate и последовательный мёрж с повторным прогоном тестов.
Check yourself
Sources