ADR 0165 — Репозиторий рождается при принятии handoff Arquiteto, и исполнение без него не начинается
Статус
Accepted. Решено сопровождающим 2026-09-18 (AT-092). Пересматривает
ТРИГГЕР RN-522, которая родилась, чтобы
выполнить обещание RN-541 («bootstrap
откладывается до handoff Arquiteto»). Ни одна из двух не вышла из ADR — обе
родились из прямой просьбы владельца продукта и живут только в
docs/business-rules.md, — так что это первая структурная запись момента, в
который рождается репозиторий. МЕХАНИЗМ provisioning не меняется: это тот же
ProvisionRepositoryUseCase, идемпотентный по замыслу начиная с
ADR 0005, вызываемый тем же
AcceptHandoffUseCase. Правило цели handoff из
ADR 0038 (внешний handoff адресует только lead
области или агента без области) и цепочка Arquiteto → Dev Lead из
ADR 0053 остаются нетронутыми.
Контекст
Реальная установка v6.1.0, 2026-09-14, дала такую последовательность в event log нового проекта:
- 02:40:54 — Arquiteto трижды предлагает
open_adr_pr. Репозитория ещё не существует, и инструмент падает все три раза. - 02:43:53 —
handoff.offeredдляinfraи дляdev-lead(подтверждение готовности архитектуры предлагает оба,OfferInfraHandoffUseCase). - Принят только
infra.dev-leadостаётсяoffered. - 03:02:34 —
POST /projects/:id/execution/activateотвечает 201. project_repositories,project_git_connectionsиrepo_bootstrapsостаются пустыми. Проект навсегда остаётся без репозитория.
Два дефекта независимы, и RN-522 несла оба:
- Триггер стоял после тех, кому нужен репозиторий. Arquiteto — первый
агент с инструментом, который ПИШЕТ в репозиторий (
open_adr_pr), а Infra Lead, активируемый handoff Arquiteto → Infra, — второй (open_infra_pr). Оба работают ДО принятия Dev Lead. RN-541 говорила «репозиторий рождается, когда начинается разработка», а разработка, измеренная тем, что делают агенты, начинается с Arquiteto. - Единственный триггер можно было ПРОПУСТИТЬ. Активация исполнения не
требует принятого handoff Dev Lead — она требует действующего
module_map, и больше ничего. Кто активирует через Обзор (или через ярлык «Ativar execução» в самой карточке handoff, RN-137), пропускает принятие, а с ним и provisioning. Ничто на пути не отказывало.
Кто передаёт Arquiteto, измерено. Arquiteto можно активировать только с
accepted handoff, адресованным ему (canActivateAgent,
domain/sessions/agent-activation.ts), — исключение только Criativo. Handoff
рождается двумя дверями, и обе пишут через ТОТ ЖЕ CreateHandoffUseCase: PO,
заканчивая backlog, через offer_handoff(to_agent: "arquiteto") (это велит
инструкция kickoff в po_server.ex), или пользователь, через ручной handoff
(RequestManualHandoffUseCase, ADR 0109). А принятие одно:
AcceptHandoffUseCase, тот же, что уже вмещал триггер RN-522. Нет пути, на
котором Arquiteto работал бы, не пройдя через это принятие.
Решение
1. Триггером становится принятие handoff, адресованного arquiteto. В
AcceptHandoffUseCase ветка, вызывающая provisionarRepositorio, начинает
срабатывать для toAgent === 'arquiteto', ДО activateAgent — тот же
порядок, что уже использовала RN-522, по той же причине: Arquiteto
просыпается в проекте, где уже есть где работать, и первый open_adr_pr
находит репозиторий. Всё остальное в ветке — байт в байт: всегда local
(единственный provider без учётных данных), отказ становится
repository.provision_failed с источником и никогда не роняет принятие,
ПРИНЯТЫЙ (adopted) репозиторий выходит раньше, не являясь отказом.
2. Старый триггер ОСТАЁТСЯ как вторая идемпотентная дверь. Принятие
dev-lead по-прежнему вызывает тот же provisioning. Три причины, по
убыванию веса:
- Это выход для уже сломанных проектов. Проект, прошедший Arquiteto ДО
этого изменения (
exp001из AT-092 — ровно такой: handoff Arquiteto принят по старому правилу, handoff Dev Lead всё ещёoffered), имеет ожидающий handoff, принятие которого выполняет provisioning. Без этой двери единственным выходом была бы страница ручного provisioning, которую ни один экран не предлагает из сессии. - Это естественное возобновление после отказа. Если provisioning упадёт при принятии Arquiteto, событие остаётся в логе, а следующий шаг, который пользователь всё равно делает, — принять Dev Lead — пробует снова, без того чтобы кому-то нужно было знать о существовании маршрута provisioning.
- Цена — ноль по построению. При уже работающем репозитории
createdProvisionRepositoryUseCaseне вызываетcreateRepo, не создаёт ни сессию, ни bootstrap, а runner сообщает все шаги выполненными (ADR 0005). Это уже было доказано для трёх последовательных запусков use case; этот ADR добавляет доказательство ЧЕРЕЗ ОБЕ ДВЕРИ: принять Arquiteto, а затем Dev Lead, против настоящего Postgres иLocalGitProvider, заканчивается ОДНИМcreateRepo, ОДНОЙ строкой вproject_repositories, ОДНОЙ вrepo_bootstrapsи ни однимrepository.provision_failed.
Удаление второй двери рассматривалось и было отвергнуто: у неё нет цены, а
альтернатива оставляет exp001 без выхода через экран, на котором находится
пользователь.
3. execution/activate без репозитория ОТКЛОНЯЕТСЯ с 409. В
ActivateExecutionUseCase, сразу после проверки module_map и ДО любого
эффекта (сохранения бюджета, засева permissions.json, создания сессии), use
case запрашивает ProvisionedRepositoryRepository.findByProjectId. Без строки —
ConflictException с НАЗВАННОЙ причиной, выбранной по состоянию handoff
проекта (HandoffRepository.findByProject, новое чтение, только чтение)
чистой доменной функцией:
- есть
offeredhandoff Arquiteto → «примите handoff Arquiteto»; - иначе, есть
offeredhandoff Dev Lead → «примите handoff Dev Lead» (вторая дверь); - иначе, один из двух уже принят → автоматический provisioning выполнился и
не оставил репозитория (
repository.provision_failedговорит почему), и путь — страница provisioning проекта; - иначе → ни один handoff Arquiteto не принят, и репозиторий рождается в нём.
ПРИНЯТЫЙ (adopted) репозиторий считается репозиторием. 409, а не 400, потому
что запрос верен: это СОСТОЯНИЕ проекта пока не позволяет. Повторная
активация уже работающего исполнения по-прежнему не является конфликтом, КОГДА
репозиторий есть; exp001, активировавший без него, начинает получать 409 и
при повторной активации — и это то, что нужно, потому что его dev agents
никогда не имели, где работать.
4. Экран не предлагает активировать без репозитория и говорит почему один
раз, текстом. Раздел Исполнения в Обзоре читает GET .../git/repository (тот
же queryKey ['repository', projectId], который уже используют
ProjectPage, CodeShell и Настройки, — по сути никакого нового запроса) и,
при ПОДТВЕРЖДЁННОМ ответе null, оставляет кнопку инертной на месте и пишет
причину, со ссылкой на страницу provisioning. При загрузке или ошибке кнопка
остаётся как была: «не знаю» не становится «нет», а бэкенд отклоняет в любом
случае. В карточке handoff Dev Lead, внутри сессии, ярлык «Ativar execução»
исчезает, пока репозитория нет, и карточка говорит, что принятие handoff его
подготавливает, — кнопка принятия рядом. Это мерка RN-102 и ADR 0064: убирает
элемент управления, никогда информацию.
Последствия
- RN-522 меняет триггер, а не механизм. RN-582 фиксирует новое правило; RN-522 и RN-541 получают заметку, указывающую на неё, а комментарий мастера создания перестаёт говорить «Dev Lead».
- Случай Arquiteto → Infra до Dev Lead перестаёт существовать. Infra Lead активируется handoff Arquiteto, который по построению существует только после принятия Arquiteto, — так что репозиторий уже родился, когда он просыпается.
- Локальный отказ
open_adr_pr/open_infra_pr(AT-088) остаётся необходимым и дополняющим: он покрывает provisioning, который УПАЛ, и унаследованный проект, существованию которого этот ADR помешать не может. - Последствие, измеренное в коде и принятое: сессия
git-bootstrapначинает рождаться ПОСЕРЕДИНЕ фазы Arquiteto.ProvisionRepositoryUseCaseоткрывает собственную сессию для лога bootstrap, а Обзор и сводка workspace выбирают «самую свежую» сессию поcreatedAt. По RN-522 эта сессия рождалась при принятии Dev Lead и вскоре обгонялась сессией исполнения; теперь она становится «самой свежей» сессией проекта на протяжении всего разговора с Arquiteto, пока не родится следующая сессия. Сессия чата не затронута (её экран читает собственный id). Исправление требует решить, заслуживает ли лог bootstrap собственной сессии или «самая свежая» должна игнорировать системные сессии, — оба варианта трогают правило другого направления (RN-139, ADR 0005), и здесь не решаются. - Проект вообще без handoff Arquiteto, с
module_map, записанным вне потока, попадает в четвёртую причину отказа и выходит через страницу provisioning. Автоматического пути для него нет, и он не нужен. - Отвергнуто: provisioning при создании проекта (отменил бы RN-541,
являющуюся просьбой владельца продукта); provisioning ВНУТРИ
execution/activate(у активации появился бы скрытый эффект git, а агенты до неё по-прежнему оставались бы без репозитория — весь дефект 1); и перенос триггера без сохранения старой двери (оставил быexp001без выхода через сессию).