ADR 0186 — Handoff от PO к Arquiteto принимается без клика, когда backlog покрыт и репозиторий локальный
Статус
Accepted. 2026-09-30 (AT-314, история HS-079, раунд 32; решение владельца от 30.09: "Handoff oferecido pelo PO (LLM) pode provisionar o repositório local sem clique?" — да). Поверх ADR 0165, который поместил рождение репозитория в принятие handoff к Arquiteto и имел неявной предпосылкой, что это принятие — всегда клик человека. Не редактирует его: добавляет второго автора того же принятия, с закрытыми условиями, и оставляет место подготовки ровно там, куда его поставил 0165.
Контекст
Измерено в коде dev 2026-09-30 (исследование AT-309):
- PO завершает backlog и вызывает
offer_handoff(to_agent: "arquiteto")(po_server.ex, kickoff: "Cubra TODAS as regras com ao menos uma história. Quando o backlog estiver pronto, ofereça um handoff ao arquiteto"). Предложение рождаетсяofferedи ждёт клика "Aceitar handoff e iniciar Arquiteto". - Этот клик подготавливает репозиторий (
AcceptHandoffUseCase, RN-582). Для репозиторияlocal— единственного, который создаёт принятие, потому что это единственный provider без учётных данных, — клик ничего не выбирает: нет хостинга для решения и нет секрета для разрешения. Это шаг подтверждения на пути без альтернативы. - Покрытие backlog уже имеет ОДИН подсчёт в продукте,
computeCoverage(domain/backlog/coverage.ts), используемый вкладкой Backlog (GetCoverageUseCase) и инструментом чтения PO (ListBusinessRulesUseCase, RN-164): правило покрыто, когда хотя бы одна история ссылается на него вbusinessRuleIds. - Таблица исследования насчитала ~11 кликов-решений (+N продвижений) от brief до выполнения; этот — один из тех, что защищают лишь сами себя.
Решение
- Когда. Предложение
po → arquitetoпринимается системой в ТОМ ЖЕ запросе, в котором engine его создаёт (POST /internal/sessions/:id/handoffs), после транзакции предложения и никогда не внутри неё, тогда и только тогда, когда:- предложение в состоянии
offered; - backlog покрыт — объективное определение, измеряемое по тому, что записывает
PO: у проекта есть хотя бы одно бизнес-правило
(
artifact.business_rule) и ни одного правила без истории, ссылающейся на него вbusinessRuleIds, по ТОМУ ЖЕcomputeCoverage, что во вкладке Backlog. Ноль правил — не покрыто: "нечего покрывать" не доказывает, что PO работал; - репозиторий, которого касается принятие, локальный и без учётных данных: у
проекта нет репозитория (принятие подготовит
local) или имеющийся —local, и у проекта нет зарегистрированного git-подключения (OAuth) вproject_git_connections; - тот, кто открыл сессию, всё ещё имеет в проекте роль, которую требует человеческий
маршрут принятия (
developer, действующая роль RN-471). Решение — чистый предикатdecidirAceiteAutomatico(domain/sessions/aceite-automatico-do-handoff.ts); у каждого "нет" есть названная причина, и именно её возвращает ответ engine.
- предложение в состоянии
- Принимает по-прежнему
AcceptHandoffUseCase. Новый use case (AceitarHandoffAutomaticamenteUseCase) лишь ЧИТАЕТ и РЕШАЕТ; при "да" вызывает обычное принятие, которое подготавливает репозиторий ДОactivateAgent(RN-582, ADR 0165). Второго пути подготовки нет. - Проверяемо.
handoff.acceptedавтоматического принятия записывается с СИСТЕМНЫМ актором{kind: 'system', id: 'handoff-auto-accept'}и payloadautomatico: true,emNomeDe(тот, кто открыл сессию, — от её имени подготавливается репозиторий,provisionedBy) иcriterio(regras,cobertas,repositorio). Следующийagent.activatedнесёт ТОГО ЖЕ актора. Человеческое принятие не меняется: акторuser, без этих полей. - Сбой не поднимается наверх. Предложение уже зафиксировано, когда выполняется автоматическое
принятие, а engine ждёт его ответа; исключение там превратилось бы в
"сбой предложения handoff" по поводу существующего предложения. Поэтому сбой становится
НОВЫМ событием
handoff.auto_accept_failed(системный актор,origem: 'infra',error), а ответ говоритfalhou. Карточка предложения остаётся на месте, когда принятие не успело отметитьaccepted. - Модель предупреждается. Ответ внутреннего маршрута получает
aceiteAutomatico: {aceito, criterio?, motivo?}и, приaceito: true,status: 'accepted';offer_handoffengine говорит PO, что Arquiteto уже активирован и что не следует предлагать снова (повторное предложение дало бы409 agente_ja_ativo, ADR 0182).
Последствия
- Обычный путь (новый проект, локальный репозиторий, PO, покрывающий правила) теряет один клик; лента показывает принятие с критерием ("o backlog cobre as N regras de negócio e o repositório é local").
- Удалённый репозиторий или репозиторий с учётными данными по-прежнему требует клика, как просил владелец. Ручной handoff (ADR 0109) тоже: он не проходит через внутренний маршрут.
- Handoff, предложенный LLM, начинает производить эффект git (
git init --bareproviderlocalи bootstrap Gitflow) без человеческого клика. Это объявленная цена, и именно её одобрил владелец; абсолютные пределы (push, PR, deploy, merge в защищённую ветку — RN-418) не сдвигаются, потому что ничего из этого не происходит при локальной подготовке. - Предложение другим адресатам и предложение Arquiteto другим агентом НЕ получают автоматического принятия. Расширение на другую пару — новый ADR.
- Ответ engine задерживается на столько, сколько занимают локальная подготовка и
startAgent, в пределах стандартногоreceive_timeoutReq. Не измерено в реальной установке в этой сессии. - Не измерено: engine не компилировался и не тестировался в этой сессии (без Elixir
в окружении); новая клаузула
offer_handoffи её тест ExUnit были написаны и оставлены для CI.