ADR 0175 — Infra Lead разговаривает через composer, а прежде его ход проходит через TurnoAssincrono
Статус
Accepted. 2026-09-27 (AT-141). Решение сопровождающего: да — Infra Lead начинает разговаривать через composer и становится СЕДЬМЫМ разговорным агентом. Ссылается на ADR 0163 (клик отвечает при принятии), который этот документ НЕ редактирует: решённое им для шести разговорных начинает действовать для седьмого тем ЖЕ механизмом. Ссылается также на ADR 0038 (иерархия агентов, lead и субагент) и ADR 0086 (приостановка Dev Lead, которой у Infra Lead нет). Правило: RN-617. Закрывает открытое решение, которое RN-584 оставила письменно.
Контекст
RN-584 (AT-098) убрала адресата по умолчанию у
сообщения чата и дала infra НАЗВАННЫЙ отказ (422 agente_sem_conversa), оставив объявленным продуктовый вопрос: разговаривает ли Infra Lead
через composer? И объявила цену ответа "да" без подготовки:
user_message/2, экспортируемый InfraLeadServer, выполнял ход ЦЕЛИКОМ
внутри handle_call, с пределом 180 с в GenServer.call.
Измерено в коде до изменения: Infra Lead был вне ВСЕХ частей, которые ADR 0163 и последующие RN дали остальным шести:
| Часть | Шесть | Infra Lead |
|---|---|---|
Немедленное принятие, working записан заранее (RN-578) | TurnoAssincrono.iniciar/3 | весь ход в handle_call/handle_cast |
Названный отказ 409 turno_em_andamento | да | второе сообщение ждало в очереди процесса |
| "Parar" (RN-122) | via_for/2 из cancel/2 | ни via_for/2 его не знал, ни Task не было, чтобы убить |
| Осиротевший ход при перезапуске (RN-586) | TurnoOrfao | "fica de fora — declarado na RN-586" |
| Остановка при закрытии сессии (RN-581) | terminate/2 бросает Task | убит после 5 с ожидания |
| Озвученный предел итераций (RN-166) | toolloop.limit_reached | цикл молча заканчивался на 14-м шаге |
Также kickoff: будучи cast, он не удерживал никакого клика, но занимал
процесс на весь ход — измерено в установке AT-089, 8,4 мин. Сообщение,
пришедшее в это время, ждало бы 8,4 мин в очереди и выходило бы
по :timeout в controller (500), а "Parar" было бы обслужено только потом.
И предел: propose_action для действий с контейнером, которые api выполняет
автоодобренными, ждёт до 225 с (RN-605), больше,
чем 180 с user_message/2. Сообщение, побудившее модель предложить
container_start, могло переполнить GenServer.call при ещё стоящем предложении.
Решение
- Сначала ход, потом клаузула. ВСЕ ТРИ хода Infra Lead —
kickoff, исправление gate (
{:correct, _}) и сообщение — поднимаются черезEngine.Agents.TurnoAssincrono, ТОТ ЖЕ модуль, что у шести. Kickoff остаётсяcast, запускаемым только при СВЕЖЕМ старте принятого handoff; меняется то, где он выполняется (в Task). Старыйuser_message/2, выполнявшийся inline, перестал существовать: имя осталось, и теперь это принятие — двух путей нет. - Консолидация с Workflows выполняется ВНУТРИ Task. Конец хода
(
concluir/1: HALTpropose_infra_pr→WorkflowsAgent→ единая PR) является частью функции хода и возвращаетstate; сигналы конца (agent.done,agent.status: idle) исходят только изTurnoAssincrono, в процессе сервера (RN-585). "Parar" убивает и идущий Workflows. - Исправление gate не отбрасывается.
TurnoAssincrono.iniciar/3безfromигнорирует запрос, когда идёт ход (защитный случай kickoff). Для исправления это потеряло бы запрос gate, поэтому оно входит в ОЧЕРЕДЬ (correcoes_pendentes) и поднимается при завершении хода — успех, сбой, crash или "Parar", — в порядке поступления. Это образецhandoff_dev_pendenteArquiteto. - Infra Lead входит в списки engine:
@agentes_de_conversaи собственная клаузулаmessage/2(отказ RN-584 уходит),via_for("infra")в "Parar" иTurnoOrfao(@agentes).Engine.Agents.Conversacionaisуже его перечислял. - Предел итераций — его собственный, 14, тот же, что у Arquiteto, Dev Lead, UX
Designer и Staff, — и 14-й шаг начинает выдавать
toolloop.limit_reached. - Экран предлагает его по обычному правилу.
infraвходит вAGENTES_DE_CHAT(apps/web/src/lib/session-readiness.ts): он является адресатом, когда он — самый свежийagent.activated, без адресата по умолчанию (RN-584). Принятие ЕГО handoff остаётся в собственной карточке RN-499, а карточка ленты исключает его по ИМЕНИ — иначе приглашение появлялось бы дважды и, будучи самым старым, снова скрывало бы приглашение Dev Lead (RN-136). - Разговор не открывает внешнего эффекта. Инструменты те же четыре;
PR инфраструктуры и два подъёма контейнера по-прежнему рождаются
proposed_action, с нетронутыми локальными отказами RN-566/RN-577/RN-610.
Последствия
- Предел 180 с перестаёт иметь значение для хода.
GenServer.calluser_message/2ждёт лишь принятия (миллисекунды); 225 сpropose_actionконтейнера идут внутри Task, без синхронного ожидания. Число 180 000 осталось, как у остальных шести, пределом ПРИНЯТИЯ. - Сообщение во время kickoff отклоняется с именем. Раньше ждало; теперь
это 409
turno_em_andamentoс durableagent.error— то же правило, что у остальных шести. - Генерируемый список не меняется.
SOLO_CONVERSATIONAL_AGENTS(генерируемыйpnpm --filter api gerar:areas) — это список агентов БЕЗ области, которых адресует ручной handoff; Infra Lead является lead области и остаётся вне его.AGENTES_DE_CHATне генерируется: это список, который защитаscripts/ci/destinos-do-composer.spec.tsсверяет с клаузулами. - Экран повествует его инструменты. Все четыре получили фразу в полосе
активности (
toolNarration.json) вместо fallback. - Объявлено и не закрыто: у Infra Lead нет ни
ask_structured_questions, ни чтения backlog — разговор с ним — это разговор с тем, что ему дал kickoff, и с обычными инструментами, а расширение этого — отдельное направление. Сообщение не перезапускает kickoff: Infra Lead, поднятый сообщением (перезапущенный engine), регидратирует историю и отвечает, не перегенерируя артефакты.