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

ADR 0192 — План тестирования рождается после сдачи dev, а gate implementavel начинает судить по истории и module_map

Статус​

Accepted. 2026-10-01 (AT-269, история HS-069, эпик EP-030, раунд 35; решение владельца от 01.10: «QA только после сдачи dev» — выход (b) из задачи, выбранный с пониманием, что он противоречит ADR 0090). Пересматривает часть ADR 0090, не редактируя его: QA-стратегия по-прежнему — это qa-lead во втором МОМЕНТЕ, без нового агента, но момент меняет место — уходит из проектирования (PRE-DEV) в начало цикла ревью сдачи. Appsec, другая половина 0090, НЕ меняется.

Контекст​

Симптом​

Реальное использование 29.09 (анализ analise-uso-real.md, пункт A23): два раунда toolloop.limit_reached 8/8, за которыми следует agent.error источника modelo, "limite de iterações atingido sem emit_plano_de_teste" (сессия be70, seq 373/374 и 463/464). Цикл потратил восемь итераций на чтение документов, пустой RAG и папки (seq 375–463), а seq 454 называет причину текстом: "O código ainda não existe". Dev Lead так и не получил заключения.

Что делал код (измерено в dev, 2026-10-01)​

  • Кто открывает gate. implementavel (docs/gates.yml) принадлежит dev-lead, fluxo: pre-dev, severidade: warn, aprovacao_humana: true, доказательство в event log (proposed_action.*, отфильтрованный по actionType: assess_implementability). Открывает его инструмент assess_implementability (Engine.Agents.DevLeadTools.run_assessment/2), который kickoff Dev Lead представляет как НЕОБЯЗАТЕЛЬНЫЙ ("use o julgamento").
  • Что он судил. run_assessment/2 читал хвост сессии; без artifact.plano_de_teste истории вызывал Dispatcher.run_qa_estrategia/3 → QaLeadServer.run_design/3 → QaEstrategiaAgent.run/4 и возвращал {:error, "chame de novo em instantes"}; с планом предлагал заключение (implementavel/inviavel + обоснование) с planoDeTeste и criteriosExecutaveis, встроенными в payload. Входом был план; СУЖДЕНИЕ уже принадлежало Dev Lead.
  • Контекст QA-стратегии. QaEstrategiaContext.fetch/3 давал story + module_map, без worktree; ReadFile/SearchWorkspace попадали в общий checkout проекта. EmitPlanoDeTeste принимает только после чтения — поэтому без кода для чтения модель ищет до потолка.
  • Потолок. "qa-estrategia" сводится к значению по умолчанию :conversacional (8) из Engine.Harness.Iteracoes, намеренно (ADR 0090: без token_budget_micros под ним подъём потолка умножил бы худший случай).
  • Кто зависит от вердикта. Никто в коде. Ни один use case api и ни один модуль engine не читает решение assess_implementability: активация исполнения его не запрашивает, ни у одного другого gate он не является entrada, а единственный потребитель — экран (фраза в apps/web/src/lib/aprovacoes.ts). Gate — warn: прохождение — это доказательство, а не замок.
  • Последующий gate. qa-verificada (block, владелец area-qa) выполняется в QaLeadServer.run/2 над worktree dev: ContextBuilder.fetch/3, делегирования (Automação всегда, Performance/Segurança по нефункциональным требованиям) и ОДИН консолидированный qa_verdict. Automação собирает coverageMatrix по правилам story (RF + RNF) и одобряет только при зелёной suite и покрытии каждого правила.

Решение​

  1. План тестирования — первый шаг цикла qa-verificada. QaLeadServer.run_area/3 после ContextBuilder.fetch/3 и до делегирований получает план СДАЧИ:

    • находит в хвосте сессии (потолок регидрации, RN-580) artifact.plano_de_teste ТОЙ ЖЕ taskId — раунд исправления (RN-015) не платит за ещё один цикл;
    • если его нет, запускает QaEstrategiaAgent.run/6 синхронно, в том же процессе (агент никогда не приостанавливается), с dev_state/ dev_context, которые получают подспециализации: workspace_root = worktree dev, единицы бизнес-правил и список файлов, затронутых сдачей (Engine.Gates.Diff, git diff dev...HEAD, до 40 имён), в первом сообщении. Diff, который падает, становится текстом ("não consegui listar: motivo"), никогда пустым списком.

    План входит в dev_context как :plano_de_teste, и Automação получает его В КОНЦЕ своего сообщения, как вход для шага 2 («какой тест покрывает каждое правило»). Мерка вердикта не меняется: одна строка coverageMatrix на правило story, approved только при зелёной suite и покрытии каждого правила. Внешний контракт по-прежнему — ОДИН qa_verdict на цикл.

  2. Неудавшийся план не задерживает ревью. QA-стратегия уже записывает agent.error с источником (RN-059); Lead продолжает без плана, а сообщение Automação — прежнее, байт в байт. Если бы отказ плана блокировал task, это был бы новый замок в gate block из-за нового входа — этого не решали, а решение владельца касается того, КОГДА рождается план, а не того, чтобы он проваливал сдачу.

  3. implementavel судит по истории и module_map. assess_implementability не читает и не запускает план: входом становится то, что у Dev Lead УЖЕ есть в контексте хода, — его kickoff перечисляет модули действующего module_map и истории backlog (RF, RNF, DoD), — а суждение идёт в justificativa. Заключение выходит при ПЕРВОМ вызове; payload теряет planoDeTeste/criteriosExecutaveis. Gate продолжает существовать, с тем же владельцем, серьёзностью, человеческим одобрением и доказательством — меняется только entrada ([story-ready, module_map]). Никакого нового gate.

  4. Что уходит. QaLeadServer.run_design/3, Dispatcher.run_qa_estrategia/3 (и callback) и QaEstrategiaContext — без вызывающего после пункта 3 они оставались бы доступными для момента, который решение владельца отправило в отставку.

  5. Артефакт меняет форму. artifact.plano_de_teste начинает требовать taskId (Engine.Harness.ArtifactSchemas): это план ОДНОЙ сдачи, и по нему Lead его находит.

  6. Потолок ОСТАЁТСЯ 8. "qa-estrategia" не получает клаузы в Iteracoes.tipo/1. Не хватало входных данных, а не запаса: когда код существует, а файлы сдачи есть в сообщении, двух-трёх чтений достаточно. Со стороны расходов изменилось то, что теперь есть task, и агент начинает работать под task_budget_micros, который делят подспециализации (RN-036), — критерий RN-085 даже позволил бы потолок gate при таком бюджете под ним, и решение — НЕ использовать эту дверь, чтобы план прошёл.

  7. docs/gates.yml и docs/fluxo.yml меняются, потому что изменилось, кто что кому передаёт: qa-verificada получает plano-de-teste в entrada; implementavel меняет plano-de-teste на module_map; в fluxo.yml qa-estrategia начинает получать сдачу от dev и передавать area-qa, с gate_saida qa-verificada (вход, а не собственный вердикт), а dev-lead перестаёт получать план тестирования.

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

За

  • У симптома реального использования больше нет причины: план больше не запрашивается у агента в момент, когда нет того, что ему нужно читать.
  • Dev Lead получает заключение при первом вызове, без асинхронного окна без callback, которое ADR 0090 объявил ценой (оно зависело от того, решит ли МОДЕЛЬ вызвать снова).
  • План начинает писаться по реальному коду, с worktree и diff, и потребляется тем, кто проверяет, — qa-verificada, — в том же процессе и том же цикле. Ноль маршрутов, ноль таблиц, ноль новых GenServer; на три детали меньше.
  • У плана появляется бюджет под ним (бюджет task).

Против

  • implementavel становится мельче. Раньше был второй взгляд (QA) на story до того, как тратить токены на написание кода; теперь заключение — только Dev Lead, по истории и module_map. Это прямая цена решения владельца, и gate был warn и необязательным — но «shift-left» QA, который купил ADR 0090, перестаёт существовать для QA (appsec остаётся в проектировании).
  • Ещё один цикл LLM на каждую СДАЧУ, на критическом пути ревью: Automação начинает только после плана. На task, а не на раунд (переиспользование по taskId); в длинной сессии план мог выпасть из окна в 200 событий и будет написан заново — это цена, никогда не неверные данные.
  • План конкурирует за тот же бюджет task, что и ревью: дорогая сдача может дойти до Automação с меньшим запасом.
  • План — вход, а не мерка. Должны ли исполняемые критерии плана стать обязательными строками coverageMatrix (и, значит, проваливать сдачу) — продуктовое решение, которое этот ADR НЕ принимает.
  • Потолок 8 по-прежнему может быть превышен на большой сдаче; тогда исход — озвученный agent.error, и ревью продолжается без плана. Измерение этого при следующем реальном использовании скажет, хватает ли 8.
  • Переведённый pt-BR docs/fluxo.yml (website/i18n/…) уже отставал от оригинала до этого изменения и продолжает отставать (AT-209).

Рассмотренные альтернативы​

Выход (a): оставить план в проектировании, на основе истории, правил и module_map, без исследования кода, с ранним требованием emit_plano_de_teste. Не противоречил 0090 и был самым дешёвым исправлением симптома. Отвергнут владельцем (01.10) в пользу (b).

Поднять потолок qa-estrategia до 60. Отвергнуто задачей и этим ADR: это означало бы проталкивать план, тратя больше, в момент, когда читать нечего.

Новый gate для плана (plano-de-teste-pronto) после сдачи. Отвергнуто: новый gate — продуктовое решение с собственным ADR, изменил бы внешний контракт (лишний вердикт на PR), а плану нечего ветировать в одиночку — вердикт покрытия уже принадлежит qa-verificada.

Запускать QA-стратегию асинхронно при сдаче (cast из DevAgentServer), параллельно с ревью. Отвергнуто: Automação начала бы до появления плана, а это та гонка, которую этот дизайн существует, чтобы избежать; и результат снова зависел бы от события, которого никто не ждёт.

Отправить implementavel в отставку. Отвергнуто: никто не просил; gate по-прежнему имеет что судить (историю и module_map) и остаётся измеримой записью того, что кто-то подумал о story до кода.

Ссылки​

  • ADR 0090 — второй момент qa-lead и secops; этот ADR меняет момент первого
  • ADR 0054 — внешний контракт gates
  • ADR 0085 — docs/fluxo.yml
  • RN-674 (docs/business-rules.md), RN-340/341 (пересмотренные ею), RN-085, RN-036, RN-163, RN-580
  • apps/engine/lib/engine/gates/qa_lead_server.ex, qa_estrategia_agent.ex, qa_automacao_agent.ex, dispatcher.ex; apps/engine/lib/engine/agents/dev_lead_tools.ex; apps/engine/lib/engine/harness/artifact_schemas.ex