Pular para o conteúdo

Uso o Google Analytics pra entender o que é lido, compartilhado e clicado por aqui. Ele só carrega se você aceitar — nada de terceiros roda antes disso. Como o Google usa esses dados

A planta · aberta

Arquitetura — a planta, em aberto

Este site é o argumento. Esta página é a planta — como ele é construído, e como você construiria o seu.

Comecei o ano perdido

Um projeto que não estava indo bem, um monte de obrigações de catch-up nas ferramentas de IA, e a coisa foi degradando até o fim do ano. O Kiro já estava à mão fazia um tempo, e eu comecei o ano disposto a aprender a usar a ferramenta. E tem um detalhe que eu suspeito que muita gente sênior está vivendo e não diz em voz alta: eu tinha a ferramenta de desenvolvimento agêntico na mão — e mesmo assim me sentia de fora do hype.

Porque o problema não era a ferramenta; era onde eu ia usá-la. Naquele começo de ano, todo trabalho com IA de que eu estava perto se dividia em duas metades: a modelagem, que é forte, e o resto — systems integration, legado que não dá pra trocar, as complicações comuns de TI corporativa. É nessa segunda metade que eu passei dezoito anos, e é ela que não tem use case pronto pra você aprender — o caso tem que aparecer sozinho, no trabalho de verdade.

O caso que virou o jogo não foi este site. No início do ano, em janeiro, comecei a construir por fora um mecanismo de autenticação e autorização com regras de negócio densas, custom em Spring Boot e Spring Security, integrando sistemas legados. Eu jamais teria conseguido desenvolver aquilo sem uma agentic development tool — e não era só o prazo: eu dividia as responsabilidades de tech lead naquele projeto ao mesmo tempo. É essa a parte que a ferramenta comprou. Não velocidade de digitação: as duas coisas caberem na mesma semana. E nada me diverte mais que ver uma aplicação funcionando bonito — numa escala que sozinho eu não alcançava. Foi ali que eu vi uma coisa que não via fazia tempo: se o requisito é onde eu fico e o código é trabalhado por AI-DLC, projeto de software engineering pode ser mais audacioso. Não como previsão — como o que eu enxerguei naquele momento, com aquilo rodando na minha frente.

"Computer programming is an art, because it applies accumulated knowledge to the world, because it requires skill and ingenuity, and especially because it produces objects of beauty."

— Donald Knuth, 1974

Letras serifadas brancas em relevo sobre uma parede clara de museu, vista de ângulo, com o nome de Donald Knuth e o ano de 1974 abaixo, à direita.
Cinquenta anos antes de mim, alguém já tinha nomeado a parte de que eu mais gosto — e a parede fica no museu que existe pra registrar isso.

As férias foram em maio, em São Francisco e no Vale, e é dali que sai o resto desta página. Não teve lugar por onde eu passei sem alguma oferta de IA — no trem, na rua, na vitrine, no crachá de quem estava do lado. Voltei com a ideia do que fazer, e desde então toco isso em duas frentes: uma interna, no trabalho, com Kiro, e esta, pública, com Claude Code. Dois harness rodando o mesmo tipo de trabalho é o que me deixa separar o que é do modelo do que é do setup em volta dele.

Numa manhã eu peguei o Caltrain para sul — 8h57, próxima parada Palo Alto. O vagão era laptop aberto de ponta a ponta, loop rodando, gente trocando ideia em voz alta a caminho do trabalho. Não era evento, não era comunidade, não era nada combinado. O que eu concluí dali, e não o que eu vi: um monte de gente fazendo o mesmo tipo de trabalho, no mesmo lugar, na mesma hora — perto o suficiente para ouvir sem pedir e para responder sem marcar. Eu estive dentro disso por uma semana, em maio. No resto do ano, não estou.

Montagem de três fotos da mesma semana: à esquerda, eu e minha companheira numa calçada de pedra ao lado de uma fileira de bicicletas vermelhas, amarelas e verde-água, com árvores e céu limpo atrás; no alto à direita, o painel de bordo de um vagão do Caltrain, com "Southbound · 510 EXPRESS · 8:57a" e, abaixo, "NEXT STOP Palo Alto"; embaixo à direita, uma vitrine de museu com um iPhone de 2007 desmontado atrás de acrílico, suas peças etiquetadas — câmera, sensor de luz, microfone, alto-falante, SIM, vibracall — sob a legenda "iPhone · INTRODUCED IN JUN 2007".
Uma semana, em maio: o visitor center do Google em Mountain View, o Caltrain para sul das 8h57, a vitrine do Computer History Museum. Um trem, uma manhã, nenhuma medição. Não é dado; é o que eu vi.

Fora daquela semana o vagão não existe, e é ele que a frente pública substitui — tem uma razão para ela existir em vez de um caderno. Há opções de configuração demais — qual harness, quais hooks, que persona, que gate, qual modelo — e ninguém tem sessões suficientes para testar todas sozinho. Trocar a experiência de cada um usando IA é o que vai acelerar esse aprendizado, e por isso o que está aqui é o setup inteiro, não só a conclusão a que ele chegou.

O que o requisito exigia, e a arquitetura que ele justificou

O requisito desta frente pública é curto: publicar conteúdo, em dois idiomas, com a construção inteira em aberto. É ele que decide a arquitetura, a conta, e o resto desta página.

E ela não foi construída enxuta. Foi construída inteira e depois cortada: havia uma plataforma com backend — BFF em Lambda, DynamoDB, Cognito, SES —, um Lambda@Edge renderizando imagens OG a cada requisição, um serviço de unfurl de links, GitFlow com staging e produção, e um PWA offline-first. Um banco sem nada para guardar. Auth sem ninguém para autenticar. Um staging para um site cujo revert é um merge. Cada uma era defensável quando foi decidida, e nenhuma sobreviveu à pergunta "para que isso serve, aqui" — e cada reversão está registrada junto com a decisão que a substituiu: 0025, 0026, 0027, 0028, 0029.

O corte não é o assunto desta página; ele é a consequência. O que sobrou são três coisas — um site estático, um plugin de dev-loop e um runtime de agente — e elas não são camadas de um mesmo sistema: cada uma existe sem as outras duas. O site roda sem o plugin. O plugin instala em qualquer repositório. O runtime não é meu. O que fica no meio é a única coisa que nenhuma das três entrega sozinha — e é ali que mora o jeito como eu construo isto, que é o jeito como eu quero ser contratado pra construir: desenvolvimento AI-native com o rigor de SDLC que a maior parte do trabalho com IA dispensa, num loop construído sobre Context & Harness Engineering, com AI-DLC rodando dentro dele.

Os três pilares, e o que fica na interseçãoTrês círculos do mesmo tamanho, sobrepostos, com uma interseção comum no centro. O primeiro círculo é a solução, o repositório tadeumendonca-io, e dentro dele estão a SPA em React com Vite e TypeScript, o Terraform que provisiona CloudFront e S3, o pipeline com os gates e o deploy, e o conteúdo em markdown no próprio repositório. O segundo é a customização do harness, o repositório tadeumendonca-skills, e dentro dele estão as personas no diretório agents, os hooks registrados em hooks.json, a biblioteca de skills no diretório skills, os seis comandos que uma pessoa digita no diretório commands — autonomy, blueprint, new-issue, sprint-planning, sprint-review e sprint-retrospective — e os ADRs de metodologia. O terceiro é o runtime do harness, o Claude Code, e dentro dele estão o orquestrador e os subagentes, os seis eventos de hook que ele dispara — PreToolUse, UserPromptSubmit, SessionStart, Stop, SubagentStart e SubagentStop —, a política de permissões e as ferramentas com o MCP. No centro, onde os três se sobrepõem, está escrito Context & Harness Engineering. A afirmação do desenho é essa: nenhum dos três círculos sozinho é a disciplina, ela é o que existe onde os três se encontram.A soluçãotadeumendonca-ioSPA React · Vite · TSTerraform: CloudFront, S3Pipeline: gates, deployMarkdown no repositórioA customizaçãotadeumendonca-skillsPersonas em agents/Hooks em hooks.jsonA biblioteca de skillsComandos que você digitaADRs de metodologiaO runtimeClaude CodeOrquestrador, subagentesSeis eventos de hookPolítica de permissõesFerramentas e MCPContext & HarnessEngineering
Os três pilares, e o que fica na interseção

O que fica na interseção é o trabalho de verdade: decidir o que o harness barra, o que ele aconselha e o que ele só documenta — e depois provar que o inventário disso continua verdadeiro.

O lançamento mostrou o resultado. A vitrine mostra a máquina.

Pilar 1 · a solução

Uma SPA totalmente estática — React + Vite + TypeScript — servida a partir de S3 atrás do CloudFront, com uma pequena CloudFront Function reescrevendo URLs limpas. Sem servidor, sem banco, sem auth. O conteúdo é markdown no próprio repositório, e cada rota publicada é prerenderizada no build, nos dois idiomas — um artigo pode ficar retido antes de ser publicado, e um retido é compilado no bundle sem nunca ser prerenderizado.

(→ ADR-0002 totalmente estático / sem backend · ADR-0013 S3 + CloudFront)

De ponta a ponta, com os bastidores junto — e o que interessa nesse desenho é o que não está nele:

As raias e os tiers — o que o leitor encontra, e o que mantém isso de péUma grade que se lê da esquerda para a direita, com quatro colunas — público, dispositivos, frontend e infra cloud — e três raias empilhadas dentro delas. A raia de cima é a audiência, o que o leitor encontra no site: o público chega por um link, o dispositivo pede uma URL, e o que volta é HTML pré-renderizado nos dois idiomas, com a SPA React assumindo depois e nenhum terceiro carregado antes de o leitor autorizar — GA4 só com consentimento, YouTube só no clique. Essa página vem inteira da coluna de infra cloud: Route 53, ACM, CloudFront com a função de reescrita, e um bucket S3 privado que só responde àquela distribuição. Entre o frontend e a infra não existe coluna de backend, e essa ausência é a afirmação do desenho: não há tier de aplicação, porque o requisito nunca pediu um, e nada meu roda a cada requisição. As duas raias de baixo são os bastidores, o que ninguém vê e que mantém a operação de pé. A raia de produção: eu abro a Issue e ratifico o irreversível; o GitHub é o console, de qualquer dispositivo, e uma menção a claude dispara o agente dentro do CI sem nenhuma máquina minha ligada; o repositório e o build carregam as personas e os hooks que negam a chamada, e produzem os dois idiomas e o PDF do CV; o Terraform aplica a infraestrutura só pelo pipeline, com estado no Terraform Cloud. A raia de operação é a mais fina, e é fina por constatação e não por desenho: o que eu meço é GA4 depois do consentimento, então quem recusa não entra na conta; nada observa o dispositivo do leitor, sem RUM, sem log de acesso e sem monitor de disponibilidade; depois de cada deploy um smoke roda contra o apex vivo e confere que a função publicada é a deste repositório; e sobre a conta inteira há um orçamento que avisa por e-mail, que é o único vigia contínuo que existe.INFRA CLOUDFRONTENDDISPOSITIVOSPÚBLICOabre um linkpede uma URLvem inteira daquie entre as duasnão backend nenhumabre o trabalhodispara o loop deagentespublica na origemAUDIÊNCIAleitores, recrutadoresquem chegou por umlinkBASTIDORES · produçãoeu abro a Issuee ratifico oirreversívelBASTIDORES · operaçãoo que eu meço:GA4 depois doconsentimentoquem recusa não émedidonavegador, celulare os scrapers deLinkedIn e Xpedindo a mesma URLGitHub, de qualquerdispositivoIssue, comentário, PRuma menção a clauderoda o agente no CInada observa odispositivosem RUM, sem log deacessosem monitor dedisponibilidadeHTML pré-renderizadonos dois idiomasa SPA React assumedepoisterceiro nenhumantes de o leitorautorizaro repositório e obuildpersonas em agentse hooks que negam achamadaos dois idiomas e oPDF do CVdepois de cada deploysmoke contra o apexvivoe a função no arconferida contra esterepoRoute 53 · ACMCloudFront com afunção de reescritaS3 privado, por OACTerraform pelopipelineplan no PR, apply nomerge, via OIDCestado no TerraformCloudsobre a conta inteiraum orçamento avisa pore-mailo único vigia contínuo
As raias e os tiers — o que o leitor encontra, e o que mantém isso de pé

A ausência é deliberada, não uma lacuna. Um desenho desses, para um sistema assim, costuma ter uma coluna de aplicação, um banco e integrações internas entre o frontend e a infra; aqui não há coluna nenhuma ali, e o que o leitor pede vem inteiro de um bucket. O único terceiro em tempo de execução é a analytics, e ela depende de consentimento. E "sem backend" levanta uma pergunta antes das outras — como um crawler enxerga isto —, cuja resposta é que nada precisa ser renderizado para ele enxergar: o que ele pede vem como HTML completo, com as tags OG dentro, direto de um arquivo estático. Sem SSR, sem renderização na borda — a função de reescrita da borda roda a cada requisição de página e mexe na URL, nada mais.

O limite viaja junto com a afirmação, porque é a parte que um leitor consegue derrubar: uma URL que não existe responde 200, não 404 — e o que volta é a landing page, com as tags OG da própria landing, sob um endereço que nunca existiu. O CloudFront mapeia 403 e 404 para /index.html, que é o que deixa uma SPA funcionar em rotas profundas e é uma troca real, não um detalhe. Já mordeu aqui uma vez: um desvio de caminho jogou as imagens de OG por artigo nesse mesmo fallback, e cada uma respondeu 200 text/html a todo scraper que a pediu.

A única lógica que roda entre um leitor e um arquivo é essa função: dez linhas executáveis, com testes unitários próprios e uma verificação pós-deploy de que a função no ar continua sendo a deste repositório. E o bucket não é público em nenhum sentido: só responde a s3:GetObject vindo desta distribuição.

(→ ADR-0004 render no build, sem SSR · ADR-0005 toda URL OG-completa · ADR-0033 analytics dependente de consentimento · iac/frontend.tf a distribuição e as policies)

R$ 34,31 por mês

Esse número mede o que este site acrescentou, não aquilo de que ele depende, e mede aquilo em que ele roda, não aquilo com que eu o construo. Dizer "custo quase zero" é a coisa mais fácil desta página, e a mais fácil de ninguém conferir — então segue a conta, com as linhas de hospedagem lidas do custo diário da conta no fim de julho de 2026 e o registro lido da tabela do registrador. Nenhuma estimada. A fatura da AWS é em dólar, e o câmbio usado aqui é R$ 5,222/USD, o fechamento de 14 de agosto de 2026 — fixo no texto, não buscado a cada build, para que o número publicado só mude quando alguém decidir mudá-lo. É com ele que você desfaz qualquer linha abaixo de volta ao valor da fatura:

  • O domínio — R$ 370,76/ano pelo .io, uma cobrança anual que cai num mês só. R$ 30,91/mês amortizado. Escolhi o .io por branding, não por custo: é a razão honesta, e a única linha daqui que você pode recusar.
  • Route 53 — R$ 2,61/mês, fixo. A hosted zone, com ou sem visitante.
  • S3 — cerca de R$ 0,78/mês, e são escritas de deploy, não leituras.
  • CloudFront — na prática R$ 0,00 com esse volume: o tráfego deste site não chega a arranhar o piso do serviço.

Some as linhas e você chega a R$ 34,30, um centavo abaixo do título. Cada linha é arredondada por conta própria, e o total lá em cima é a conversão dos USD 6,57 — que não são uma fatura só: USD 0,65 saem do custo diário da AWS e USD 5,92 são o .io amortizado, da tabela do registrador. Os dois nascem em dólar, e o real desta seção inteira é derivado deles.

Fora da AWS o critério é o mesmo. GitHub Team e Claude Max são pagos e ficam fora do total — a assinatura do GitHub Team é anterior ao site, embora a carga de CI em cima dela seja inteiramente dele; GitHub Actions e SonarCloud são zero porque os repositórios são públicos — propriedade dos repositórios, não do plano — e Terraform Cloud é zero porque a infraestrutura é pequena. E o iCloud+ é a linha que mostra o critério sendo aplicado em vez de anunciado: ele é anterior ao site, mas carrega o e-mail com domínio próprio no apex e o iac/email.tf provisiona os registros MX, DKIM e SPF dele — então não é adjacente a esta infraestrutura, está dentro dela. (→ ADR-0016)

Fora do total ficam também todas as minhas horas: R$ 34,31 por mês é o que custa manter isto no ar, não o que custou construir. Em pessoas, custou uma — fins de semana, em paralelo com consultoria. E a mesma leitura mostrou cerca de R$ 66,84 por mês que o site não estava usando: web ACLs de WAF e IPv4 públicos ociosos, esquecidos quando o backend foi aposentado. Descobri lendo a fatura, o que é tarde — infraestrutura que você para de usar não para de cobrar —, e quem vigia agora é um orçamento em iac/budget.tf deliberadamente não escopado às tags deste projeto: se fosse, só enxergaria gasto que este repo criou, e este era justamente do tipo que ele não criou.

Pilar 2 · a customização

A parte interessante não é a stack — é como ela é construída: agent-led verification, human-residual. O agente prova o "pronto" com gates mecânicos e evidência real (lint, tipos, testes ≥85%, build verde, SonarCloud, E2E funcional, um revisor de contexto fresco); o humano fica com as decisões irreversíveis e arquiteturais. Esse loop vive num plugin à parte — tadeumendonca-skills —, então é uma metodologia que você pode adotar, não algo sob medida só pra este site.

Como o trabalho atravessa os tiers de agente — e onde eu entroUm fluxo de cima para baixo em três tiers, com o dono nas duas pontas e uma caixa grande no meio que roda sem ele. No topo estou eu: sou o único que gera demanda, e abro a Issue. O tier 1 é a admissão, e não é uma caixa só: são três raias, e o tipo da issue decide em qual ela entra. Uma issue de produto fecha pelas duas lideranças que discordam por construção, product-lead e tech-lead. Uma de conteúdo fecha por product-lead sozinha, julgando se vale a pena escrever aquilo — não como será escrito. Uma de loop, que é a maquinaria em si, fecha por agents-lead sozinha — nunca em par, e sem exceção nenhuma —, porque a maquinaria é o objeto dela e de mais ninguém. As três raias desembocam no mesmo rótulo ready, que é o artefato que diz que a descrição foi fechada — e numa issue de loop esse rótulo é meu, só eu ponho. Do ready para baixo começa o trecho AFK, o que roda sem perguntar quando eu mando drenar a fila: tudo ali dentro passa pelo orquestrador, que é a sessão principal e o eixo por onde toda persona é acionada, que commita e empurra, e que nunca faz merge e nunca decide o irreversível — um hook recusa as duas coisas vindas da sessão principal. Na edição do repositório não há tranca nenhuma: o hook que recusava isso foi apagado, então o que mantém uma edição passando pela persona dona daquilo virou uma regra, e não um mecanismo. Ao lado dessa regra fica o scrum-master, desenhado fora do caminho: um profile que não tem ferramenta nenhuma — não aciona, não edita, não roda comando, não põe label — e que ordena a fila elegível e nomeia num registro quem deveria agir em seguida. São três partes, e a terceira é a que precisa ficar: nada impede a edição, o registro nomeia quem deveria ter agido, e o registro é escrito pela própria parte que ele restringe e não é lido por nada. Isso é detecção, autodeclarada, e não prevenção. Ele aciona o tier 2, o build, também dividido por tipo: developer no produto, content-writer no conteúdo, agents-lead no loop, construindo o que ele mesmo acabou de estressar. No conteúdo o build é um par, e não um perfil só — é por isso que aquela caixa carrega dois nomes: o content-reviewer lê o rascunho contra a mesma régua com que ele foi escrito, no máximo duas rodadas, e o que ele barra é um rascunho, nunca um merge. Quem aciona os dois é o orquestrador, como todo o resto aqui dentro — um não passa trabalho para o outro direto. Sai dali uma merge request por story, que chega ao tier 3 — contexto fresco, sem viés de autoria — onde quality-assurance verifica a Definition of Done e, à parte, se aquilo pode quebrar a produção; é o único que pode fazer merge. A raia de loop chega na mesma caixa em vez de passar por fora, e ali ela responde por mais, e não por menos: numa mudança na própria maquinaria, o quality-assurance confere a mesma Definition of Done e a mesma pergunta sobre produção, e ainda exige que o agents-lead tenha deixado o marcador de veredito dele antes de poder fazer merge — é um revisor a mais que precisa ter passado, não uma revisão que deixa de acontecer. O que é classe segura ele mesmo mergeia, e o merge é o deploy. O que é classe de fronteira — infraestrutura, as regras do próprio loop, publicar na minha voz — sai do trecho AFK e volta para mim, e só depois do meu go é que sobe. Depois que existe uma merge request, recusa é um canal só: o gate pedindo mudanças e o meu no-go caem na mesma caixa de devolvido, e ela volta pelo orquestrador, nunca direto para quem construiu. Nove caixas de persona, oito nomes, por dois motivos diferentes: product-lead e agents-lead aparecem duas vezes cada, porque o mesmo perfil é acionado em momentos diferentes; e uma caixa carrega dois nomes em vez de um porque a raia de conteúdo é um par. E há um canal tracejado meu com o orquestrador, para quando algo trava — existe o tempo todo e não fica no caminho. A afirmação do desenho é essa: entre o rótulo ready e o merge não há nenhum humano no caminho, e eu apareço só nas duas pontas — o que atravessa aquele trecho sozinho é apenas a classe segura.AFK · do ready ao merge, nada no caminho é humanoTIER 1 · conteúdoTIER 1 · produtoTIER 1 · loopprodutoprodutoconteúdoloopprodutoconteúdoloopacionada peloorquestradorclasse seguraclasse de fronteiragomudançasno-goHITL · EUo único que gerademandaabro a Issueagents-leadsozinha a maquinariaé o objeto delaproduct-leadtech-leaddiscordam porconstruçãoproduct-leadsozinha se vale apena escreverO TIER 1 FECHA AQUI ·o rótulo readya descrição fechadae numa issue de loop, eu ponhoORQUESTRADOR ·a sessão principalaciona toda persona,commita, empurraum hook recusa a ele omerge e o push natrunkna edição dorepositório nada orecusascrum-master · não temferramenta nenhumaordena a fila e nomeiaquem age em seguidaum registro que elemesmo escreveninguémTIER 2 · BUILDdeveloper produtoTIER 2 · BUILDcontent-writer comcontent-reviewerconteúdono máximo duasrodadas, contra amesma réguaTIER 2 · BUILDagents-lead loopconstrói o queestressouMERGE REQUEST · umapor storyTIER 3 · GATE contexto fresco, semviés de autoriaquality-assurance · oúnico que pode fazermergetoda raia aDefinition of Done,e se isso quebra aproduçãoloop e o marcador deveredito doagents-leaddevolvido um canalde voltamerge em main = odeployHITL · EUclasse de fronteira:irreversível,arquiteturalgo / no-go
Como o trabalho atravessa os tiers de agente — e onde eu entro

Eu apareço nas duas pontas, e são trabalhos diferentes: no começo abrindo a Issue, e no fim, só no que é classe de fronteira, decidindo se aquilo sobe. Entre uma ponta e outra não há humano nenhum no caminho. E o desenho afirma uma coisa mais estrita do que "no plano": eu sou a única origem de demanda — nada entra na fila por conta própria —, e quem fecha a admissão é o rótulo ready, que é o artefato que diz que a descrição foi fechada; do ready para baixo, só a classe segura atravessa sozinha. Nenhuma raia passa por fora do gate, nem a que constrói o próprio loop. Uma mudança na maquinaria é escrita pelo agents-lead e chega na mesma caixa, e ali ela responde por mais, e não por menos: a mesma Definition of Done, a mesma pergunta sobre produção, e em cima das duas uma exigência que nenhuma outra raia tem — que o agents-lead tenha deixado o marcador de veredito dele antes de o gate poder fazer merge. É um revisor a mais que precisa ter passado, não uma revisão que deixa de acontecer. E o custo disso, já que o resto desta página assume os seus: quem decide que uma mudança é segura é o mesmo tipo de coisa que escreveu a mudança. Classifique uma errado e ela pega o caminho vazio. O que torna isso aceitável aqui é raio de impacto, não confiança — é um site estático, e reverter é um merge.

(→ ADR-0003 trunk-based, ambiente único · ADR-0018 os gates de CI)

Do que o harness é feito

Do que o harness é feitoA afirmação vem primeiro, porque é pra ela que a grade existe. De tudo que o plugin exporta, exatamente um tipo consegue recusar: só os hooks RECUSAM, e só em dois dos seis eventos em que eles se registram. As personas ACONSELHAM, e as skills e os comandos só DOCUMENTAM. Quatro tipos contra três classes de força dá doze células; cinco têm conteúdo e sete estão vazias, e as sete vazias são o argumento. Agora o inventário de onde a afirmação é lida. As raias são os 14 registros de hook no hooks.json, escritos por 13 scripts porque o preflight está registrado duas vezes; as 8 personas do diretório agents; as 15 skills do diretório skills; e os 6 comandos do diretório commands. Na coluna que nega há uma célula só, e ela tem 3 registros em dois eventos de formatos diferentes: o permission-guard no matcher Bash e o mcp-guard no matcher das ferramentas MCP, os dois no PreToolUse, que recusam uma chamada de ferramenta antes dela rodar; e o preflight no UserPromptSubmit, que recusa o próprio prompt, antes do turno começar. Persona, skill e comando não têm célula nenhuma ali. Na coluna do meio também há uma só: as 8 personas — agents-lead, content-reviewer, content-writer, developer, product-lead, quality-assurance, scrum-master e tech-lead — que ACONSELHAM, e isso é uma afirmação sobre o julgamento delas, não sobre a cadeira, porque uma lente que ninguém aciona falha em silêncio. A terceira coluna, a dos componentes que só DOCUMENTAM, tem três células: as 15 skills, que é o que o modelo aciona sozinho; os 6 comandos, que é o que uma pessoa digita; e os outros 11 registros de hook — 4 no SessionStart, 2 no SubagentStart e no SubagentStop, e 5 no Stop. Lidas as duas colunas juntas, o preflight aparece nas duas, porque está registrado duas vezes com uma classe diferente em cada evento, e é isso que significa o desenho ser derivado e não desenhado: ninguém escrevendo à mão desenharia um script em duas colunas da mesma grade.DOCUMENTA11 hooks · nenhumrecusa onde está4 hooks · SessionStartpreflight ·session-wip ·session-plugin-version· cadence-notice2 hooks ·SubagentStart eSubagentStopdispatch-metrics-start·dispatch-metrics-stop5 hooks · Stopzombie-loop-detect ·orchestrator-tool-censuspremature-pr-link-detect ·owed-pr-link-detectclosure-artifact-guardStop e SubagentStoppoderiam barrarnenhum dos seis barra nenhuma persona15 skills · skills/o que o modelo aciona6 comandos · commands/o que você digitaautonomy · blueprint ·new-issuesprint-planning ·sprint-reviewsprint-retrospectiveACONSELHA nenhum hook8 personas · agents/agents-leadcontent-reviewercontent-writerdeveloperproduct-leadquality-assurancescrum-mastertech-lead nenhuma skill nenhum comandoNEGA3 hooks · doiseventos, dois formatos2 hooks · PreToolUserecusam uma chamadamatcher Bash ·permission-guardmatcher mcp__.* ·mcp-guard1 hook ·UserPromptSubmitrecusa o promptpreflight nenhuma persona nenhuma skill nenhum comandotipo · o que o -skills exportahooks · 14hooks.json13 scripts um estáregistrado duas vezespersonas · 8agents/skills · 15skills/comandos · 6commands/
Do que o harness é feito

Dos componentes do próprio plugin, exatamente um tipo consegue te barrar, e essa é a versão honesta do convite a adotar: três dos catorze registros no hooks.json devolvem uma negativa antes do ato, e o ato não acontece. E eles fazem isso em dois formatos diferentes, que é a parte que vale ter: o permission-guard no Bash e o mcp-guard nas ferramentas MCP estão no PreToolUse e recusam uma chamada de ferramenta; o preflight está no UserPromptSubmit e recusa o prompt, antes do turno começar. os três hooks de PreToolUse — o permission-guard e o wip-guard no Bash, e o orchestrator-write-guard nas ferramentas que escrevem arquivoriscado: daquelas quatro afirmações, sobrou uma. Dois desses guards foram apagados no plugin e o mcp-guard nunca tinha aparecido nesta página. E nada aqui pegou isso — que é a perna atrasada descrita dois parágrafos abaixo, chegando como instância e não como ressalva: o desenho concordava com o manifesto, então aquela checagem ficou verde com os dois lados desatualizados juntos, e um apagamento de lá não dispara nada deste lado. E o preflight está desenhado em duas colunas da grade acima, porque está registrado duas vezes com uma classe diferente em cada evento — que é a única coisa aqui que ninguém escrevendo à mão teria desenhado.

Os outros onze registros só reportam, e não pelo mesmo motivo — e essa distinção esta página te deve, não dá pra arredondar. Cinco deles estão em eventos que não recusam nada, o SessionStart e o SubagentStart, então esses cinco não conseguiriam barrar nem se quisessem; e repare que o motivo é o evento, e não a ausência de uma chamada de ferramenta, porque o UserPromptSubmit também não tem chamada nenhuma na frente e recusa assim mesmo. Os outros seis conseguiriam. O Stop consegue barrar o fim do turno e o SubagentStop consegue barrar a parada de um subagente, e todos esses scripts escolhem não barrar: toda saída deles é de sucesso. O zombie-loop-detect percebe, um turno atrasado e uma vez por sessão pra aquele head, que tem branch parado em cima de um veredito do gate que ninguém tratou; o dispatch-metrics-stop registra os números do acionamento e sai da frente; o orchestrator-tool-census nomeia o que o orquestrador fez com as próprias mãos, as chamadas que escrevem e postam separadas das que só leem, e não decide nada sobre isso. Seis hooks escolhendo não ser mecanismo dizem mais sobre este harness do que um dizia. E as personas aconselham — o julgamento delas não é verificado por nada, e o guia deste repositório diz com todas as letras que uma lente que ninguém aciona falha em silêncio. Essa é a garantia que o loop dá — e ela vale exatamente o que valer o inventário do desenho acima.

E o inventário desse desenho é conferível — é essa a segunda garantia, e ela é de outra natureza. Renomeie uma persona no plugin e o build deste repositório fica vermelho. O diagrama acima é escrito à mão: um teste compara o desenho, nó a nó e contagem a contagem, com um manifesto versionado, nas duas edições; e um job de CI compara esse manifesto com a árvore viva do plugin. A diferença entre desenhar um harness e provar que o desenho ainda é ele é exatamente essa, e ela é mecânica. E ela tem duas pernas, com limites diferentes, ditos aqui e não depois. Do desenho para o manifesto, a comparação inclui a classe de força de cada uma das doze células, nas duas edições: dar denies a uma persona no manifesto deixa isto vermelho, e é essa a afirmação central da grade. Do manifesto para a árvore viva do plugin, a checagem chega tarde, porque nada deste lado enxerga um merge de lá, e a classe de cada componente sai de uma regra sobre o formato dele — em que evento um hook está registrado —, não de uma leitura do que o script faz: relendo o manifesto, o que se confere é que a classe é um valor legal, não que ela é verdadeira daquele componente.

E é por isso que eu chamo isto de uma coisa, e não de outra. AI-DLC não é meu — é o nome que a AWS deu a um ciclo de entrega cujas etapas são executadas e verificadas por agentes; eu adoto, não inventei, e ele roda dentro disto, não ao lado. Context & Harness Engineering é a afirmação que eu faço, e as duas palavras são duas coisas diferentes de construir. O contexto é o que um agente lê antes de agir: as skills, os registros, os briefs que dizem a ele o que já foi decidido, para que ele não decida de novo e diferente. O harness é o que fica entre ele e o ato: os hooks, os gates, as personas que leem o que ele produziu — e a grade acima é o inventário de quais deles conseguem de fato barrá-lo. Nenhuma das metades é a disciplina sozinha — contexto sem nada que o sustente é conselho, e harness sem nada para ler é um muro — e construir, versionar e provar as duas é o trabalho. Adotar uma metodologia não custa nada dizer — e é justamente por isso que dizer não vale nada. Essa aqui é paga, e o pagamento está no parágrafo acima: um build que quebra quando o inventário deixa de ser verdade. É a mesma régua de agent-led verification que o resto desta página aplica ao código, virada para a metodologia: quem afirma é quem produz a evidência.

(→ ADR-0043 o inventário ancorado no plugin)

Oito personas, contra quem cada uma argumenta — e o que cada uma carrega ao ser acionada. A última coluna é o preload de cada brief: as skills que entram na sessão da persona antes de ela ler a primeira linha da tarefa.

quemo que é delecontra quem argumentaque skills carrega ao ser acionada
product-leado leitor, valor, ordem, tamanho da fatia — e posicionamento, voz, e a verdade de qualquer coisa publicada fora da via de contento tech-lead; e num diff de product ou loop barra um merge diante de uma afirmação publicada que não é verdade — por convenção, não por hook. Em content esse veto acabou em 2026-09-03: lá ele fica com a intake e nada mais, e nenhuma persona tem bloqueio de copy nessa viaagents-configuration · engineering-standards · definition-of-ready · shell
tech-leadarquitetura, medição, sequenciamento — e é ele que escreve os registros de decisão de produto e de sistema; os do próprio loop não são dele, são do agents-leado product-lead, de propósito: produto-e-mercado e sistema são otimizações genuinamente diferentesagents-configuration · engineering-standards · definition-of-ready · documentation-standard · devops · shell
developera fatia inteira — aplicação, infraestrutura, pipeline, e os testes escritos juntoninguém. Ele constrói, e é pra ele que o gate está apontadoagents-configuration · engineering-standards · code-review · quality-gates · devops · shell
quality-assurancea entrega contra a Definition of Done, e, à parte, se a mudança pode quebrar a produçãoo developer, nos dois eixos numa passada só — e é o único que o hook de permissão deixa fazer mergeagents-configuration · engineering-standards · definition-of-done · quality-gates · devops · shell
content-writerredige artigos, texto do site e a linguagem dos posts de rede social na voz do dono — molda, corta, estrutura e traduz uma experiência que ele já tem, nunca origina umao content-reviewer, e é só isso: ele lê o rascunho contra a mesma régua com que ele foi escrito, e desde 2026-09-03 o product-lead não roda a lente de copy nesta trilha — o papel dele em content é a admissão do item, e nada alémagents-configuration · engineering-standards · shell · published-voice · content-publishing
content-reviewerlevantar a régua de um rascunho antes de ele chegar a mim — no máximo duas rodadas contra o published-voice, e ele não barra nada — conserta o rascunho ali mesmo, por dois motivos e só dois: quando consegue citar uma cláusula dessa skill, ou quando a afirmação é falsa contra a fonteo content-writer, e este é o primeiro par de verdade do elenco: tudo que não cai em nenhum dos dois motivos volta rotulado como opinião descartável, e o texto fica como estáagents-configuration · engineering-standards · shell · published-voice · content-publishing
agents-leada maquinaria em si: hooks, permissões, instruções das personas, skills e comandos, o plugin — e é ele que escreve os registros de decisão sobre o loop, a outra metade da divisão acimaeu — e esse é o caso interessante: o contraponto dele não é outra persona, é a única cadeira deste loop que não tinha com quem discutiragents-configuration · engineering-standards · documentation-standard · definition-of-ready · devops · shell
scrum-masterque os ritos aconteçam, na ordem, e que nada seja pulado — ele ordena a fila elegível e nomeia, num registro, qual profile age em seguidaninguém, e ele não tem ferramenta nenhuma: não aciona, não edita, não roda comando e não põe label, então o que ele produz é um registro que a sessão principal executa — e que nada lêagents-configuration · engineering-standards

Duas coisas dessa última coluna valem ser ditas. agents-configuration e engineering-standards estão nas oito — é o preload universal: entender o próprio loop, e os padrões de engenharia contra os quais ele roda, não é assunto de especialidade nenhuma. A shell — a disciplina de arquivo e comando — está em sete das oito, e a que fica de fora é a scrum-master, que não tem nem Write nem Bash, então a regra não teria o que governar ali. E só 11 das 15 skills da biblioteca são pré-carregadas por alguém: as quatro que ninguém carrega — backend, cloud-infrastructure, frontend e planning-poker — só chegam numa sessão se o modelo as encontrar sozinho, pela descrição delas.

E esta tabela é escrita à mão — a coluna nova inclusive. Os nomes de persona do desenho lá em cima são comparados com o manifesto e com a árvore viva do plugin, então aposentar uma persona deixa um build daqui vermelho. Aqui, nada compara coisa nenhuma: o check-harness-drift confere nomes e contagem de personas, e não confere quais skills cada uma carrega. Basta alguém mudar o bloco skills: de um brief para esta coluna passar a mentir no dia seguinte, sem nenhum sinal. E um papel mudando de mãos também não deixa nada vermelho: o que essa checagem compara de uma persona é o nome dela, e quantas personas existem — nunca o que uma linha afirma que ela faz. A autoria dos registros de decisão se dividiu entre duas destas linhas e build nenhum percebeu; aquelas duas células foram corrigidas à mão, como todo o resto desta tabela.

Uma linha corre por baixo de tudo isso, e ela é sobre consentimento, não sobre ferramenta. Tudo o que está no desenho acima é versionado — hooks, personas, skills, comandos — e isso é deliberado: é o que faz o trabalho ser reproduzível, de modo que um fork herde o piso em si, e não uma descrição dele. O que não é versionado, e não vai ser, é a configuração que faz o operador ser alcançável. Toda sessão que eu rodo tem Remote Control ligado desde o primeiro segundo — remoteControlAtStartup, que abre uma ponte para eu acompanhar a sessão local viva e aprovar as chamadas de ferramenta dela do meu celular. Não é o sandbox na nuvem que o Claude Code também oferece, e os dois não se substituem: dentro de uma sessão na nuvem o Remote Control se declara indisponível, porque ali não existe a sessão local que ele existe para alcançar. Essa chave mora em ~/.claude/settings.json, na minha máquina, no único arquivo que este repositório não consegue publicar.

Essa assimetria parece um buraco no harness, e a ferramenta já tinha resolvido a discussão — eu é que nunca tinha lido. Então eu li: não o nome da chave, que não é especificação, mas o binário publicado. Configuração com escopo de repositório não consegue ligar o Remote Control. Comite "remoteControlAtStartup": true no .claude/settings.json de um projeto e a CLI ignora e diz por quê — "repo-scoped settings cannot enable Remote Control; set it at user scope". A única coisa que um repositório consegue fazer com essa chave é colocá-la em false, e essa direção é respeitada na hora, à frente do que prefira a pessoa que está rodando a sessão. Ligar é decisão de uma pessoa; desligar é decisão de um projeto; e só a segunda viaja através de um clone.

O que é a mão certa para um repositório cuja premissa inteira é ser forkado. Comitar o true teria entregado uma capacidade de sessão a todo mundo que clonasse isto e à CI, sem que nenhum deles escolhesse — e, pelo parágrafo acima, nem sequer teria funcionado: uma linha que não faz nada parecendo que faz é exatamente o defeito que esta página não para de nomear. Então o passo de configuração está no README, com os dois métodos e o que cada um custa. Esta página guarda a linha, não as instruções.

Pilar 3 · o runtime

O orquestrador é a parte do harness que você não consegue instalar. Ele não está em nada do inventário acima — nem na grade de componentes, nem no manifesto, embora o fluxo dos tiers o desenhe bem no meio do trecho AFK — e é a sessão principal: o contexto que lê uma Issue, decide qual persona acionar e pesa o que volta. O ator não é componente do plugin, a política dele em parte é, e o que você põe é o contexto que roda aquilo. É também a parte contra a qual as fronteiras acima foram desenhadas: a coluna do meio não consegue afirmar nada além de que uma persona aconselha, e o motivo de não conseguir é que nada obriga um acionamento — o que nomeia o modo de falha sem nomear quem aciona. Quem aciona é ele, e uma lente que ele esquece é uma lente que ninguém rodou.

E o contexto dele acaba. É isso que um subagente compra: ele lê, roda, erra e refaz dentro da sessão dele, e o que chega ao orquestrador é a conclusão. Uma tarefa custa ao orquestrador o veredito, não a execução, e por isso a única alavanca real deste harness é o tamanho do veredito, girada escrevendo as instruções de cada persona. Medi uma vez, na sessão deste repositório, em 7–8 de agosto de 2026, lendo as transcrições: o que ficou dentro dos subagentes foi mais de uma ordem de grandeza maior do que o que voltou. E a economia tem teto — mesmo assim, os vereditos que voltaram foram uma fatia grande de tudo que o orquestrador consumiu vindo de uma ferramenta. Não é fuga: aquela sessão compactou duas vezes de qualquer jeito. O número não é publicado porque a fonte é uma transcrição de sessão privada, que gate nenhum alcança.

E o chão embaixo dele se mexe. Esta é a parte que o resto desta página não tem: o site eu controlo, o plugin eu controlo, o runtime não. Quem o produz publica mudança o tempo todo, e cada modelo novo muda qual configuração ainda faz sentido — não porque a configuração tenha ficado errada, mas porque ela estava compensando uma fraqueza que sumiu.

Não é dedução minha. Quando o Opus 5 saiu, o time do Claude Code apagou mais de 80% do próprio system prompt — o do produto deles, não a configuração pessoal de alguém — e o modelo ficou melhor sem o andaime. E não como evento único: a cada upgrade grande de modelo é menos andaime necessário, então se apaga regra e se recoloca só onde o modelo ainda erra. Isso é um ciclo, não uma faxina.

A tese que vem junto é a que me interessa, e ela é dura: modelos de fronteira estão sendo limitados por produtos construídos para os modelos fracos de ontem, e a vantagem fica com quem põe esforço de engenharia em verificação, e não em instrução. É a pessoa que construiu a ferramenta dizendo isso — e o nome que eu dou a esse movimento, agent-led verification, é meu, não dele. Não estou citando isso de enfeite: é corroboração independente de uma escolha que eu já tinha feito, vinda de quem tem o dado que eu não tenho.

E é por isso que este loop é feito de hooks e de gates, e não de um prompt gigante explicando ao agente como se comportar. Instrução envelhece a cada modelo novo, e envelhece calada. Um gate, não: ele confere o resultado, e o resultado é a mesma coisa antes e depois do upgrade. Se o argumento acima estiver certo, a parte do meu harness que vai sobreviver é a que verifica — e a que manda é a que eu vou apagar.

Boris Cherny, que construiu o Claude Code, no canal do Y Combinator:

O registro de decisões É a documentação

O argumento clássico para ADRs é o humano do futuro: registre por que a decisão foi tomada, para que daqui a dois anos alguém não a desfaça sem saber o que estava em jogo. Aqui o argumento é outro, e é ele que decide o formato.

Num repositório onde quem desenvolve são agentes, o registro é contexto de inferência. O agente não tem memória do que foi discutido — ele tem o repositório, e é dali que infere o que fazer. Se a arquitetura que se formou ao longo do tempo não estiver ancorada em algum lugar do próprio código, cada mudança nova é decidida sem ela, e o resultado não é uma decisão errada isolada: é uma decisão nova que contradiz uma decisão que ninguém lembra de ter tomado. Por isso uma decisão revertida continua aqui e diz que foi revertida. Sem essa marca, o registro de uma arquitetura aposentada lê como instrução — que é a forma mais barata que existe de fazer um agente reconstruir aquilo que foi cortado de propósito. Com uma exceção, e é a única que existe: havia duas web ACLs de WAF e só a regional tem ADR — a da borda do CloudFront foi construída, foi cortada, e o registro dela é esta frase e não um arquivo.

É esse propósito que escolhe o formato, e não o contrário. MADR: contexto, as opções que estavam na mesa, a decidida, e a consequência. Um documento curto por decisão, um arquivo por decisão, tudo no mesmo repositório que o agente já lê — nada de wiki, nada de ferramenta à parte. O que um formato assim entrega para um leitor humano é rastreabilidade; o que ele entrega para um agente é o que ele precisa para não contradizer.

São 49 decisões — e o que é mecânico aqui é o índice, não este número. O índice é gerado a partir de docs/adr/, commitado como artefato e conferido no CI: acrescentar ou substituir uma decisão sem regenerar o índice deixa o pipeline vermelho, então o artefato e o diretório não têm como se separar. O 49 desta frase é digitado à mão: enquanto a tabela era renderizada aqui, ele vinha conferido de graça; cortá-la tirou essa amarração, e o que sustenta o número agora é o link abaixo, a um clique de você contar. As linhas não estão impressas aqui de propósito — esta página aponta o detalhe canônico em vez de repeti-lo, e uma cópia de 49 linhas seria a própria regra sendo quebrada na única seção que existe para defendê-la.

(→ a biblioteca de decisões · ADR-0001 enxuto por design)

Replique para o seu contexto

Está tudo público — dois repos, sem segredos.

tedeuxx/tadeumendonca-ioTypeScriptEste site e o Terraform que o serve — uma SPA estática em S3 + CloudFront, prerenderizada nos dois idiomas, com a biblioteca de decisões que registra as decisões que sustentam peso e o que cada uma custou. Ver no GitHub tedeuxx/tadeumendonca-skillsShellO plugin reutilizável do dev-loop: a camada de princípios, as personas dos agentes, os guardas de permissão. A metade que dá pra adotar sem nenhuma conta em nuvem. Ver no GitHub

Os passos de "do fork até no ar" estão nos READMEs, não nesta página: o deste repo traz o caminho de nuvem inteiro, do domínio até o primeiro merge, e o do plugin traz a metade do loop, que se instala sem nenhuma conta em nuvem. E a régua que um projeto precisa passar pra entrar no portfólio daqui também é pública, em docs/catalog-ready.md — o gate de prova de engenharia.

Uma coisa eu recomendaria copiar sem pensar duas vezes. O deploy entra na AWS por OIDC, então não há segredo guardado: uma chave vazada é acesso até alguém revogar, e um token vazado é acesso até expirar — e só se quem o pegou também satisfizer a condição do trust, que aqui é o subject imutável daquele repositório, por ID numérico e não por nome, porque nome pode ser transferido pra outra pessoa e os IDs não. A troca é que a raiz dessa confiança precisa nascer fora: o Terraform daqui não cria o provedor OIDC, nem a role que roda o próprio Terraform. É um buraco documentado num piso, e nenhum plan vai te avisar que ela saiu do lugar.

(→ ADR-0042 raiz de confiança fora do Terraform · ADR-0015 subject imutável)

O que eu ficaria nervoso de ver alguém copiar sem o resto é o merge direto pra produção. Trunk-based com ambiente único é rápido e implacável na mesma medida; sem os gates na frente, sobra só a segunda metade.

Isto está em aberto porque há mais escolhas de configuração do que sessões que uma pessoa sozinha consegue rodar. Se você rodou alguma dessas escolhas de outro jeito, é você que tem a metade que falta nesta página: me conte o contra-exemplo, ou compartilhe dizendo o que faria diferente.