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

ADR 0018 — NoopDevAgent как постоянный режим выполнения

  • Статус: принят
  • Дата: 2026-07-25
  • Фаза: 4a (валидация инфраструктуры выполнения)

Контекст​

NoopDevAgent существовал в Фазе 4a (ADR 0011) как тело :work DevAgentServer и был заменён реальным агентом в следующей сессии (ADR 0012). Из-за этого единственным способом задействовать инфраструктуру выполнения от начала до конца — изолированный worktree, идентичность dev-<modulo>[bot], конвейер proposed_actions, PR — стал запуск РЕАЛЬНОГО агента, зависящего от LLM: дорогого, медленного и недетерминированного. Smoke-тест инфраструктуры не может зависеть от модели.

Эта сессия возвращает Noop, теперь как постоянный режим выполнения, сосуществующий с реальным агентом.

Решения​

1. Engine.Dev.AgentIo — общие побочные эффекты​

Новый модуль с via/2, emit/3, claim_task/1, propose/3, propose_commit/2 (идентичность), propose_push/1, propose_pr/3, persist/1, block_task/3 и worktree_manager/0, извлечёнными из DevAgentServer без изменения поведения. Оба сервера используют один и тот же код: Noop, переписывающий worktree/идентичность/конвейер, проверял бы копию, а не инфраструктуру. dev_agent_server_test.exs (12 случаев) — страховочная сеть извлечения — прошёл без правки.

2. Engine.Dev.NoopDevAgentServer — режим, а не идентичность​

Собственный GenServer, тот же Registry, тот же agent_id (dev-<modulo>) и то же долговечное состояние, что у реального агента. Поскольку agent_id тот же, автономия и инструкции, засеянные api, действуют без каких-либо изменений. Цикл: claim → worktree → тривиальный файл → commit → push → PR → task in_review. Не открывает gate (QA — LLM-агент): Noop останавливается на PR. {:correct, _} имеет защитную клаузулу, которая возвращает task с диагнозом вместо обрушения процесса, — DevAgentServer.correct/3 — это cast в via/2, и он пришёл бы сюда.

3. dev_agent_states.impl — режим долговечен​

Новая колонка ("real" по умолчанию). Регидратация выбирает, какой модуль поднять: без сохранения режима Noop вернулся бы как реальный агент после перезапуска узла — начав тратить токены, хотя никто не просил. DevAgentSupervisor.start_agent/7 получает impl; server_for/1 отображает режим → модуль (всё, что не "noop", попадает в реальный — безопасное значение по умолчанию — продакшен). Принятие распараллеливания наследует impl базового агента по той же причине, по которой уже наследовало пределы.

4. Принятие распараллеливания начинает засевать дополнительного субагента​

Исправление реального бага: AcceptParallelizationUseCase не создавал строку agent_autonomy и инструкцию для dev-<modulo>-2. Без автономии decide() падает в значение по умолчанию require_approval — "принятие одним кликом" превращалось в три ручных одобрения на task, и критерий приёмки ("видеть третьего работающим") не закрывался. Теперь засевает инструкцию + auto_approve в тех же DEV_AUTO_GIT_ACTIONS, что у базового агента, до вызова engine. git_merge остаётся вне.

5. Bare repos смонтированы RW для engine в Compose​

Было git_local_repos:/data/git-repos:ro. С Фазы 4a исполнитель git_push работает В ENGINE и отправляет ветку worktree в bare repo — при монтировании только для чтения push умирает с remote unpack failed: unable to create temporary object directory, и PR dev agent никогда не открывается. :ro был предпосылкой Фазы 1 (engine лишь делал checkout), которую Фаза 4a сделала недействительной так, что никто не заметил: демо критерия приёмки не проходило с прежней конфигурацией.

Последствия​

  • POST /projects/:id/execution/activate принимает devAgentImpl: 'real' | 'noop' (опущено = real). UI не меняется — режим Noop — инструмент валидации, а не возможность продукта; демо включает его скриптом.
  • pnpm --filter api demo:noop-execution прогоняет весь критерий приёмки против стека Compose.
  • Новые тесты: noop_dev_agent_server_test.exs (цикл, идентичность, отсутствие LLM, падающий worktree, 2 параллельных агента), dev_rehydrator_test.exs (режим переживает перезапуск, :work не перезапускается, идемпотентность) и git_executor_test.exs (идентичность в настоящем git: автор bot + Co-authored-by, изолированные worktrees) — последний закрыл пробел: до сих пор утверждался лишь payload предложения, никогда — получившийся коммит.

Область и допущения​

Noop ни в чём не заменяет реального агента: не читает контекст task, не запускает suite, не проходит gates. Он служит, чтобы ответить "поднята ли инфраструктура выполнения?", не включая LLM. Предложение распараллеливания по-прежнему countClaimableByModule >= 2, а не граф зависимостей tasks (в схеме нет зависимостей между tasks) — остаётся открытым, как уже фиксировал ADR 0017.