Перейти к основному содержимому

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"

Две ошибки сразу:

  1. task блокировалась решением, которое никто не принимал;
  2. источник был ложью. Никакая инфраструктура не падала. Это противоречит правилу, которое закрепил 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 продолжал умирать, только с правильной меткой. Честно и бесполезно.