ADR 0186 — The PO's handoff to the Arquiteto is accepted without a click when the backlog is covered and the repository is local
Status
Accepted. 2026-09-30 (AT-314, story HS-079, round 32; owner decision on 30/09: "Can a handoff offered by the PO (LLM) provision the local repository without a click?" — yes). On top of ADR 0165, which placed the repository's birth in the acceptance of the handoff to the Arquiteto and had, as an implicit premise, that this acceptance is always a person's click. It does not edit it: it adds a second author of the same acceptance, with closed conditions, and leaves the place of provisioning exactly where 0165 put it.
Context
Measured in the dev code on 2026-09-30 (AT-309 survey):
- The PO finishes the backlog and calls
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"). The offer is bornofferedand waits for the "Accept handoff and start Arquiteto" click. - That click is what provisions the repository (
AcceptHandoffUseCase, RN-582). For thelocalrepository — the only one the acceptance creates, because it is the only provider without a credential — the click chooses nothing: there is no hosting to decide nor secret to resolve. It is a confirmation step on a path without alternatives. - Backlog coverage already has ONE computation in the product,
computeCoverage(domain/backlog/coverage.ts), used by the Backlog tab (GetCoverageUseCase) and by the PO's read tool (ListBusinessRulesUseCase, RN-164): a rule is covered when at least one story cites it inbusinessRuleIds. - The survey's table counted ~11 decision clicks (+N promotions) from the brief to execution; this is one of those that protect only themselves.
Decision
- When. The
po → arquitetooffer is accepted by the system, in the SAME request in which the engine creates it (POST /internal/sessions/:id/handoffs), after the offer's transaction and never inside it, if and only if:- the offer is
offered; - the backlog is covered — an objective definition, measured over what
the PO records: the project has at least one business rule
(
artifact.business_rule) and no rule without a story citing it inbusinessRuleIds, through the SAMEcomputeCoverageas the Backlog tab. Zero rules is not covered: "nothing to cover" does not prove the PO worked; - the repository the acceptance touches is local and without a
credential: the project has no repository (the acceptance will provision
local) or the one it has islocal, and the project has no git connection (OAuth) registered inproject_git_connections; - whoever opened the session still has, in the project, the role the human
acceptance route requires (
developer, effective role of RN-471). The decision is the pure predicatedecidirAceiteAutomatico(domain/sessions/aceite-automatico-do-handoff.ts); each "no" has a named reason, and that is what the response to the engine returns.
- the offer is
- Whoever accepts is still
AcceptHandoffUseCase. The new use case (AceitarHandoffAutomaticamenteUseCase) only READS and DECIDES; with "yes" it calls the usual acceptance, which provisions the repository BEFOREactivateAgent(RN-582, ADR 0165). There is no second provisioning path. - Auditable. The automatic acceptance's
handoff.acceptedis recorded with the SYSTEM actor{kind: 'system', id: 'handoff-auto-accept'}and payloadautomatico: true,emNomeDe(whoever opened the session — the repository is provisioned on their behalf,provisionedBy) andcriterio(regras,cobertas,repositorio). The followingagent.activatedcarries the SAME actor. Human acceptance does not change: actoruser, without those fields. - Failure does not surface. The offer is already committed when the
automatic acceptance runs, and the engine waits for its response; an
exception there would become "failed to offer handoff" on an offer that
exists. So the failure becomes the NEW event
handoff.auto_accept_failed(system actor,origem: 'infra',error) and the response saysfalhou. The offer card stays there when the acceptance did not get to markaccepted. - The model is told. The internal route's response gains
aceiteAutomatico: {aceito, criterio?, motivo?}and, withaceito: true,status: 'accepted'; the engine'soffer_handofftells the PO that the Arquiteto was already activated and that it should not offer again (re-offering would give409 agente_ja_ativo, ADR 0182).
Consequences
- The common path (new project, local repository, a PO that covers the rules) loses one click; the thread shows the acceptance with the criterion ("the backlog covers the N business rules and the repository is local").
- A remote or credentialed repository still asks for the click, as the owner asked. A manual handoff (ADR 0109) too: it does not go through the internal route.
- A handoff offered by an LLM now produces a git effect (the
localprovider'sgit init --bareand the Gitflow bootstrap) without a human click. That is the declared price, and it is what the owner approved; the absolute ceilings (push, PR, deploy, merge into a protected branch — RN-418) do not move, because none of that happens in local provisioning. - Offers to other destinations, and the offer to the Arquiteto by another agent, do NOT get automatic acceptance. Extending to another pair is a new ADR.
- The response to the engine takes as long as local provisioning and
startAgenttake, withinReq's defaultreceive_timeout. Not measured on a real installation in this session. - Not measured: the engine was not compiled nor tested in this session (no
Elixir in the environment); the new
offer_handoffclause and its ExUnit test were written and are left for CI.