ADR 0190 — Infra поднимает контейнер при принятии handoff, шагом сервера и с засеянной автономией, и именно она передаёт работу Dev Lead
Статус
Accepted. 2026-10-01 (AT-260, история HS-065, эпик EP-030, раунд 35;
решение владельца от 01.10: «container_start рождается засеянным
auto_approve при принятии handoff Infra, и сервер сам запускает подъём, когда
есть module_routing, не завися от модели»). Пересматривает фразу «никогда
не засеивается в автоодобрение» из
ADR 0133 (RN-491) и опирается на
страницу контейнеров из
ADR 0136, не редактируя ни один из них:
путь выбора, потолки и отказы, которые они установили, остаются; меняется то,
КТО запускает первый подъём и ждёт ли он клика.
В том же раунде и тем же решением от 01.10 (AT-262, RN-672): «Dev Lead
предлагается Infra, только при контейнере running; двойной handoff Arquiteto
уходит». Это меняет отношение между ролями, установленное
ADR 0053 (Dev Lead рождается от
Arquiteto) и зафиксированное ADR 0085
в docs/fluxo.yml, не редактируя их, — решение 7 ниже.
Контекст
Измерено в коде dev 2026-10-01, до изменения:
- При реальном использовании 29.09 (сессия
be70) Infra написала, что поднимет контейнер «параллельно» с PR инфраструктуры (seq 282), и не вызвалаpropose_container_start; единственной попыткой былcontainer_start_via_runner(seq 280), отклонённый по режиму (mounted). Контейнер поднялся лишь в 06:47:53, человеком на/containers. - Подъём целиком зависел от модели: kickoff Infra Lead (
build_kickoff) просил выбор на шаге 4, и ничто в коде не делало его, если модель не делала. AT-264 (RN-668) уже закрыла обе половины МОЛЧАНИЯ — весь пакет ответа диспетчеризуется до HALT PR, а завершение хода говорит, фразой сервера, когда подъём НЕ был предложен, — но оставила объявленным, что подъём как шаг сервера — это эта задача. То, что обещает RN-668 ("não foi proposta"), было правдой, но было обычным исходом. - Текст карточки говорил, что kickoff делает подъём необязательным («до,
после или никогда»,
infra_lead_server.ex:955-961). Больше не воспроизводится: RN-668 переписала шаг 4 («происходит, только если вызван ДО неё или в ТОМ ЖЕ ответе»). Остальное воспроизводится. - Принятие handoff Infra засевало ДВЕ автономии (
INFRA_AUTONOMY_SEEDSвaccept-handoff.use-case.ts):open_infra_pr: auto_approveиterminal: deny.container_startпо умолчанию оставалсяrequire_approval—maintainerвdecide.ts, ВНЕ блока абсолютных потолков и ДОКАЗАННО способный достичьauto_approveчерезagent_autonomy(decide.spec.ts, "agent_autonomy auto_approve CONSEGUE chegar a auto_approve"), — аProposeActionUseCaseуже выполняет подъём на путиauto_approved(ветка существовала для тех, кто настраивалpermissions.json). То есть весь механизм уже существовал, не хватало семени и запуска. /containers(ADR 0136, RN-521) предлагает как ЧЕЛОВЕК (actor.kind: 'user'), аagent_autonomyчитается только когда актор — агент. Засев для Infra ничего не меняет на этой странице.
Решение
-
Семя. Принятие handoff, адресованного
infra, засевает ТРЕТЬЮ автономию:container_start: auto_approve, рядом с двумя прежними. Только её:container_start_via_runnerиcontainer_stopостаются настраиваемыми и никогда не засеваются, аcontainer_removeостаётся в абсолютном потолкеdecide.ts(RN-495), который не пересекает никакая автономия. -
Запуск — шаг СЕРВЕРА, а не новый инструмент. В kickoff Infra Lead — ходе, рождающемся из принятого handoff, — ДО первого обращения к модели сервер читает контекст инфраструктуры (одно чтение, то же, что для текста kickoff) и, если есть ДЕЙСТВУЮЩАЯ маршрутизация (
artifact.module_routingсоstatus: roteadoи хотя бы одним кандидатом) и проект поднимается через broker (container/mounted), предлагаетcontainer_startТЕМ ЖЕ путём, что инструмент (propor_container_start/2): сначала локальные отказы по режиму и состоянию (RN-566, RN-610), затемpropose_action, где api отказывает без broker (RN-591), разрешает автономию и —auto_approved— выполняетExecuteContainerStartUseCase, который выбирает образ черезDecidirImagemDoProjetoUseCaseсdecidedBy: 'infra-lead'(RN-491). Никакого параллельного пути выбора не рождается. -
Выбор сервера детерминирован. Кандидат с НАИБОЛЬШИМ числом модулей маршрутизации; при ничьей — тот, что встречается первым. Сеть
noneи ресурсы по умолчанию — те же значения по умолчанию, что у инструмента.rationaleговорит, что это был сервер и почему («кандидат Arquiteto для N из M модулей»). Это всегда один из кандидатов, и api отклоняет любой другой. -
След — инструментальный, без лжи о том, кто вызвал. Шаг записывает долговременные
tool.call(сorigem: "servidor") иtool.resultи НЕ кладёт сообщениеrole: "tool"в историю модели: модель его не вызывала, а ответ инструмента без вызова providers отклоняют. Модель узнаёт о подъёме из ТЕКСТА kickoff, который начинает говорить, что УЖЕ произошло (предложение и возвращённый статус или отказ и причину), вместо того чтобы просить выбор. -
runnerостаётся как есть. Путьcontainer_start_via_runner(поднимает уже решённый образ на машине пользователя) не получает ни шага сервера, ни семени: решение владельца его не коснулось. Kickoff проектаrunnerсохраняет прежние шаги, а подъём по-прежнему предлагается моделью и решается человеком. -
RN-668 остаётся согласованной. Шаг сервера помечает ход ТЕМ ЖЕ
registrar_subida/3, что и инструмент: подъём, предложенный сервером, не получает фразы "não foi proposta" в завершении; ОТКЛОНЁННЫЙ подъём сервера (и не исправленный моделью) получает фразу отказа, которая правдива. Завершение по-прежнему говорит в случаях, когда подъём не был делом сервера (нет маршрутизации,runner), а модель его не предложила. -
Dev Lead предлагается Infra, при контейнере
running(RN-672). «Confirmar arquitetura pronta» начинает предлагать только Infra:offerDevHandoff, маршрут engineoffer-dev-handoffи:offer_dev_handoff/handoff_dev_pendenteу Arquiteto уходят. Сервер Infra Lead предлагаетinfra → dev-lead(режимif_absent, ADR 0182), когда контейнер проекта ЗАРЕГИСТРИРОВАНrunning, спрашивая в конце каждого своего хода и при приходеcontainer.runningиз outbox — второй потребитель этого события, рядом с wake dev agents (RN-502). Вторая дверь репозитория при принятии Dev Lead (RN-582) остаётся: принятие то же, изменилось лишь, кто предлагает.
Последствия
- Обещание становится правдой по построению в обычном случае. У проекта
container/mountedс маршрутизацией подъём предложен — и, с семенем, выполнен — до того, как модель что-либо скажет. - «Никогда не засеивается» перестаёт быть правдой, и это ЕДИНСТВЕННОЕ
меняющееся правило. Минимальная роль остаётся
maintainer, читаемой как ЭФФЕКТИВНАЯ роль того, кто открыл сессию (resolveEffectiveRole.forProject(session.createdBy)): сессия, открытаяdeveloper, получаетdeny, как раньше, и kickoff говорит об отказе.denyизpermissions.jsonпо-прежнему побеждает автономию. - Выключение — это правка СПЕЦИФИЧНОГО правила. Переключатель
ручной/авто на карточке агента пишет шаблон (
*, RN-153), а специфичное правило побеждает шаблон: перевод Infra в «ручной» НЕ выключает это семя — та же ситуация, что уже действовала дляopen_infra_prначиная с Фазы 4a. Кто хочет подъём с кликом, должен поменять правилоcontainer_startу Infra. Объявлено, здесь не решено. - Повторное принятие Infra заново засевает. Принятие делает
upsert, как уже делало с двумя прежними семенами: новый handoff Infra в новой сессии снова ставитauto_approve. То же поведение, что у существующих семян. - Kickoff может ждать подъёма.
propose_actionконтейнера, выполненный автоодобренным, отвечает послеstartbroker (потолок 225 с в engine, RN-605), а шаг выполняется до первого обращения к модели: ход kickoff остаётсяworkingэто время, в Task, с «Parar», обслуживаемым как всегда. Большой образ, превышающий потолок pull (RN-605), приходит какfailed, и kickoff говорит это модели. - Контекст инфраструктуры, который падает, ничего не поднимает. Без чтения контекста нет маршрутизации для выбора; kickoff деградирует, как раньше, и подъём в этом ходе снова зависит от модели. Подъём сервера также происходит только в kickoff: исправление gate и сообщение composer его не повторяют.
- Dev Lead перестаёт быть принимаемым без контейнера. Цепочка становится
Arquiteto → Infra → (контейнер
running) → Dev Lead → исполнение, иdocs/fluxo.ymlговорит это: выходhandoff-duploу Arquiteto становится одним handoff, к Infra, а Infra получает два выхода к Dev Lead (контейнер и handoff). Проект, чей контейнер никогда не поднимается, не доходит до Dev Lead этим путём; ручной handoff агенту по выбору (ADR 0109) продолжает существовать. - Уведомление из outbox — at-most-once, как
Engine.Dev.Wake: без работающего Infra Lead, когда приходитcontainer.running(сессия закрыта, engine перезапускается), предложение ждёт конца следующего хода Infra. Проектrunnerзависит от этого уведомления, потому что его подъём никогда не происходит внутри хода Infra. - Комментарий в
decide.ts, говорящий «не засеян вINFRA_AUTONOMY_SEEDS», устаревает в этом изменении, потому что файл принадлежит другому направлению, идущему в том же раунде; само правило (container_startвне потолков, способный достичьauto_approve) не меняется.