ADR 0190 — A Infra sobe o container no aceite do handoff, por passo do servidor e com a autonomia semeada, e é ela quem entrega ao Dev Lead
Status
Accepted. 2026-10-01 (AT-260, história HS-065, épico EP-030, rodada 35;
decisão do dono em 01/10: "container_start nasce seedado auto_approve no
aceite do handoff da Infra, e o servidor dispara a subida sozinho quando houver
module_routing, sem depender do modelo"). Revisa a frase "nunca seedada em
auto-aprovação" do
ADR 0133 (RN-491) e se apoia na
página de containers do
ADR 0136 sem editar nenhum dos dois: o
caminho de eleição, os tetos e as recusas que eles puseram continuam; o que
muda é QUEM dispara a primeira subida e se ela espera um clique.
Na mesma rodada, e pela mesma decisão de 01/10 (AT-262, RN-672): "o Dev Lead
é oferecido pela Infra, só com o container running; o handoff duplo do
Arquiteto sai". Isso muda a relação entre papéis que o
ADR 0053 pôs (o Dev Lead nasce
do Arquiteto) e que o ADR 0085
registrou em docs/fluxo.yml, sem editá-los — a decisão 7 abaixo.
Context
Medido no código de dev em 2026-10-01, antes de mudar:
- No uso real de 29/09 (sessão
be70) a Infra escreveu que subiria o container "em paralelo" à PR de infra (seq 282) e não chamoupropose_container_start; a única tentativa foicontainer_start_via_runner(seq 280), recusada por modo (mounted). O container só subiu às 06:47:53, por um humano na/containers. - A subida dependia inteiramente do modelo: o kickoff do Infra Lead
(
build_kickoff) pedia a eleição no passo 4, e nada no código a fazia se o modelo não a fizesse. A AT-264 (RN-668) já tinha fechado as duas metades do SILÊNCIO — o lote inteiro da resposta é despachado antes do HALT da PR, e o fecho do turno diz, com frase do servidor, quando a subida NÃO foi proposta —, mas deixou declarado que a subida como passo do servidor era esta atividade. O que a RN-668 promete ("não foi proposta") era verdade, mas era o desfecho comum. - O texto da card dizia que o kickoff tornava a subida opcional ("antes,
depois, ou nunca",
infra_lead_server.ex:955-961). Não reproduz mais: a RN-668 reescreveu o passo 4 ("só acontece se for chamada ANTES dela ou na MESMA resposta"). O resto reproduz. - O aceite do handoff da Infra semeava DUAS autonomias
(
INFRA_AUTONOMY_SEEDSemaccept-handoff.use-case.ts):open_infra_pr: auto_approveeterminal: deny.container_startficavarequire_approvalpor padrão —maintaineremdecide.ts, FORA do bloco de tetos absolutos e PROVADO capaz de chegar aauto_approveporagent_autonomy(decide.spec.ts, "agent_autonomy auto_approve CONSEGUE chegar a auto_approve") —, eProposeActionUseCasejá executa a subida no caminhoauto_approved(o branch existia para quem configurassepermissions.json). Ou seja: o mecanismo inteiro já existia, faltava a semente e o disparo. - A
/containers(ADR 0136, RN-521) propõe como HUMANO (actor.kind: 'user'), eagent_autonomysó é lida quando o ator é agente. Semear para a Infra não muda nada nessa página.
Decision
-
A semente. O aceite do handoff endereçado a
infrasemeia uma TERCEIRA autonomia:container_start: auto_approve, ao lado das duas de sempre. Só ela:container_start_via_runnerecontainer_stopseguem configuráveis e nunca semeados, econtainer_removesegue no teto absoluto dedecide.ts(RN-495), que nenhuma autonomia atravessa. -
O disparo é passo do SERVIDOR, não tool nova. No kickoff do Infra Lead — o turno que nasce do handoff aceito —, ANTES da primeira ida ao modelo, o servidor lê o contexto de infra (uma leitura, a mesma do texto do kickoff) e, se há roteamento VIGENTE (
artifact.module_routingcomstatus: roteadoe ao menos uma candidata) e o projeto sobe pelo broker (container/mounted), propõecontainer_startpelo MESMO caminho da tool (propor_container_start/2): as recusas locais por modo e estado (RN-566, RN-610) primeiro, depoispropose_action, onde a api recusa sem broker (RN-591), resolve a autonomia e —auto_approved— executaExecuteContainerStartUseCase, que elege a imagem porDecidirImagemDoProjetoUseCasecomdecidedBy: 'infra-lead'(RN-491). Nenhum caminho paralelo de eleição nasce. -
A eleição do servidor é determinística. A candidata do MAIOR número de módulos do roteamento; no empate, a que aparece primeiro. Rede
nonee recursos padrão — os mesmos defaults da tool. Orationalediz que foi o servidor e por quê ("a candidata do Arquiteto para N de M módulos"). É sempre uma das candidatas, e a api recusa qualquer outra. -
O rastro é de ferramenta, sem mentir sobre quem chamou. O passo grava
tool.call(comorigem: "servidor") etool.resultduráveis, e NÃO põe mensagemrole: "tool"no histórico do modelo: não houve chamada dele, e uma resposta de ferramenta sem a chamada é recusada pelos providers. O modelo sabe da subida pelo TEXTO do kickoff, que passa a dizer o que JÁ aconteceu (proposta e o status devolvido, ou a recusa e o motivo) em vez de pedir a eleição. -
runnerfica como está. O caminhocontainer_start_via_runner(sobe a imagem já decidida, na máquina do usuário) não ganha passo do servidor nem semente: a decisão do dono não o tocou. O kickoff de um projetorunnersegue com os passos de sempre, e a subida segue proposta pelo modelo e decidida por humano. -
A RN-668 continua coerente. O passo do servidor marca o turno pelo MESMO
registrar_subida/3da tool: subida proposta pelo servidor não ganha a frase "não foi proposta" no fecho; subida do servidor RECUSADA (e não corrigida pelo modelo) ganha a frase de recusa, que é verdade. O fecho segue falando nos casos em que a subida não cabia ao servidor (sem roteamento,runner) e o modelo não a propôs. -
O Dev Lead é oferecido pela Infra, com o container
running(RN-672). "Confirmar arquitetura pronta" passa a oferecer só à Infra: oofferDevHandoff, a rotaoffer-dev-handoffdo engine e o:offer_dev_handoff/handoff_dev_pendentedo Arquiteto saem. O servidor do Infra Lead ofereceinfra → dev-lead(modoif_absent, ADR 0182) quando o container do projeto está REGISTRADOrunning, perguntando no fim de cada turno dele e quando ocontainer.runningdo outbox chega — o segundo consumidor desse evento, ao lado do wake dos dev agents (RN-502). A segunda porta do repositório no aceite ao Dev Lead (RN-582) continua: o aceite é o mesmo, só mudou quem oferece.
Consequences
- A promessa vira verdadeira por construção no caso comum. Projeto
container/mountedcom roteamento tem a subida proposta — e, com a semente, executada — antes de o modelo dizer qualquer coisa. - "Nunca seedada" deixa de ser verdade, e é a ÚNICA regra que muda. O
papel mínimo continua
maintainer, lido como o papel EFETIVO de quem abriu a sessão (resolveEffectiveRole.forProject(session.createdBy)): sessão aberta pordeveloperrecebedeny, como antes, e o kickoff diz a recusa.denydepermissions.jsoncontinua vencendo a autonomia. - Desligar é mexer na regra ESPECÍFICA. O toggle manual/auto do card do
agente escreve a curinga (
*, RN-153), e regra específica vence a curinga: pôr a Infra em "manual" NÃO desliga esta semente — a mesma situação que já valia paraopen_infra_prdesde a Fase 4a. Quem quiser a subida com clique precisa trocar a regracontainer_startda Infra. Declarado, não resolvido aqui. - Reaceitar a Infra re-semeia. O aceite faz
upsert, como já fazia com as duas sementes de antes: um handoff novo à Infra numa sessão nova volta a pôrauto_approve. Mesmo comportamento das sementes existentes. - O kickoff pode esperar a subida. O
propose_actionde container executado auto-aprovado responde depois dostartdo broker (teto de 225 s no engine, RN-605), e o passo roda antes da primeira ida ao modelo: o turno do kickoff ficaworkingesse tempo, numa Task, com "Parar" atendido como sempre. Imagem grande que estoura o teto de pull (RN-605) chega comofailed, e o kickoff diz isso ao modelo. - Contexto de infra que falha não sobe nada. Sem a leitura do contexto não há roteamento a eleger; o kickoff degrada como antes e a subida volta a depender do modelo naquele turno. A subida do servidor também só acontece no kickoff: correção de gate e mensagem do composer não a repetem.
- O Dev Lead deixa de ser aceitável sem container. A cadeia passa a ser
Arquiteto → Infra → (container
running) → Dev Lead → execução, e odocs/fluxo.ymldiz isso: a saídahandoff-duplodo Arquiteto vira um handoff só, à Infra, e a Infra ganha duas saídas para o Dev Lead (o container e o handoff). Projeto cujo container nunca sobe não chega ao Dev Lead por este caminho; o handoff manual a um agente à escolha (ADR 0109) continua existindo. - O aviso do outbox é at-most-once, como o
Engine.Dev.Wake: sem Infra Lead de pé quando ocontainer.runningchega (sessão fechada, engine reiniciando), a oferta espera o fim do próximo turno da Infra. Projetorunnerdepende desse aviso, porque a subida dele nunca acontece dentro de um turno da Infra. - O comentário de
decide.tsque diz "não está semeado emINFRA_AUTONOMY_SEEDS" fica desatualizado nesta mudança, porque o arquivo é de outra frente em curso na mesma rodada; a regra em si (container_startfora dos tetos, capaz de chegar aauto_approve) não muda.