ADR 0185 — «Estou pronto — a necessidade está validada»: один клик закрывает готовность, gate necessidade-validada и принятие PO
Статус
Accepted. 2026-09-30 (AT-311 и AT-312, история HS-079, эпик EP-031, раунд 32). Решение владельца, с двумя вопросами из исследования AT-309, на которые дан ответ ДА:
«Может ли один клик 'Estou pronto — a necessidade está validada' закрыть оба gate, даже если product_brief создаётся после клика?»
«Может ли принятие PO быть неявным в 'Estou pronto'?»
Пересматривает дизайн ADR 0095 (gate
necessidade-validada закрывается ОТДЕЛЬНЫМ кликом) и
RN-406. Не редактирует 0095: фиксирует, что меняется и
что из него продолжает действовать.
Контекст
Измерено в origin/dev (94e5dc721d), на основе исследования AT-309:
- «Validar necessidade» ничего не блокировало. Кнопка включалась только при
artifact.product_briefв сессии (hasProductBrief,session-readiness.ts), шла ПОСЛЕ «Estou pronto para produzir», записывала толькоnecessity.validated(ValidateNecessityUseCase), а gate —warnвdocs/gates.yml: сама RN-406 говорит, что ничто в продукте не ПРОВЕРЯЕТ прохождение. - Принять PO было ещё одним кликом сразу после «Estou pronto».
Handoff Criativo→PO рождается внутри хода, который запускает «Estou pronto»
(
CriativoServer.executar_confirm_readiness/1→EngineApiClient.create_handoff→POST /internal/sessions/:id/handoffs), а карточка принятия вызывалаAcceptHandoffUseCase, который записывает переход вaccepted,handoff.accepted(актор — человек),agent.activated(актор — человек) и, по RN-635,handoff.supersededдругих предложений PO в проекте. - Три клика — готов, проверить, принять — чтобы выразить одно намерение: «то, что собрал Criativo, верно, можно продолжать».
0095 явно отказался сливать проверку с confirm_readiness, с
аргументом, который остаётся верным: confirm_readiness сам по себе — это структурный
НИЖНИЙ ПОРОГ («≥1 зафиксированное правило», RN-142), а не суждение по СУЩЕСТВУ, и
слияние двух «сохранило бы мелкий порог, замаскированный под новый gate». На это
возражение здесь отвечает МЕТКА: клик перестаёт быть «Estou pronto para
produzir» и становится «Estou pronto — a necessidade está validada». Человек
объявляет обе вещи словами обеих; структурный порог по-прежнему
проверяется engine, а суть по-прежнему объявляется человеком.
Решение
1. Один POST, два события (RN-657)
POST .../sessions/:id/readiness (ConfirmReadinessUseCase) записывает
readiness.confirmed, вызывает engine и, только если engine принял ход,
записывает necessity.validated с тем же человеком в качестве актора,
productBriefId: null, via: 'readiness.confirmed' и readinessEventId.
Нулевой productBriefId — ответ на вторую половину вопроса владельца:
brief рождается из хода, который запускает клик, и событие указывает на КЛИК,
а не на ещё не существующий brief.
Отказ engine (409 ход идёт, 422 нет правила) поднимается до второго
события: без принятого хода нет brief в пути, и потребность не становится
проверенной. readiness.confirmed по-прежнему записывается ДО команды,
как всегда (ADR 0163).
Gate остаётся warn. Ничто не начало его проверять, и его повышение остаётся
продуктовым решением с собственным ADR, как сказал 0095.
2. Принятие PO неявное, с ТЕМИ ЖЕ событиями (RN-658)
readiness.confirmed нового клика несёт метку
{ necessidadeValidada: true, aceiteImplicitoDoPo: true }. Когда engine
создаёт предложение Criativo→PO по внутреннему маршруту, api спрашивает
(decidirAceiteImplicitoDoPo, чистое правило), является ли оно тем предложением, которое запросил
этот клик, — criativo → po, offered, этой сессии, несущее product_brief,
родившийся ПОСЛЕ самого свежего помеченного readiness.confirmed, — и, если да,
принимает его через AcceptHandoffUseCase, ТОТ ЖЕ сценарий использования, что у карточки, от имени того,
кто кликнул.
Проверяемый след — как у карточки, без нового события: handoff.accepted и
agent.activated с человеком в качестве актора и метка
implicito: { via: 'readiness.confirmed', readinessEventId } в payload обоих.
Из двух вариантов, оставленных владельцем (новое событие или те же с
человеком-актором и меткой), выбран второй, потому что каждый сегодняшний читатель —
canActivateAgent, список handoffs, проекция графа, экран — уже
понимает handoff.accepted; новое событие потребовало бы научить каждого, а
забытый прочитал бы PO как никогда не принятый. Ни одно событие не редактируется.
Принятие происходит во внутреннем маршруте, а не в POST клика, потому что
предложения в момент клика не существует. Сбой принятия не поднимается к engine
(Criativo рассказал бы «не смог предложить handoff», что ложно): он становится устойчивым
agent.error, актор system/aceite-implicito, с источником. 400 «не offered» —
не сбой: другая вкладка решила предложение в промежутке, и это уже в log.
3. Engine не меняется
Измерено: Criativo ничего не ждёт от принятия (ответ внутреннего маршрута лишь
сопоставляется с {:ok, _}), а PO включается тем же привычным startAgent,
чей kickoff — GenServer.cast. Единственное отличие — МОМЕНТ: PO поднимается
внутри вызова создания предложения, ещё в ходе Criativo, а не
после клика человека.
4. Экран
Кнопка называется «Estou pronto — a necessidade está validada» (en: «I'm ready
— the need is validated»), заголовок во включённом состоянии говорит, что закрывает клик и что
PO входит сам, а уведомление об успехе повторяет это. Кнопка «Confirmar
necessidade validada» ушла вместе с handleValidateNecessity и
validateNecessity клиента. Клик также делает PO получателем
composer — это жест его вызова, тот же, что принятие через карточку (RN-631).
Последствия
За
- С трёх кликов до одного, не трогая ни одного потолка: принятие PO не подготавливает репозиторий (только Arquiteto и Dev Lead, RN-582), не делает ни merge, ни push.
- Проверка по-прежнему принадлежит ЧЕЛОВЕКУ, никогда Criativo, — антипаттерн, ради избежания которого существовал 0095, по-прежнему избегается.
- След принятия тот же, что у карточки, читаемый каждым существующим потребителем, и метка говорит, из какого клика он пришёл.
Против, объявлено
necessity.validatedперестаёт указывать на brief, который он проверил: связь — черезreadinessEventId, а brief — этоartifact.product_brief, следующий за тем кликом. Кому нужна точная пара, читает log по порядку.- Проверка объявляется ДО того, как человек прочитает резюме. Так
решил владелец; gate —
warnи измеряет объявление, а не чтение. POST .../agents/criativo/validate-necessityостаётся в api, без потребителя на экране: это путь для сессии, чей «Estou pronto» предшествует этому ADR (там карточка принятия тоже остаётся, потому что у старого события нет метки). Его удаление — нарушение контракта API, и оно остаётся на время, когда не будет старой сессии, которая его использует.- PO поднимается без клика человека в момент подъёма. Человек решил раньше, в клике, и именно он записан как актор.
Рассмотренные альтернативы
Новое событие (handoff.accepted_implicitly). Отвергнуто: см. пункт 2 — каждому
читателю handoff.accepted пришлось бы изучить второй тип.
Принимать в POST клика, предварительно создав предложение. Отвергнуто: предложение несёт
product_brief как артефакт и рождается из хода Criativo; создать его раньше
потребовало бы изменить engine и предложить handoff без артефакта, который его обосновывает.
Позволить engine вызывать человеческий маршрут принятия. Отвергнуто: человеческий маршрут требует аутентифицированного пользователя, а агент, включающий агента, — это ровно то, от чего отказывается описание внутреннего маршрута. Здесь решает api, по метке, которую записал ЧЕЛОВЕК.
Ссылки
- ADR 0095 — дизайн, который этот пересматривает
- ADR 0163 — клик отвечает при принятии
- ADR 0182 —
superseded, сохранено - RN-406, RN-657, RN-658, RN-633, RN-635
apps/api/src/domain/sessions/estou-pronto.tsapps/api/src/application/use-cases/agents/confirm-readiness.use-case.tsapps/api/src/application/use-cases/agents/aceite-implicito-do-po.use-case.ts