ADR 0057 — Агент gate ждёт одобрения, как ждёт dev
- Статус: принят
- Дата: 2026-08-07
- Контекст: FASE 13b, находка AB
- Расширяет: ADR 0052
Контекст
ADR 0052 решил для
dev agent проблему инструмента, которому нужно одобрение посреди
цикла: агент ПРИОСТАНАВЛИВАЕТСЯ, удерживая историю, а решение возобновляет его с
настоящим результатом на месте слова pending.
Агенты gate остались вне того исправления, и никто этого не заметил, потому что ни одно выполнение до них не доходило. FASE 13b дошла.
В 6-м реальном выполнении qa-automacao выполнил:
ls -la && find . -name "AGENTS.md" -o -name "package.json" | head -50
Составная команда с сегментом вне allow — корректно отклонена и
корректно приостановлена ToolLoop. То, что произошло дальше, и есть дефект:
у QaLeadServer не было клаузулы для этого исхода, он попадал в catch-all и
записывал:
delegation.failed failureOrigin: "infra"
"QA de Automação não concluiu o parecer — desfecho inesperado do ToolLoop"
Две ошибки сразу:
- task блокировалась решением, которое никто не принимал;
- источник был ложью. Никакая инфраструктура не падала. Это противоречит правилу, которое закрепил ADR 0020: источник НАЗЫВАЕТСЯ, а не получается методом исключения.
Решение
Агенты gate приостанавливаются и возобновляются ровно как dev agent.
Субагент получает третий исход, рядом с {:ok, parecer} и
{:blocked, info}:
{:awaiting, %{action_id:, tool_call_id:, tool_name:, ctx:}}
Весь ctx передаётся вместе — именно он делает возобновление возможным, и это тот же
выбор, что в ADR 0052.
Lead становится тем, кто ждёт. Он уже был GenServer на проект, поэтому
получил три вещи:
- подписывается на
Engine.Dev.Wakeза субагентов (qa-automacao,qa-performance-seguranca), потому чтоtask.action_settledприходит с ключом актора, ПРЕДЛОЖИВШЕГО действие, а предлагает подспециальность; - хранит состояние в полёте (
pendente) — приостановленное делегирование, уже собранное, то, что осталось выполнить; - при
{:action_settled, ...}с ожидаемым имaction_idвозобновляет этого субагента и продолжает область с той точки, где остановился.
Что изменило форму и почему
rodar_ativas был Enum.map по активным делегированиям: прямая линия, которая
не могла остановиться посередине. Он стал continuar_area/4, рекурсивной по списку
оставшихся, с накопленными собранными. Именно эта форма позволяет приостановиться
между двумя делегированиями, не теряя первого.
Последствия
Одобрение перестаёт убивать gate. Пока ожидается, область не консолидирует, не выдаёт вердикт и не блокирует task — она ждёт. Клик пользователя разблокирует, а не приходит слишком поздно.
Отказ тоже возобновляет. Причина встаёт на место результата, и агент узнаёт, что этот путь закрыт, вместо вечного ожидания. То же правило, что в ADR 0052.
Источник infra перестаёт использоваться для ожидающего решения. То, что остаётся в
catch-all, — действительно неожиданный исход.
Чего это НЕ решает
Не устраняет одобрение и не должно. Allowlist остаётся закрытым списком, а агент продолжает выдумывать команды — находки X, Z и AD FASE 13b остаются открытыми. Меняется последствие: то, что раньше было стеной, становится очередью решений.
Перезапуск посреди ожидания теряет цикл. pendente живёт в памяти
QaLeadServer, который restart: :temporary. Dev agent решил это,
сохраняя статус в dev_agent_states; у gate нет эквивалентной таблицы, и
её создание — отдельная область. Объявлено как известное ограничение — gate запускается
снова через Dispatcher, когда task вернётся в цикл.
Рассмотренные альтернативы
Позволить gate работать с полной автономией, минуя конвейер одобрения. Отвергнуто: gate выполняет команды в worktree dev, а граница существует именно потому, что это исполняемый код третьей стороны. Заменить стену дырой — не решение.
Лишь исправить источник (infra → politica), без приостановки. Это был
промежуточный шаг, и его было недостаточно: gate продолжал умирать, только с
правильной меткой. Честно и бесполезно.