Changelog¶
Todas as mudanças relevantes deste projeto são documentadas aqui.
O formato segue o Keep a Changelog e o projeto adere ao SemVer — ver versionamento.
Unreleased¶
1.2.1 - 2026-10-02 (constituição 2.2.2 · kit 2.0.11)¶
Alterado¶
- Túnel do Sentry no web por
Object.defineProperty(norma 2.2.2): no bi-comercial e no bi-transporte, o patch que envolvia oSentry.initnão pegava, porque osentry_flutter9.x recarrega o SDK do CDN no boot e reatribui oinit, e os envelopes iam direto para o GlitchTip. A página do Flutter passa a dar a receita que intercepta a propriedadeinitcom setter, e a armadilha ganha a cura certa. build_websem cache gha (kit 2.0.11): ocache-to: type=gha,mode=maxsubia a imagem Flutter inteira a cada release, de 2 a 9 min, e nenhuma camada era lida: o cache do Actions é por ref, a tag não lê o de outra tag e o master não builda desde o 2.0.10. Sem ele obuild_webcai de 10–14 min para ~4,5 min (onpetro-bi e vantroba-bi). O harness ganha a asserção e o mutante.
Migração¶
- Kit 2.0.11, ao re-derivar o
pipeline.ymlde app Flutter web: saem as linhascache-fromecache-tododocker/build-push-actiondobuild_web.
1.2.0 - 2026-10-01 (constituição 2.2.1 · kit 2.0.10)¶
Adicionado¶
- Migration de raw table congela a própria DDL (norma 2.2.0): no bi-transporte, as migrations das raw
tables derivavam colunas e índices da lista viva, então uma futura
ALTER TABLE ADD COLUMNquebraria todo install novo comduplicate column, sem aparecer no dev. A página do PowerSync passa a exigir migration aplicada imutável e com DDL literal, travada por teste de paridade: migrations num banco vazio = DDL viva =syncedColumns, e o upgrade chega ao mesmo esquema do install novo. - Coordenadas do sourcemap do web cobradas no gate (kit 2.0.9): no bi-comercial, o upload do sourcemap é
degradável, então uma coordenada faltando no
app.jsonpassava calada e o evento chegava minificado no GlitchTip. Ovalida-frontmatter.pypassa a reprovar o app comwebembuild.targetsefeatures.glitchtip.enabled=truequando falta o DSN com host, ofeatures.glitchtip.projectou osmoke.glitchtip.org, que são as três coordenadas que obuild_webderiva.
Alterado¶
- Menos minutos do GitHub Actions por release (kit 2.0.10, norma 2.2.1): em 30 dias os 20 repos
gastaram ~2.016 minutos cobrados, acima da cota de 2.000, e o Actions bloqueou os jobs em 30/09. O GitHub
cobra cada job arredondado ao minuto, e 518 dos 805 jobs duraram menos de 60 s. O commit
chore(release)nomasterdeixa de alocar runner (oif:dodecidepula o job, também nopipeline-config.ymle nobuild-site.ymldo central, que ignora ainda push só de backlog e andaime). Orelease-checkvira o último step dogate, só na tag e mesmo com teste vermelho, e osmokevira o último step dodeploy. Odocspassa a esperar ogate: no push de tag só publica com ele verde e, verde, libera o deploy; falha só de publicação no Garage vira aviso, sem atualizar oversions.json. A constituição muda só de redação (o contrato de release é passo do gate). O CI do central passa a rodar oactionlint1.7.12 (pinado comsha256) nos dois templates e nobuild-site.yml, porque o harness lê texto e não avalia expressão do Actions.
Migração¶
- Kit 2.0.10, ao re-derivar o
pipeline.yml(ou opipeline-config.yml): os jobsrelease-checkesmokesomem, e quem personalizou o template refaz à mão: - tirar o
release-checkdoneeds:dodeploye pôr odocs; - mover os steps próprios do job
smokepara o fim dodeploy, lendosteps.pre.outputs.*; - quem tem o
release-forgejodescomentado leva ostartsWith(github.ref, 'refs/tags/v')noif:, senão ele cria Release no Forgejo a cada push nomaster.
1.1.1 - 2026-10-01 (constituição 2.1.0 · kit 2.0.8)¶
Adicionado¶
- Sync travado do PowerSync vira evento no GlitchTip (norma 2.1.0): no app Flutter com PowerSync, o
downloadErrorsó virava breadcrumb e o sync parado não gerava evento nenhum; um cliente do bi-comercial ficou mais de 5 min com dado velho sem nada chegar ao GlitchTip. A página do PowerSync passa a exigir que o app reporte o primeirodownloadErrorde cada tipo e rode um vigia de sync travado (download sem avançar, ou sem conexão com erro, por 5 min; ocioso não conta), testado com relógio injetado. - Armadilha do gatilho pendente no PowerSync: cache em memória que recarrega no checkpoint e só quando a
tabela mudou ficava um checkpoint atrás, porque o
onChangecomthrottlechega depois dostatusStreamdo mesmo checkpoint. A tabela de armadilhas do cliente ganha a cura: o gatilho sem escrita vista fica pendente e dispara quando oonChangechega. - Pipeline mudado não passa calado até a tag (kit 2.0.7): no
xadm-commons, o rename do env do publish passou por todos os gates e só falhou no job da tag, com a tag já no ar. O filtrocodedopipeline.ymldo app passa a incluir.github/workflows/**, então o push que mexe no pipeline roda ogate; a/xadm-releasedetecta pipeline mudado desde a última tag, confere com o dev os nomes de env que o build lê e avisa no relatório. A página de CI e testes ganha a seção Caminho que só roda na tag.
Corrigido¶
- Todo
curldo pipeline com timeout (kit 2.0.8): com um dos dois IPs da zona*.xadm.bizfora do ar, ocurlsem timeout ficava pendurado no connect desse IP e o job morria notimeout-minutes. Todocurldopipeline.ymle dopipeline-config.ymlpassa a levar--connect-timeout 10 --max-time 60(600 s no upload de asset de release), e otesta-guardas-pipeline.pyreprovacurlsem eles. Os deploys pelo control-plane falham com o nome doCENTRAL_DEPLOY_TOKENquando o secret falta, e o aviso ao central vira::warning::em vez de um 401.
1.1.0 - 2026-09-30 (constituição 2.0.2 · kit 2.0.6)¶
Adicionado¶
- Guarda dos ícones do Flutter web: o app instalado como PWA saía com o logo do Flutter, porque os
ícones de
web/icons/eram os doflutter createe nenhum gate olhava para eles. O gate da variante Flutter web passa a rodar ocheca-icones-web.py, que reprova PNG deweb/idêntico ao do scaffold e avisa, sem reprovar, quando o alvo doapple-touch-iconou um íconemaskabletem transparência. A página do Flutter ganha a receita dos ícones do web. - Chamada de app para app pela URL pública, com DNS local por host (norma 2.0.2, decisão 0039): a
zona
*.xadm.bizpassou a ter dois registros A, e o hairpin NAT de dentro do host falha por um deles. Chamada de um app ao domínio público de outro falhava às vezes comNo route to host(GlitchTip #1356,thoms-webstorm→int.thoms), e a varredura achou o mesmo caminho em integradores,pied,central-backend, apps de planilha e noSENTRY_DSN. A decisão fixa a URL pública como único caminho entre apps (nunca IP, alias Docker nem--add-host), oxadm-dnsem todo host Coolify e o--dns 10.0.1.1 --dns 1.1.1.1em todo recurso. A página do Coolify ganha a seção da chamada de app para app, o provisionamento doxadm-dnsno baseline do host, o passo no criar recurso e no checklist, e a checagem periódica. O lint do compose dopipeline-config.ymlavisa serviço semdns:.
Corrigido¶
- Túnel do Flutter web resolve o destino por pedido: a receita do túnel same-origin punha o host no
proxy_pass, e o nginx resolve esse nome ao carregar a config. Em 2026-09-27 a VM de produção religou com a WAN fora: os dois BI saíram comhost not found in upstreamem crash-loop e ficaram parados até o redeploy manual, enquanto o resto da frota voltou sozinho. A receita passa a usar variável comresolver(DNS do Docker), e opipeline.ymlganha a guarda proxy_pass resolvido no boot, que reprova host literal noproxy_pass(IP,localhosteupstreamnomeado passam). - Scripts do kit fora da conferência de presença, e a cópia local vira sobra: a
/xadm-docssó explicava a presença das entradas dearquivos, e a leitura literal acusava como ausentes os scripts, que não têm destino porque o CI e as skills os baixam frescos. Ao mesmo tempo, repos guardavam cópias antigas emscripts/: o agente rodou uma delas, que ainda imprimia a constituição 1.4.8, e recebeu "0 erros" antes de baixar o validador fresco. A skill passa a dizer que scripts não entram na conferência e que qualquer cópia deles no repo sai comgit rm. Essa regra genérica substitui as duas entradas doremoverque tratavam os scripts do sync-rules um a um. A referência de docs da/x-planejar, que mandava rodarscripts/valida-frontmatter.pylocal, passa a baixar os scripts frescos.
1.0.4 - 2026-09-25 (constituição 2.0.1 · kit 2.0.4)¶
Adicionado¶
- Guardas de doc antes do commit: a
/x-documentarrodacheca-navecheca-modelagemno fechamento, porque o ADR fora do nav e a migration semmodelagem.mdsó apareciam no pré-flight da release. Ocheca-modelagemcompara a base com a árvore de trabalho (antes via só o commitado e, com o baseHEAD~1, só o último commit), e opipeline.ymlusa obeforedo push. Na/xadm-release, a correção de pré-flight ganha destino: amend no commit do dev antes do CHANGELOG, ou commit separado quando o HEAD já está no origin. - Manual de integração: roteiro prático de como o dado sai do X-Adm para a nuvem e de como o dado de fora entra no X-Adm, com o que configurar e em que ordem; a página Plataforma de Integração fica alinhada ao PIED.
- Área Plataforma com porta de entrada: índice com entrada por tarefa e diagrama do que se replica por cliente contra o que é instância única, e o inventário da nuvem (apps, instâncias de sincronização e serviços de plataforma). A Infraestrutura passa de Transversais para Plataforma.
Alterado¶
- Skills do kit ajustadas ao modelo atual: a
/x-refinarprioriza achados de alto impacto em vez de um teto numérico, a/x-documentarpede as três facetas e deixa a delegação a subagentes opcional, e a/xadm-setuptroca as narrativas de incidente pela regra no presente. - Gate Java no Windows: a página da stack dá o
gradlew.batpelo PowerShell ou oexclude_commandsdo rtk, já que ortk gradlewtrava sem saída; a edição em lote passa a cobrir encoding, porque o-Encoding utf8do PowerShell 5.1 grava BOM e quebra ojavac. - Repo novo nasce com a pasta no slug: o
app-novoorienta criar a pasta com o slug da decisão 0038; os repos existentes não são renomeados.
Removido¶
- API Reference sai do nav: a condição de sincronização era inalcançável e nenhum artefato chegou ao bucket; a API gerada continua servida dentro do site de cada app.
Corrigido¶
- Carimbo de commit do portal: o
/commit.txtrespondiadevporque a variável chegava vazia ao build; o Dockerfile trata vazio como ausente, e a página do Coolify registra a conferência pós-deploy (ligar o marcador Available at Buildtime segue sendo ação no painel).
1.0.3 - 2026-09-22 (constituição 2.0.1 · kit 2.0.3)¶
Corrigido¶
- O aviso de link do Forgejo deixa de confundir imagem de registry com link de repo: a guarda
publicada no kit 2.0.2 casava qualquer menção a
fonte.xadm.biz/xadm/, e o mesmo host serve o registry de imagens — medido na frota, 23 dos 32 avisos eram o nome da imagem (…:native-amd64) dentro de code span, que a norma não restringe. Guarda com falso positivo ensina a ignorar aviso. Agora a regra casa só a forma de navegação (link markdown,/src,/issues,/compare,.git) e ignora code span e bloco de código; sobraram 18 links reais, tratados nos repos.
1.0.2 - 2026-09-22 (constituição 2.0.1 · kit 2.0.2)¶
Adicionado¶
- Entrada por tarefa na porta do site: o
indexganha a seção "Preciso fazer uma coisa", que mapeia as onze tarefas frequentes para a página que já as cobre — antes a porta organizava só por assunto, servindo quem quer estudar o padrão e não quem chegou para fazer. O glossário entra no nav (nenhuma página o linkava). - Diagrama nas páginas de fluxo: versionamento (as três versões e a detecção de defasagem),
ci-testes (o que gateia o deploy) e libs (o caminho da lib até o app) trocam prosa por fluxograma
Mermaid
TB, lintado no CI. - Norma 2.0.1 — identidade no piso: a constituição passa a afirmar o invariante da decisão
docs/decisoes/0038-uma-identidade-por-app.md(app_id,sluge projeto de erro são o mesmo valor), que o validador já reprovava sem a norma dizer. A referência de classes (dev/api/) vira opt-in explícito nos dois lados — bloco comentado nopipeline.ymle aviso na constituição — em vez de promessa que o kit não cumpria (resultado medido: link 404 no site publicado). Ovalida-frontmatterpassa a avisar sobre link de repo fora de lugar. - Guarda de legibilidade no
checa-redacao.py(avisa, não falha): frase acima de 35 palavras e página com mais de 10% das palavras em negrito. Mede por parágrafo, não por linha — a quebra do Markdown esconderia toda frase longa. O glossário ganha a seção Vocabulário da casa (piso, guarda, gate, smoke, carimbo, ponto de injeção, desvio, rede de segurança).
Alterado¶
- Anglicismo com tradução direta sai da norma viva e dos templates: seam vira ponto de injeção, drift vira desvio, backstop vira rede de segurança, wrapper vira invólucro. As decisões ficam como estão — ADR é retrato datado, não se reescreve. As três páginas mais pesadas (powersync, publicar-docs e integração) tiveram as frases longas quebradas sem mudar o conteúdo técnico.
Corrigido¶
docs/documentacao/templates.mdrestaurado: a edição em lote do passe de redação apagou 66 linhas (as seções Diagramas (Mermaid), Figura autoral em SVG e Riscos consolidados) e injetou dois bytes NUL no lugar de uma cerca de código. Com as cercas rompidas, exemplos dentro de bloco viraram links reais e omkdocs build --strictpassou a abortar com cinco links e âncoras quebrados — o site não subiria. É a armadilha que a regra de edição em lote já descreve: script que reescreve arquivo versionado confere o--numstatantes de fechar.- Cópias de skill re-derivadas dos templates:
references/flutter.mddasx-definir,x-desenhar,x-planejarex-refinarestavam defasadas em.claude/skills/, com ocheca-dogfood.pyvermelho. - O exemplo de
app.jsonna norma deixa de ensinar a divergência que o próprio gate reprova. - Redirect relativo, para a navegação não saltar por
http; links quebrados do CHANGELOG publicado e redirect dos slugs antigos.
1.0.1 - 2026-09-22 (constituição 2.0.0 · kit 2.0.1)¶
Adicionado¶
- Decisão 0038 — uma identidade por app: o
app_iddodocs/app.jsoné igual aoslug, e esse nome é o mesmo no repo, na imagem do registry, no recurso Coolify, no bucket da doc, no catálogo dodb_authe no projeto do GlitchTip. Substitui a cláusula "slug pode ≠ app_id" da 0024 (o resto dela segue valendo) e registra a migração em duas fases para os apps que compilam o próprioapp_idno bundle. - Guarda de identidade no
valida-frontmatter.py: o validador barraapp_idausente em app combuild.targets,app_id≠slugefeatures.glitchtip.project≠slug. Era a checagem que pegaria sozinha os dois defeitos que motivaram a 0038 — projeto de erro inexistente e rename pela metade —, ambos falhas silenciosas.
Alterado¶
app.json:nomederiva doslug(slug sem o prefixo<cliente_id>-, em caixa alta, hífen de família virando-) estacké rótulo curto<linguagem ou runtime>/<framework>; cenário e variante vão nadescricao, não no nome nem na stack.- A tabela
appsdodb_authé espelho doapp.json, e só a/xadm-setupa escreve: oPOST /api/setup/provisionleva o bloco de identidade inteiro (app_id,nome,descricao,grupo,cliente_id,slug,production_url,stack,toolchain), não só os campos que o broker exige. Editar oapp.jsonnão muda a Central — o valor novo chega na próxima/xadm-setupdaquele repo. Documentado emdocs/documentacao/publicar-docs.mde notemplates/xadm-setup-skill.md.
1.0.0 - 2026-09-16 (constituição 2.0.0 · kit 2.0.0)¶
Constituição 2.0.0 · kit 2.0.0 — release MAJOR. A norma foi reestruturada: núcleo curto com âncoras fixas, versão do kit separada da versão da norma, perfis de repo,
pipeline.ymlcorrigido e ADRs governadas. Leia a seção Migração antes do primeiro push depois do deploy do central.
Mudado¶
- Núcleo da constituição reescrito (1.134 → 432 linhas): só princípios e pisos, até três frases
por regra, com link para a norma derivada dona. Headings sem número e com âncora fixa
(
#convencoes,#definicao-de-pronto…); seção se cita pelo nome, nunca por número. Umcheca-redacao.pycobra a redação (sem caso, data, item de backlog, versão de release nem seção por número) no núcleo, nas normas derivadas, nas páginas satélite (documentação, transversais, glossário e início) e nos templates do kit. - Três números, três coisas: a norma (
versao:da constituição,constituicao-versao.txt), o kit (arquivoKIT,kit-versao.txt) e o site (VERSION). Mudança de template, script ou skill bumpa o kit, não a norma; oapp.jsonregistra os dois (constituicao,kit). O manifesto ganhakit,perfis,stacksedestinopor artefato e a listaremover; o validador publicado imprimeconstituição C · kit Kna primeira linha. - Perfis de repo (
perfilnoapp.json:app,config,lib): cada perfil recebe do manifesto só o que é dele, e as stacks PowerSync e oxadm-commonsdeixam de viver de exceção. - Server Micronaut: native por padrão, jar declarado com motivo
(0033) — o app é native se e só se o
build.targetscontémnative. Opipeline.ymltraz o native ativo e o deploy jar comentado. - Libs da casa na versão corrente (0034): atrás é pendência
e a
/xadm-releasebumpa aplicando as### Migraçãoem ordem; abaixo do piso ela recusa. Os pisos vivem nopisos-libs.json(publicado em/toolchain/pisos-libs.json) e aparecem na página nova Bibliotecas da casa. - Deploy só com gate verde e release válida: o
deployexige ogatee, em tag, orelease-checkcom sucesso, sem caminho que pule o gate; um step de deploy por alvo; o jobdocsbuilda e valida sempre, inclusive em PR; oCENTRAL_DOCS_URLé derivado doCENTRAL_DEPLOY_URL; a versão da toolchain vem doapp.json. - Normas derivadas enxutas:
java-micronaut2.225 → 466 linhas; engenharia, infraestrutura eia.md4.645 → 3.148. Nasce a área Plataforma (central-de-apps,integracao, com redirect das URLs antigas). - ADRs governadas: decisão aprovada só recebe
> **Correção …:**visível; os comentários HTML e os blocos "Emenda/Evolução" viraram Correção ou ponteiro para a norma; 0003 e 0015 passam aobsoleto; entram a 0033, a 0034, a 0035 (eleição de instância no relay M2M) e a 0036, que registra o porquê desta reestruturação. - Linha de feedback:
§6: nada a reportarviraFeedback: nada a reportar/Feedback: candidato — <o quê>nas regras de IA, nas skills e nos templates. - O
CLAUDE.mddo central virou roteador (4.923 → 156 linhas); as pendências foram para obacklog.md.
Adicionado¶
templates/views-jte/kit/pager.jte(kit de UI, opcional): o paginador das listagens, paraPageeSlice, que preserva os filtros e deixa o?sort=da URL de fora.templates/pipeline-config.yml(perfilconfig): lint das sync rules, lint do compose, smoke de carga da imagem pinada e deploy pelo control-plane comtarget=compose; com os scriptslint-sync-rules.mjs,smoke-sync-rules.shecompose-sem-coolify.py.- Guardas novas no
pipeline.yml: toolchain ×.fvmrc/FROM,settings.local.jsonfora do git, locale pt-BR no native, placeholder no Dockerfile, libs no piso (aviso) e controller fora do event loop (que honra o marcador// event-loop-ok:). O harness passa a cobrir as 21 guardas (142 casos, 52 mutantes). scripts/envs-coolify.py: a/xadm-releasepreenche "Coolify — antes do deploy" e "depois do deploy" pela diferença de placeholders entre a última tag e oHEAD, e pede confirmação do "antes".- Hook de defasagem que separa norma e kit, acusa cache inconsistente e manda baixar a skill fresca quando o repo atrasou uma major.
- A
/x-desenhare a/x-definirmandam ler a norma que a feature toca: as seções da constituição que o pedido alcança, a norma derivada dona e, quando a norma do repo atrasou, a seção Migração do CHANGELOG. O manifesto diz o que re-derivar; só a leitura da norma diz o que a feature tem de respeitar. - Comentário de linha ou de bloco: uma linha, só para o porquê que o código não mostra (constituição,
Engenharia; Stack da casa). Javadoc e dartdoc de API seguem o
padrão da linguagem e documentam o contrato inteiro (
@param,@return,@throws), no presente e sem história. A história do bug ou da feature vai para o commit, o CHANGELOG ou a decisão; armadilha de linguagem ou framework vira linha na seção Armadilhas da página da stack. Os templates do kit foram enxugados pela regra, e os títulos das seções de armadilha das páginas passaram a usar esse nome (âncoras mantidas). validation.links.anchors: warnno site do central: âncora quebrada reprova o build.- Postgres de teste com um padrão só (Java/Micronaut §Testes):
o singleton da
xadm-comum-teste, com tmpfs. Ojdbc:tc:semTC_DAEMON=truerecria o Postgres a cada classe, e o Micronaut Test Resources deixa servidor órfão; os dois migram quando o repo tocar os testes. Trava vira falha: timeout da tasktest(15 min), timeout JUnit por teste (2 min) e Hikari de teste comconnection-timeoutde 5 s. A limpeza entre testes vem daxadm-comum-teste(IntegracaoComPostgresouPostgresTestResource.limparTabelas()), poupando o histórico do Flyway, e a fábricaRegrasArquiteturatraz as fronteiras por tipo do package-by-feature. Treze armadilhas daxadm-commonse do central-backend entraram na tabela do Java/Micronaut, entre elas oDurationqualificado no Kotlin DSL, oget()herdado doTestPropertyProvider, os serdes gerados que reprovam a regra de controller e o sub-blocohikari:que o datasource ignora, agora reprovado nogate. A norma de segurança registra a exceção nominal dos dois tokens do central-backend, um por escopo de rota. - Versão sobe à mão antes da release, e o
mavenLocalentra no build filtrado (versionamento, libs): a lib sobe o número, publica nomavenLocale o app testa contra ela antes do deploy. Versão acima da última tag e sem tag própria é a pendente, e a/xadm-releasea mantém. OmavenLocal()fica depois do registro e só parabr.com.xadm, então a versão publicada sempre vem do registro, e a release recusa dependência da casa que não está nele. Com o registro fora do ar, o Gradle não cai no local:./gradlew --offline. A versão mínima de uma API externa que o código exige é restrição e cabe em comentário (Stack da casa). - Segurança e operação Java/Micronaut (feedback do bi-comercial-xls e do int-pied): o
@Secureddecide antes dointercept-url-mape das regras próprias, então anônimo vai no método, nunca na classe (Segurança); o build native declara-march=compatibility; dedup de notificação externa mora no banco, reivindicado porUPDATEcondicional, nunca só na RAM; tabela semeada por migration fica fora doTRUNCATEda lib; e histórico que a UI exibe é dado, em/app/data, não em/app/logs(Coolify). checa-rotas.pyaceita família de sub-rotas (CI):MÉTODO /prefixo/…(ou/...) passa se o OpenAPI tem ao menos uma rota do método abaixo do prefixo. Antes, a reticência caía e a rota virava o prefixo, reprovada pelo método.valida-release.pyreprova link](docs/…)na seção da versão (Versionamento): o CHANGELOG embutido emdocs/o transforma emdocs/docs/…, e só o jobdocsacusava, com o app já deployado. Agora pega nas guardas finais da/xadm-releasee norelease-check, de que o deploy depende./xadm-releaseno perfil config (feedback do onpetro-powersync): o deploy, o gate de docs e a via "só push" deixam de descrever o fluxo do app para quem usa opipeline-config.yml. Lá não há build no CI nem jobdocs, o Coolify builda o recurso compose do git e push em master que muda a config deploya.- Paginação e ArchUnit (feedback do onpetro-bi-xls): listagem paginada devolve o
Page/Slicedo micronaut-data como está, com odefault-page-sizedeclarado (sem ele, o default é 100);@QuerycomPageabledeclara noFROMo alias que oORDER BYusa; e as regras de fronteira escrevem a fatia com o nome completo, com a origem vazia passando só na anti-vácuo. - Feedback passa por revisão adversarial no central (Feedback):
entra o que corrige, generaliza ou substitui uma regra, reescrita no lugar; cada aceite mostra o que saiu
ou foi fundido, e o que só acrescenta leva justificativa no backlog. O caso de um repo só é remendo e é
recusado, e cada ponto volta ao repo como
Resposta do central: aceito | já coberto | recusado | pendência. - Guarda de toolchain resolve
ARG, e a/xadm-docsremove sobra de outra stack (feedback do bi-comercial):FROM img:${FLUTTER_VERSION}passa a ser comparado pelo default doARG, e artefato de outra stack que o kit antigo copiou sai junto doremover. A regra de comentário ganha o limite de coluna da stack e a posição acima do código, e o--dart-definevazio chega como"", sem odefaultValue. - Build native com
configure<GraalVMExtension>(feedback do onpetro-bi-xls): o exemplo deixa o accessorgraalvmNative {}, que falta nos apps da frota sem o plugin de Test Resources, e a regra de paginação diz como testar a forma doPagesem contexto. - Zero negativo no web e comentário dentro de expressão (feedback do bi-comercial, medido): nasce a
seção Armadilhas do Flutter, com o
-vque no dart2js dá-0,00e a cura0 - v; e a regra de comentário corrige o que dizia do Dart — o comentário em linha própria dentro de uma expressão também re-flui o código, e o layout torto em volta dele se cura encurtando-o. - Rodada e2e sobre a 2.0.0 (feedback do etc/tests): o e2e-local chama o
manifesto de build de
BUILD-INFO.md, e só a rodada verde e limpa o promove; o harness recusa lib da casa pendente que não seja a do HEAD e republica o~/.m2atrasado; a ferramenta da toolchain sai do checkout local do central; gate que não roda sai como PULADO, nunca OK. A suíte native declaraname:no compose, builda oDockerfile.nativecomXADM_COMMITquando não há lib pendente e confere a imagem pelo label de identidade — ocommitinjetado por env do container não prova nada. O e2e do cliente PowerSync afirma onumericcomo texto no fio, e cada bump do pin o prova de novo. - Smoke com uma rota de cada classe de credencial (Smoke de produção):
o
/healthsozinho não pega a tela em 500 nem a rota que o AOT podou. A guardaClasse servida sem rota no smokedogatereprovasession(app comxadm-segurancae view) oum2m(app.api-tokenouROLE_API) sem rota declarada; a classe que só tem rota com efeito colateral declara o motivo emsmoke.sem_rota, que ovalida-frontmatter.pyconfere. - Sessão dos apps com prazo de revogação (0037, feedback do bi-comercial): o TTL do access token é o prazo em que a revogação e a troca de papel chegam ao app, e fica em no máximo 60 min; o app renova pelo refresh do central, sem a senha, e a sessão tem teto absoluto de no máximo 7 dias (Segurança). O TTL de produção só cai depois de o refresh existir no central e nos apps que renovam sessão.
- JWT no Flutter web: o piso diz o que protege (feedback do bi-comercial): no web o
flutter_secure_storageofusca e não defende de XSS, porque a chave AES fica no mesmolocalStorage; o token roubado renova pelo refresh, e quem limita o estrago é a revogação e o teto da sessão (Segurança). A race da primeira gravação entrou nas Armadilhas do Flutter. - Badge de cobertura no repo do app (feedback do etc/tests): o % publicado sai da rodada e2e local, em
dois badges —
cobertura unit+intecobertura +e2e— gravados emdocs/assets/badges/e exibidos noREADME.mde no site (CI e testes). A constituição abre a exceção nomeada para esse gerado versionado. Oscripts/checa-badges.py, novo no kit, é a fonte única da regra "diff só do bloco", para a/xadm-releasee o harness do etc/tests; a skill leva no commit do release o que ele lista e avisa quando o sha medido tem código diferente do HEAD. - Integrador-server para native (feedback): no app multi-instância, o jar da troca vai para
int-jar.<cli>.xadm.bize fica nobuild.targetsaté a última instância trocar (Coolify). O dedup de notificação externa ganha a reivindicação porINSERT … ON CONFLICTe passa a nomear a sobreposição do rolling deploy; o skip de integração sem Docker se descreve pela forma, não por uma anotação que nenhuma lib entrega; e oguia-do-codigo.mdlinka ocomo-rodar.mdque a norma exige. Duas armadilhas novas no Java: o?sort=da URL numa listagem de tela e oWITH … RETURNINGnuma@Queryde escrita. - Bi-transporte na 2.0.0 (feedback):
sendDefaultPiiligado em toda stack, com o Flutter seguindo a Segurança. Obuild_webderiva dodocs/app.jsono destino, a org e o projeto do upload de sourcemap, e as varsSENTRY_URL/SENTRY_ORG/SENTRY_PROJECTsaem. O jobdocsinstala omkdocs-redirects, e omkdocs.ymldo kit traz o plugin comentado para o redirect que a constituição exige ao renomear slug. A/xadm-docsnão trata o Dockerfile do Flutter como sobra de outra stack, e o manifesto manda apagar axadm-compound, que a/x-documentarsubstituiu. O connector do PowerSync renova pelo refresh do central, com a regra de queda; as armadilhas do SDK do PowerSync, do go_router, do get_it/watch_it, do Sentry, do Aptabase e do Flutter web entram nas tabelas.
Removido¶
templates/build-deploy.ymletemplates/build-deploy-web.yml: opipeline.ymlúnico os substitui, e o manifesto manda o repo apagar o.github/workflows/build-deploy.yml.- Campos
nativeedeploydoapp.json. - O fallback
GT_*dosmoke.py: a base do GlitchTip vem do DSN; oGLITCHTIP_URLque opipeline.yml1.x passa segue aceito.
Migração¶
Quando fica vermelho. O pipeline baixa o validador e os scripts do site a cada execução, então as
regras novas valem no primeiro push depois do deploy do central, qualquer que seja a versão que o
repo segue. O que fica vermelho é o job docs (o deploy não depende dele). Medido em 2026-09-12 nos
14 app.json da frota — reconferir no dia do release:
| Regra nova (erro) | Repos que falham |
|---|---|
docs/dev/como-rodar.md |
10 de 10 com build.targets: int-pied, bi-comercial, bi-comercial-xls, webstorm-ecom, bi-transporte, bi-transporte-xls, central-backend, central-ui, int-sascar, integrador-server |
docs/dev/guia-do-codigo.md |
5: bi-comercial, webstorm-ecom, central-ui, int-sascar, integrador-server |
campo native presente |
7: int-pied, bi-comercial-xls, webstorm-ecom, bi-transporte-xls, central-backend, int-sascar, integrador-server |
smoke.routes, toolchain, alvo fora de {jar,native,web}, em-revisão com acento, sub-bloco hikari: sob datasources |
0 |
Perfil app (perfil ausente ou app):
- Rode a
/xadm-docs: ela re-deriva o kit (pipeline, skills,settings.json, Dockerfiles, templates) sem perguntar e grava okitnoapp.json; a norma nova ela re-audita com a sua aprovação. Com a skill local anterior à 2.0.0, a primeira rodada re-deriva a própria/xadm-docs(o manifesto a marca em 2.0.0): pare ali e rode de novo, já com a skill nova. - No
docs/app.json, tirenative(vale obuild.targets) edeploy; confirasmoke.routes, com/healthno mínimo, etoolchain. - Crie
docs/dev/guia-do-codigo.mdedocs/dev/como-rodar.md; o README passa a linkar os dois. - Apague o
.github/workflows/build-deploy.yml, se ainda existir. - No
CLAUDE.md, troque o bloco de regras pelo detemplates/claude-regras.md(linha de feedback no lugar de§6:) e repontem os links para âncoras da constituição pelo mapa abaixo. - Suba as libs da casa para a versão corrente — a próxima
/xadm-releaserecusa abaixo do piso. - No
smoke.routes, declare uma rotaGETsem efeito colateral de cada classe de credencial que o app serve, ou o motivo emsmoke.sem_rota: a guardaClasse servida sem rota no smokereprova ogate. Medido em 2026-09-15, reprova 5 de 7: bi-comercial-xls (m2m,session), bi-transporte-xls (m2m,session), integrador-server (m2m,session), int-sascar (session) e webstorm-ecom (m2m). - Quem roda o
smoke.pyà mão (o ensaio do e2e native) passaGLITCHTIP_API_TOKEN: o fallbackGT_URL/GT_TOKENsaiu. - App com usuário que loga no
central-backendrenova a sessão pelo refresh do central e desloga pelo código de revogação (0037), antes de o TTL de produção cair para 60 min — sem isso, o usuário re-loga a cada TTL. - App Flutter:
sendDefaultPii = truenoSentryFlutter.init. Apague as varsSENTRY_URL,SENTRY_ORGeSENTRY_PROJECTdo repo: obuild_webas deriva dodocs/app.json, e oDockerfiledeclara osARGsem default.
Perfil config (stacks PowerSync): app.json mínimo (slug, perfil: config, constituicao,
kit), o hook, as skills, VERSION + CHANGELOG e o templates/pipeline-config.yml no lugar do
.github/workflows/pipeline.yml (um workflow ou o outro, nunca os dois); apague
scripts/lint-sync-rules.mjs e scripts/smoke-sync-rules.sh (o pipeline baixa da toolchain) e a
.claude/skills/xadm-setup/ (não há feature a provisionar). O lint
de compose reprova hoje nas seis stacks — Dockerfile sem digest e com RUN, e o serviço de init
sem limite e reserva de memória —, e é o custo de adoção do perfil.
Perfil lib (xadm-commons): app.json mínimo com perfil: lib e toolchain, o hook e as
skills; o pipeline, a /xadm-release e o índice de CHANGELOGs são customização local; o site mínimo
segue Bibliotecas da casa. Apague a .claude/skills/xadm-setup/.
Controller de lib com I/O bloqueante leva @ExecuteOn; o sem bloqueio declara // event-loop-ok:,
que a guarda do event loop honra.
Mapa de âncoras da constituição:
| 1.5.0 | 2.0.0 |
|---|---|
#1-principios (§1.1–1.9) |
#principios (e uma âncora por princípio: #doc-perto-do-codigo, #interfaces-decisoes-uso, #docs-as-code, #registro-e-referencia, #enxuto, #migracao-oportunista, #consulta-e-compreensao, #portugues, #duas-fontes) |
#2-os-seis-niveis |
#niveis · livro, Resumo e nav: #livro |
#3-estrutura-padrao-de-cada-repo-de-aplicacao |
#repo · perfis: #perfis |
#4-frontmatter-obrigatorio |
#frontmatter |
#5-convencoes |
doc: #convencoes · código e plataforma: #engenharia |
#6-governanca |
#governanca · #definicao-de-pronto · #versoes · #redacao · #regras-de-ia · #feedback |
#7-central-de-apps |
#central-de-apps |
#8-plataforma-de-integracao |
#integracao |
#9-entrega-deploy |
#entrega |
0.39.4 - 2026-09-11 (constituição 1.5.0)¶
Adicionado¶
/xadm-docsdetecta template obrigatório ausente, não só defasado (feedback do int-sascar). Omudou_emsó acusa o que mudou depois da base do app; um obrigatório nunca copiado passava invisível — omkdocs.ymldo int-sascar apontavastylesheets/xadm.css, o arquivo não existia e o site servia 404 no CSS de identidade. O passo 2 confere a presença de todo artefato fora deopcionais, com filtro de stack (checkstyle só em Java,analysis_options.yamlsó em Flutter), e a tabela de destinos passa a cobrirxadm.css,checkstyle.xml/suppressions.xmleanalysis_options.yaml— e troca as entradas dos workflows Forgejo aposentados pelopipeline.yml.- O texto de feedback §6 se identifica (constituição §6, regras copiadas para os apps,
/x-documentar). Abre com o repo e a versão da constituição que ele segue, dá o contexto — o que se fazia, o que foi medido e o que é suposição — e, quando fizer sentido, aponta a fonte com link do Forgejo em vez de parafrasear o código. Sem isso o central reconstrói o caso às cegas ou precisa pedir autorização para ler o repo do app. - Prontidão do Postgres em harness:
pg_isready -h 127.0.0.1(e2e-local §13). Sem-h, opg_isreadyconsulta o socket unix e responde "pronto" já no servidor temporário do initdb, que não escuta TCP e reinicia logo depois; o app que conecta nessa janela falha comEOFExceptionna negociação SSL do pgjdbc, com cara de defeito do app. - Inteiro escalado no cliente como forma exata de ler
numeric(PowerSync §Tipos). O número segue viajando como texto; o app pode convertê-lo num inteiro na escala da coluna mexendo só na string, semDecimal, semBigInte sem ponto flutuante — oDecimaldo Dart pesa na web. A página diz também o que não serve (escalar multiplicando em ponto flutuante, que trunca0.29 * 100em28) e o teto de 2^53 dointna web, que vale para o total.
Corrigido¶
- A guarda "Destino de proxy cravado" nunca rodava na stack que ela protege (
pipeline.yml). Ela estava só no gate-Java, que o app Flutter web apaga ao adaptar o template. Agora a variante Flutter traz uma cópia idêntica, e otesta-guardas-pipeline.pyconfere a paridade entre as duas. - O anti-vácuo do ArchUnit agora reconhece a fábrica da casa (
pipeline.yml). O regex não conheciaRegrasArquitetura.importNaoVazio()(xadm-comum-teste≥ 0.3.0) — quem adotava a lib caía em::warning::eterno. Caso verde + mutante notesta-guardas-pipeline.py(53 casos, 20 mutantes). - O step de deploy do
pipeline.ymlengolia a falha docurl -fe saía vermelho depois de deployar.echo "…$(post …)"saía 0 com o POST falhando, e o step ficava verde sem deploy. Na forma[ … ] && …, com dois alvos descomentados, a última linha com o alvo desligado saía 1 e virava o exit do step: job vermelho depois de ter deployado. Agora éif … then resp=$(post …); echo …; fi: ocurl -fque falha aborta o step, e o alvo desligado sai 0. Conferido nobash -enos dois sentidos. - Massa de dados barrada em todo
docs/, não só empublic/(valida-frontmatter.py). O mkdocs copia o arquivo para o site e o publica mesmo fora do nav — um.xlsxreal emdocs/projeto/anexos/passou verde e foi publicado. Exceções: sobprivado/e o.docxde pré-projeto (nível 1 admite Word, constituição §4.1). O caso de teste que dizia "anexos/ é livre" foi invertido, com fixtures das duas exceções. Migração: o pipeline dos apps baixa o validador do site a cada execução, então a trava vale no primeiro push depois do deploy do central, qualquer que seja a versão da constituição que o app segue. Antes do próximo push, mova planilha, dump e banco dedocs/paraprivado/(ou tire do repo) — inclusive modelo de planilha com dado fictício, que antes podia morar emanexos/. - 0022 e
coolify.mdreconciliados com 0026/0027 (emenda in-place datada). A 0022 ainda prescreviabuild-deploy.ymlcom ponte SSH no presente e dizia "jobbuild" (ébuild_native). Ocoolify.mdmandava "a ponte SSH dispara", davabuild-deploy*.ymlcomo workflow vigente em nove pontos e trazia a tag imutável como<git-sha>— ela é<git-sha>-native, o alvo do rollback do smoke. O gatilho vigente é oPOST /api/ci/deployfeito pelos jobs dopipeline.yml; osbuild-deploy*.ymlficam rastreados como legado. - O túnel do Sentry na receita do Flutter web respondia 403 (Flutter §Deploy web). O
bug.xadm.bizé GlitchTip, que exige a key no pedido (X-Sentry-Authou query) — o DSN no envelope não basta (medido no bi-comercial: 403 sem a key, 200 com). A receita deriva também a key do DSN, põe o cabeçalho e troca os nomesBUGSINK_*porGLITCHTIP_*: foi o nome errado do servidor que sustentou a suposição; a nota de sourcemap do Flutter web também deixou de chamar o servidor de Bugsink.
0.39.3 - 2026-09-10 (constituição 1.4.10)¶
Adicionado¶
- e2e-local ganha a pasta por rodada, o cenário isolado e o marcador de fim (feedback do
etc/tests, 10/09). Cada execução escreve emout/rodada-<AAAAMMDD-HHMMSS>/(§2); cada cenário roda comtry/catchpróprio, e o crash de um vira resultado daquele cenário em vez de derrubar a run inteira — o relatório conta OK, FALHA e crash, para "não rodou" nunca passar por "passou" (§7/§8); e o último ato dofinallyé oFIM.txtda rodada. O caso que motivou: a pré-condição de um cenário novo caiu comthrow, otrapencerrou tudo, os cenários seguintes não rodaram e o dump de cobertura se perdeu. - "Terminou" é o marcador, não o pipe (agentes, seção nova). Quem acompanha tarefa longa decide o fim pelo EOF do stdout, que só chega quando morre o último processo que herdou o handle — medido: a notificação chegou cerca de 40 minutos depois do fim real, quando um daemon Gradle órfão morreu. Tarefa longa vai com saída para arquivo, o fim é o marcador daquela execução, e "a notificação não chegou" não é evidência de que a tarefa está viva.
- Assert negativo exige prova de que o ciclo rodou (e2e-local §8). "Nada reabre" passa verde de
qualquer jeito se o job agendado morreu; e
Start-Sleep 35com um comentário apontando o intervalo de 30 s vira decoração no dia em que alguém sobe o intervalo. A suíte espera evidência positiva do ciclo (tick no log, contador, timestamp de última execução) e só então afirma o nada; o teto da espera é derivado do knob que ela mesma configurou. - Barreira de daemon Gradle órfão em harness (Java/Micronaut §Armadilhas): depois de cada
gradlewcom Testcontainers, o harness encerra só o daemon com identificação positiva — os três critérios juntos: nascido na janela daquela chamada, pai morto (ou PID do pai reciclado) e ao menos um filho cuja linha de comando aponta para o repo da chamada. "Pai morto" sozinho não basta: o daemon é reutilizado por outros clientes via socket, e a versão sem o terceiro critério encerrou, ao vivo, o daemon de outra sessão. Filhos primeiro, PID reconferido na hora — nunca por nome de processo, nunca por imagem de container. - Destino de proxy do nginx vem do
app.json, derivado no build (constituição §5, Flutter §Deploy web, guarda nova nopipeline.yml; feedback de app). Os dois BIs tunelam Aptabase e Bugsink pelo próprio nginx e cravavam o host à mão — duas vezes porlocation—, enquanto ofeatures.analytics.aptabase_hostdoapp.jsonnão era lido por nada. Quandoanalytics.xadm.bizsaiu do ar, oapp.jsonjá estava certo e o túnel seguiu apontando para o host morto: 503 em produção. A fonte única passa a valer também entre arquivos do repo; a receita renderiza o template comenvsubstde lista explícita no estágio final do Dockerfile, falha o build com destino vazio e fecha comnginx -t. O túnel same-origin entra como receita opcional, e a guarda reprova host doapp.jsonescrito literal em.conf/.conf.template(ARGdo Dockerfile fica como ponto cego declarado).
Corrigido¶
- Postgres 18+ monta o volume em
/var/lib/postgresql(Java/Micronaut §Testes, e2e-local §13, Coolify §Banco). A imagem oficial mudouPGDATApara/var/lib/postgresql/18/dockere oVOLUMEpara/var/lib/postgresql; volume nomeado no caminho antigo fica vazio sem erro, e o dado mora num volume anônimo que umdown+upnão reaproveita. Atinge as suítes doetc/testse o compose de dev docentral-backend. Produção roda 18.4 e o ponto de montagem do recurso ainda precisa ser conferido no host. - O reaper do item 118 mandava
./gradlew --stopsem ressalva — ele derruba todos os daemons daquela versão do Gradle, inclusive os do IDE. Segue como gesto manual de último recurso, e passa a dizer que nunca é passo de harness. - Run sobre árvore suja não promove mais o manifesto de build (e2e-local §4): a âncora da última run verde apontava para uma árvore que já não existia. A run suja grava o patch completo na pasta da rodada — patch reproduz, hash só confere.
- A hint native do
SentryAppenderprescrevia só a classe (Java/Micronaut §Observabilidade e §Native, decisões 0022 e 0019; feedback docentral-backend). O logback acha os setters porgetMethods(), que no native só devolve método registrado — com a classe sozinha,<options>e<minimumEventLevel>são ignorados em silêncio. O DSN sobrevive porque o SDK o lê da env, e o nível perdido coincide com o default; o que some é oenvironmentdos eventos. A receita passa a trazer as três entradas com métodos (appender,SentryOptions,Level.valueOf), e a hint que axadm-comum-webvai embarcar tem de levar as três — senão distribui o mesmo defeito para a frota. /xadm-docspasso 5 re-lê o que oCLAUDE.mddo app declara e confere as guardas antigas dopipeline.yml(feedback de app). Trocar só o número da versão noCLAUDE.mddeixava escrita uma divergência que a migração acabara de desfazer ("preservar o vendor local"), contradizendo o código. E, ao re-derivar opipeline.yml, só se conferia se as guardas novas tinham entrado — guarda existente com corpo velho seguia com o defeito já corrigido na casa.- A defesa anti-vácuo do ArchUnit dava aviso falso em
assertFalse(classes.isEmpty())(guarda dopipeline.yml). Passa a reconhecer tambémassertTrue(classes.size() > 0);assertTrue(x.isEmpty()), que afirma o contrário, segue avisando. - A guarda do reflect-config da JTE quebrava no Windows (
WinError 193) quando o dev a rodava local: chamava./gradlew, que é script sh. Agora usa.\gradlew.batviacmdno Windows — com caminho explícito, porque comNoDefaultCurrentDirectoryInExePath=1(o ambiente do agente) ocmdnão busca na pasta atual egradlew.batpuro não é reconhecido — e normaliza a barra do caminho que oglobdevolve, que também quebraria o corte do nome da classe. - Estático para usuário anônimo é decidido pelo
intercept-url-map, não pela whitelist Java (Java/Micronaut §UI). A regra do YAML roda antes (ordem -100) e, quando algum padrão casa o caminho, devolveALLOWEDouREJECTEDsem consultar a regra de view — com o fail-closed da casa, liberar só na whitelist Java não tem efeito. Verificado no fonte do micronaut-security 5.0.0.
0.39.2 - 2026-09-09 (constituição 1.4.8)¶
Alterado¶
- Tabela de pisos do 0019 atualizada com as publicações de hoje (constituição 1.4.8).
xadm-segurancasobe 0.5.1 → 0.7.2: a 0.5.1 fazia o placeholder não resolvido recusar o boot, e a 0.7.2 fechou o par que faltava —app.api-tokendeclarado e vazio também recusa (BearerTokenGuard); abaixo dela, feature-ligada/env-ausente passa em silêncio e o app sobe healthy com/api/**em 401 a tudo. Entraxadm-comum-testepiso 0.3.0: a lib exporta o ArchUnit porapi(...), então a versão dela vaza para o test-classpath de quem não pina — abaixo da 0.3.0 saiarchunit-junit51.3.0, cujo ASM não conhece o class major 69 e pula as classes no import, deixando a suíte de arquitetura vácua em todo adotante de uma vez. - Mecânica do fail-fast de auth registrada, agora que foi entregue (segurança §Segredos): a lib
sabe que a feature está ligada pela presença da property — declarar o token é declarar a
feature —, não por flag paralela, que seria uma segunda fonte de verdade capaz de discordar da
primeira; e a guarda mora num bean
@Context, não@Singletonlazy, porque o objetivo é falhar no boot. - Entregas registradas no 0019 (conferidas no
maven-metadata.xml, não na nota de release): o seam da eleição saiu naxadm-mensageria0.4.0 —RelayM2m.executarSeEleito(), com o contrato de três entradas (executar()passe direto ·executarSeEleito()sob eleição ·tick()não é API) —, e a guarda de boot naxadm-seguranca0.7.2. Os dois saem da faixa "prescrito/implementado e não publicado".
Corrigido¶
- Ponto cego declarado na guarda do piso do ArchUnit (
templates/pipeline.yml): versão herdada de lib não aparece no grep — app que não pina não declara a coordenada, a guarda não acha nada e sai fail-open com a suíte rodando vácua. Quem cobre esse caso é o piso doxadm-comum-teste, cobrado pelo/xadm-docs; resolver dependência transitiva exigiria rodar o Gradle, o que o gate estático não faz.
Adicionado¶
- Feature de auth LIGADA com o segredo vazio = boot recusado (constituição 1.4.7; segurança
§Segredos, invariante (3) refinado + checklist do revisor). O invariante dizia só "config que não
quebra o boot de quem não usa a feature" — e tratava como uma coisa só duas situações diferentes:
feature desligada (dormente, correto: não derruba quem não usa) e feature declarada ligada com
o segredo vazio, que é misconfig. O efeito da segunda é ruim justamente por ser
fail-closed: ninguém autentica, o endpoint responde 401 a tudo, o app sobe healthy, e o
sintoma chega horas depois como "a integração parou", sem nada no boot. É a mesma classe do
placeholder não resolvido, que já recusa o boot desde a
xadm-seguranca0.5.1 — tratar os dois de formas opostas é incoerente. Fica explícito que o risco aqui é indisponibilidade silenciosa, não auth-bypass: o vazio já nega por construção (invariante 2), com teste na lib.
Removido¶
- Dez andaimes
.ia/consumidos —015(control-plane → decisão 0026),020(sync event-driven → 0028),021(higiene de nomes → 0024) e022(release scenario-aware → jobdecidedopipeline.yml), auditados contra o repo (decisão publicada e/ou checkboxes do plano), não pelo título. Ficam006e007(marcados FUTURA, e a007é citada na constituição como gating que ainda não existe) e014(feature flags, citada 5× como protocolo vigente). As duas referências que ficariam apontando para o vazio foram reconciliadas: a0006passa a apontar a decisão 0026, e o comentário dopipeline.ymldeixa de citar a spec removida. - O
/xadm-docspassa a COBRAR o hook de defasagem, não só cravá-lo (constituição 1.4.6; item novo no passo 3b). O hookcheca-constituicao.shsó avisa se duas peças existirem — o.shem.claude/e a entradaSessionStartno.claude/settings.jsonque o dispara — e a instrução de cravar existe desde a 0.31.3. O que faltava era cobrança: repo que copiou osettings.jsonverbatim (que não trazhookspor desenho), ou que instalou antes de a fusão existir, fica com o.shpresente e nunca acionado, e o silêncio é circular — quem não tem o hook é exatamente quem não fica sabendo que precisa re-sincronizar. Agora a skill confere as duas peças e reporta a ausência, o que alcança o app que já está nesse estado, não só o próximo a instalar (mesma lição do item 132: a instrução existia, faltava quem cobrasse).
Corrigido¶
- Seam público da eleição do relay registrado como "implementado, não publicado" (0019). O
RelayM2m.executarSeEleito()está no código doxadm-commonse sai na 0.4.0, que não existe no registro (release corrente0.3.4, conferido em 2026-09-09) — pela régua a tag, não o commit, o adotante ainda não pode declarar a dependência. Sai da faixa quando omaven-metadata.xmlservir a 0.4.0. - Verde declarado por agente vem do ARTEFATO, não do exit code (ci-testes, seção nova). No CI o
sinal continua sendo o veredito — o step roda
./gradlew checkpuro sobbash -ee o runner reprova sozinho. Fora dali muda: quem declara verde lendo a saída de um comando (o agente no pré-flight, o dev antes de taggar) depende de um exit code que atravessou wrapper, shim, shell e PowerShell, e cada camada pode reescrevê-lo. Norma: para dizer "os testes passaram", agreguetests/failures/errorsdosbuild/test-results/**/*.xmle confira o mtime de todos — XML antigo significa task pulada (up-to-date do Gradle), isto é, você está lendo outra rodada e chamando de verde; zero XML não é verde, é "não rodou". Origem: relato de./gradlewsaindo 0 comBUILD FAILEDno stdout — não reproduzido aqui (ortk errpropaga o código; opipeline.ymlnão mascara), o que aponta para propriedade do ambiente de quem roda e reforça a regra: sinal que depende de onde você está não serve de carimbo. É a disciplina que a casa já pratica com outros nomes — validador fresco do site, identidade pelo/healthe não pelo retorno doPOSTde deploy, cobertura do.execdaquela invocação. - Recurso global que a lib ocupa tem de ser NOMEÁVEL pelo adotante (java-micronaut §Específico —
face documental da norma do bean bom-cidadão, item 124). O
@Requiresresolve colisão em runtime, mas há recurso global que não colide e ainda assim precisa ser visível: advisory lock, fila, tabela, prefixo de rota, chave de cache. Quando a lib deriva o nome de uma property (hashtext('<micronaut.application.name>_relay_m2m')), ele não existe em lugar nenhum que o adotante consiga procurar — não sai nogrepdo app, não está noapplication.yaml, e só se descobre lendo o fonte da lib. Norma: o release nomeia a chave e a regra de derivação no§Migraçãodo CHANGELOG do módulo, e diz como ela se relaciona com o que o app já tem. Caso concreto: quem tinha lock próprio do relay (<app>_relay) e adota a eleição daxadm-mensageriafica com duas chaves distintas — de propósito, porque lock compartilhado serializaria relay e jobs do app sem motivo —, logo migrar é apagar o reagendamento local, não renomear o lock.
Corrigido¶
- O
flavordo/healthestava registrado como "sai na 0.7.0"; saiu na0.7.1(0019 e java-micronaut §Específico). A0.7.0existe no CHANGELOG da lib e não no registro (0.6.1 → 0.7.1 → 0.8.0 → 0.9.0) — caso-escola da régua a tag, não o commit, e o motivo de a versão corrente se perguntar aomaven-metadata.xml, nunca à nota de release. - Faixa "prescrito e não entregue" do 0019 reconferida contra os CHANGELOG dos módulos: o 404
autenticado em
ERRORsaiu naxadm-comum-web0.7.1 (sai da faixa); oreflect-configdoSentryAppendere o filtro de request-id/MDC seguem ausentes — agora com a verificação carimbada em 0.9.0, não mais em 0.6.1. Entrou na faixa o seam público para o adotante exercitar a eleição do relay (otick()daxadm-mensageriaé package-private; oexecutar()público não tem lock), pedido em 2026-09-09.
0.39.1 - 2026-09-09 (constituição 1.4.5)¶
Adicionado¶
- Versão de lib da casa: a corrente vem do REGISTRO, o central declara só o piso (constituição
1.4.5; tabela de pisos no 0019,
item novo no passo 3b do
/xadm-docs, ponteiros em constituição §5 e java-micronaut §Específico). A ideia era listar as versões doxadm-commonsna constituição para o/xadm-docscobrar; o levantamento mostrou que a lista escrita à mão é o problema, não a solução: a tabela do 0019 diziaxadm-comum-web 0.2.0enquanto o registro servia 0.9.0, e três dos oito módulos estavam defasados no mesmo dia em que a página foi editada. Omaven-metadata.xmldo registro é público, anônimo e sempre verdadeiro — então a corrente passa a ser consultada lá (umcurl), e o que a casa escreve é o piso: a versão abaixo da qual estar é defeito, com o motivo, que é justamente o que o registro não sabe dizer. Piso inicial:xadm-seguranca0.5.1 (Bearer fail-closed; abaixo, 401-em-massa silencioso),xadm-comum-web0.5.0 (seam decode/title+ log incondicional do erro; abaixo, 5xx não chega ao GlitchTip),xadm-mensageria0.3.4 (eleição de instância; abaixo, entrega em dobro sob canário jar × native). Os demais não têm piso — adotar bump é decisão do app. O/xadm-docsreporta em dois pesos: abaixo do piso entra destacado com o motivo; atrás da corrente mas acima do piso é informação, não pendência (senão todo bump de patch vira dívida e o relatório vira ruído). Sem gate de CI — a casa não derruba build de frota por versão de lib. O allowlist dotemplates/settings.jsonganhou o host do registro, escopado em/api/packages/xadm/maven/*. - §Tipos no schema do cliente (powersync) — a página não dizia nada sobre tipo de coluna, e a
convenção da casa morava no javadoc de um cliente. Agora a norma sai do mapeamento que o
serviço faz: o SDK tem três tipos (
text/integer/real), o schema declara o que vai chegar, e declarar diferente não dá erro — a doc é explícita ("if a value doesn't match, it is cast automatically"), o cast é silencioso. Tabela por tipo do Postgres (bool→integer1/0;int2/4/8→integer;numeric/decimal→text;float4/8→real;date/timestamp→textISO;json/jsonb→text;idimplícito, não declarar) e as três consequências: booleano chega 1/0 e código que esperatrue/falselê tudo falso, sem log;numericdeclaradorealfunciona por cast e troca a precisão exata pordouble— em BI o erro acumula na agregação — a convenção não perde precisão, e não há caso em que perder seja aceitável: quem escolheunumericno Postgres já decidiu que o número tem de fechar, então declaretexte converta paraBigDecimal/Decimalna leitura, comrealrestrito ao que já nasce float na origem (float4/float8). O custo da regra vem resolvido, não ignorado: decimal emtextordena lexicograficamente no SQLite, e as duas saídas exatas são somar/ordenar na aplicação ou o dono da tabela publicar uma coluna inteira escalada (valor_milesimos bigint) —CAST(x AS REAL)no SQL local não é saída, é o mesmo double por outro caminho;integerdeclaradotextfaz o SQLite comparar lexicograficamente ('10' < '9') e quebraORDER BY,BETWEENe índice. Fica registrado que a casa tem duas convenções: ointegrador-clientdeclara tudotextcom o porquê escrito (números viramBigDecimal, nuncadouble), obi-comercialdeclaraColumn.realsobrenumeric(15,3)/numeric(10,2)— que agora é dívida a migrar, não trade-off a documentar. Trave por teste contra o SQLite real, e afirme o decimal contra o valor exato (nunca umdoublecom tolerância: asserção com margem passa justamente no caso que a regra existe para pegar). - Teste de guarda de cluster precisa de SESSÃO distinta, não de thread distinta (java-micronaut
§Testes).
pg_try_advisory_locké por sessão e as requisições empilham: a conexão que já detém o lock o readquire sempre. Teste com duas threads sobre o mesmo pool pode receber a mesma conexão, as duas "instâncias" entram, e ele fica verde afirmando o contrário do que a guarda promete. Receita determinística: segurar o lock numa conexão fora do pool (DriverManager, com a URL que o Testcontainers publicou) e então chamar a guarda, esperando coalesce. Dois detalhes que evitam trocar de assunto no meio: a guarda vem injetada (o@Connectablesó aplica em bean proxiado — em objetonew-ado o teste exercita outro caminho) e o teste roda@MicronautTest(transactional = false)+@TestInstance(PER_CLASS). @Scheduledque vem de lib: veja se a lib já elege antes de reagendar (java-micronaut §Banco). Job agendado dentro de uma lib dispara em cada réplica igual ao do app, e a correção mora na lib. Trazer o job para dentro do lock do app é o caso restrito — lib que não elege, ou app que precisa do trabalho dela e do próprio sob a mesma eleição (locks distintos elegem instâncias distintas). Aí a receita é desligar a property do agendador da lib e chamar o método público sob o lock do app, o que funciona porque o gate costuma estar no tick, não no método público — com a ressalva de conferir no fonte da lib: gate no método público faz o reagendamento virar no-op silencioso e o job simplesmente para.- DTO de resposta de contrato leva
@JsonInclude(ALWAYS)(java-micronaut §Armadilhas, refinando a regra que já existia do lado do consumidor). A causa ganhou nome e default:micronaut.serde.serialization.inclusionvaleNON_EMPTY(lido no schema de configuração domicronaut-serde-api3.0.0), eNON_EMPTYnão some só com coleção e mapa vazios — some também com string vazia, então umString observacao = ""desaparece pelo mesmo mecanismo. Anotação no DTO, não a property global (mudar a inclusão do app inteiro engorda todo payload e altera endpoints que ninguém revisou); o defensivo do consumidor continua valendo, porque produtor de terceiro segue omitindo. - Tabela que o PowerSync replica: o
@Idda entidade é a PK real do banco (java-micronaut §Banco). O Micronaut Data aceita@Idsobre coluna apenasUNIQUEe a divergência só cobra do outro lado, onde aREPLICA IDENTITYdefault é a PK. Com a razão certa: não é que oDELETEchegue "sem o id" — o serviço casa a linha pelo hash da replica identity guardado nocurrent_data, e PK composta funciona; o que a regra evita é a divergência entre a chave que o serviço casa e o id que o cliente vê, que torna ilegível o único diagnóstico disponível quando o delete some.
Corrigido¶
- 0019 e 0021 davam a eleição de instância do
RelayM2mcomo "implementada e não publicada" — axadm-mensageria0.3.4 saiu (tagxadm-mensageria-v0.3.4, 2026-09-09) commensageria.relay.cluster-lockdefault ligado. Registro por versão atualizado no 0019 e o estado da entrega no 0021; adotar é subir a versão, sem DDL nem config. Quem contornou desligando o relay da lib e reagendando sob lock próprio pode devolver a guarda para a lib.
Adicionado¶
- Sync Streams
edition: 3— o que a subconsulta libera, e o teto de 1000 que ela traz junto (powersync §Config). O formato praticado nas seis stacks éstreams:comconfig: edition: 3(várias migradas debucket_definitions:) — e a distinção importa na hora de escrever query: sync rules clássicas não aceitam subquery, JOIN nem CTE; Sync Streams aceitamIN (SELECT …), subconsulta aninhada,INNER JOIN(colunas selecionadas de uma só tabela) e CTE no blocowith:, sendo owith:global o que exigeedition: 3.GROUP BY/ORDER BY/LIMIT/UNIONestão fora nos dois formatos. Ganho: recortar a filha pelo estado da mãe sem denormalizar. Preço: subconsulta é parameter query, e oPSYNC_S2305limita 1000 resultados (e 1000 buckets) — estourar devolve HTTP 500 no endpoint de sync e trava o cliente inteiro emconnecting, levando junto os demais streams; o dano não fica contido na query. Duas mecânicas que decidem o dimensionamento: o contador soma os streams da conexão e a parameter query roda uma vez por stream (32 streams com o mesmo recorte pagam 32×; a resposta do mantenedor a esse caso real foi fundir os streams, não encolher o dado), e a CTE baixa a conta porque o contado passa a ser o resultado dela — dimensione pelo que a subconsulta devolve, nunca pelo tamanho da tabela. - Como o DELETE encontra a linha: replica identity, não o
idda sync rule (powersync §Fonte). Cada linha é casada por um hash das colunas da replica identity; no delete o Postgres manda só essas colunas, e o serviço não avalia a sync rule com esse pedaço de linha — busca o registro anterior nocurrent_datapelo hash e remove os buckets já gravados no insert. Decorrências documentadas: tabela sem PK e sem índice único sai fatal (No replication id found … Configure a primary key on the table); o modo de falha silencioso é o registro anterior faltando, com assinatura própria no log (Cannot find previous record for delete on <tabela>: <replicaId> / <id>) e a linha ficando no cliente para sempre;REPLICA IDENTITY FULLresolve por força bruta e dobra as operações (todo update vira delete+insert); trocar a identity re-replica a tabela inteira. Higiene da casa:idé a PK simples — não porque o delete precise doidno evento, mas porque PK simples faz identidade eidcoincidirem, e aí o diagnóstico acima basta. - O serviço tem diagnóstico próprio, e ele vem com o comando da correção (powersync §Fonte).
POST /api/admin/v1/diagnosticsePOST /api/admin/v1/validatedevolvem os erros por tabela já resolvidos — tabela fora da publication sai comoTable <t> is not part of publication 'powersync'. Run: ALTER PUBLICATION powersync ADD TABLE <t>., RLS semBYPASSRLSsai comoPSYNC_S1145com oALTER ROLEescrito. Exigemapi_tokensna config (sem token:Authentication disabled) — o que explica a impressão de que esses erros são silenciosos: o serviço acusa, ninguém pergunta. - Gate cuja lista é datada por versão de terceiro prova o vermelho a cada bump (ci-testes, seção
nova; receita concreta em powersync §Deploy). Gate que codifica o que a versão X rejeita — o lint
de sync rules contra a tag pinada é o caso da casa — fica verde para tudo quando a versão nova
passa a aceitar a construção proibida ou muda a string de erro procurada: a regra passa a valer
por omissão e ninguém nota, porque verde é o esperado. Norma: ao subir o pin, rodar o gate
contra entrada sabidamente inválida e confirmar que reprova; não reprovou, a regra saiu de
validade (reescrever ou remover). É a disciplina de mutante das guardas do
pipeline.ymlcom o gatilho deslocado — lá o mutante roda a cada mudança nossa, aqui a prova roda a cada mudança deles.
Corrigido¶
- A §Fonte prescrevia declarar a lista de tabelas na publication; a casa cria
FOR ALL TABLES(powersync §Fonte) — prescrição aspiracional numa página que se declara só o praticado, contra o que as seis stacks fazem (CREATE PUBLICATION powersync FOR ALL TABLES). Reconciliado ao praticado, com o trade-off escrito nos dois sentidos: publication ampla custa WAL e memória a mais e faz tabela nova entrar sozinha; lista explícita exigeALTER PUBLICATION … ADD TABLEno mesmo PR da migração (mais oGRANT SELECT), e esquecer não quebra deploy — a tabela só não replica. Fica registrado que o caminho não é reversível de graça: o Postgres não aceitaALTER PUBLICATION … ADD/DROP TABLEnuma publication criadaFOR ALL TABLES. Mesma correção no irmão que o operador de fato copia: o bloco SQL de coolify §Banco que é fonte do PowerSync traziaCREATE PUBLICATION powersync FOR TABLE a, bcom a nota "FOR ALL TABLESsó em dev" — drift entre irmãos, as duas páginas prescreviam contra a prática.
0.39.0 - 2026-09-09 (constituição 1.4.4)¶
Adicionado¶
- Nome de tabela que o app cria em schema-espelho: prefixo
<app>_também para a tabela de DOMÍNIO, e o substantivo é decisão (integração §Taxonomia — dona da convenção; eco em java-micronaut §Banco, onde quem cria a tabela lê). A convenção existia mas cobria só staging e status por chave natural; a unidade de trabalho do app (lote de entrega, remessa, agrupamento de envio) ficava sem regra, e num schema que espelha sistema de terceiro é o prefixo que separa "nosso" de "deles". Agora:<app>_para toda tabela do app, composto<app>_<fluxo>_quando o app serve vários fluxos e a tabela é de um só; prefixo é sempre o app, nunca a origem —<origem>_já significa coluna do vocabulário do parceiro (só rastreabilidade, decisão 0009), e o mesmo prefixo com dois sentidos confunde quem a convenção deveria ajudar. O prefixo resolve o schema; o substantivo resolve a conversa: confira o termo contra o vocabulário do sistema espelhado antes de nomear —lotecomo unidade de entrega, num schema em quelotequer dizer lote de estoque (loteest,itenslote), não colide no banco e colide na reunião; homônimo inevitável vai para o glossário do repo do app. E tabela sem prefixo que já existe (request,push_enviada) é legado, não exemplo — rename oportunista por expand/contract quando a tabela for tocada, nunca big-bang. scripts/testa-guardas-pipeline.py— as guardas inline dopipeline.ymlganham teste permanente (constituição 1.4.4; wire nobuild-site.ymle no pré-flight da/xadm-release; norma em ci-testes §Guarda inline dopipeline.ymlnasce com caso de teste). As ~12 guardas do jobgaterodam na frota inteira, em todo push, e nenhuma tinha teste: a verificação era ad-hoc, na sessão que escrevia a guarda, e evaporava com ela — numa única sessão o extrator do YAML foi reescrito 7×, e das 5 guardas tocadas em set/2026 4 tinham defeito, todos achados por um app rodando (itens 151/186). O harness extrai o bloco do própriotemplates/pipeline.ymlpelonamedo step (nada extraído = vermelho, se o formato mudar), roda cada caso numa árvore temporária e aplica mutação declarada — mutante vivo = falha, porque a linha mutada não estava sendo exercida por nenhum caso. Entrada: 6 de 12 guardas, 38 casos, 13 mutantes; cobertura oportunista (§1.6). Meta-guarda: guarda declarada no harness que suma do YAML reprova, em vez de virar teste que não testa nada. Provado nos dois sentidos — mutar a guarda real nopipeline.ymldeixa o harness vermelho, e renomear um step dispara a meta-guarda.
Corrigido¶
- Comentário duplicado na guarda do piso do ArchUnit (
templates/pipeline.yml), resquício do item 186 — o bloco explicandoassertTrue(...hasNext())aparecia duas vezes. - Réplicas do MESMO app competiam pela outbox — a partição da 0021 só separava apps diferentes
(constituição 1.4.3; emenda in-place na
0021, receita em
java-micronaut §Banco, ponteiro em integração §Entrega garantida, registro por versão no
0019). O filtro por
tiporegistrado (0.2.0) particiona aSAIDAentre apps; réplicas do mesmo app registram os mesmostipo, casam o mesmo predicado e drenam a mesma linha — eN > 1é o estado normal da casa (canário jar × native mantém os dois recursos no ar atrás do mesmo host), então o efeito externo saía em dobro, sistematicamente, não por corrida rara. Norma: o tick do relay roda sobpg_try_advisory_lock(hashtext('<app>_relay_m2m'))— chave por app (compõe com o filtro, não o substitui), por sessão e não transacional (oPUTacontece fora da transação, umpg_advisory_xact_lockou umFOR UPDATE SKIP LOCKEDsoltaria antes do envio), etry(a réplica perdedora coalesce). O contrato não muda: segue at-least-once, a idempotência do receptor por chave natural segue precondição e o exactly-once segue deferido — o gatilho da alternativa descartada ("só se um receptor comprovadamente não puder ser idempotente") não disparou. A distinção que faltava nomear: duplicação evitável (duas réplicas fazendo o mesmo trabalho — a lib remove) × inerente (crash entre o efeito externo e o commit — o receptor absorve).
Adicionado¶
- Guarda que avisa:
@Scheduledsem eleição de instância (templates/pipeline.yml). O achado é maior que o relay — todo job agendado dispara em cada réplica, e sob canário isso é o default, não a borda. A guarda lista os@Scheduledquando nenhuma fonte de produção mostra eleição (advisory_lock/SchedulerLock/LeaderElect);// agendado-ok: <por quê>no arquivo silencia aquele arquivo, como o-- destrutivo-ok:do Flyway. Só::warning::— job idempotente por desenho é legítimo e a detecção não sabe distinguir (ponto cego declarado). Sandbox 9/9 + 4 mutantes mortos (filtro de gerados, marcador, strip de comentário, self-skip de stack).
0.38.6 - 2026-09-09 (constituição 1.4.2)¶
Corrigido¶
SOURCE_COMMITé variável do Coolify, e em recurso pull-only ele a SOBRESCREVE comHEAD— a env de identidade da casa passa a serXADM_COMMIT(constituição 1.4.2;templates/dockerfile-java,templates/dockerfile-java-native,templates/dockerignore-java,templates/pipeline.yml,scripts/smoke.py, coolify §Carimbar o commit, smoke-produção §6,docs/decisoes/0031-smoke-de-producao-pos-deploy.md). O sintoma é mudo e caro, visto no primeiro release docentral-backendcom o/healthnovo: o build passou--build-arg SOURCE_COMMIT=<sha>, a imagem no registry carregavaENV SOURCE_COMMIT=<sha>, e produção respondia"commit":"HEAD"— o PaaS injeta a própria variável em runtime, por cima doENVda imagem, porque num deploy por imagem não há commit a resolver. Três estragos em cadeia: a camada 1 do smoke nunca casaria, areleasedo GlitchTip virariaHEAD(o log-guarantee preso no fallback por tempo, para sempre) e o alvo de rollback da entrega seguinte seria a tag inexistente<imagem>:HEAD-<target>— falha no pior momento. A cura é o namespace, não o valor: disputar o nome com o PaaS em runtime é briga que se perde no próximo release dele. Exceção deliberada: oDockerfiledo próprio site mantémSOURCE_COMMIT— ali quem builda é o Coolify, o valor é real e não há env de runtime em jogo.HEADpassa a valer como AUSENTE dos dois lados — lib (xadm-comum-web≥ 0.9.0, comSOURCE_COMMITcomo fallback legado para e2e local edocker run) escripts/smoke.py. App não migrado omite o campo em vez de publicar identidade falsa, eHEADnoPREVIOUSdeixa de virar alvo de rollback.- A camada 1 do smoke deixava passar verde contra o container VELHO (
scripts/smoke.py,docs/decisoes/0031-smoke-de-producao-pos-deploy.md). Pedido umEXPECT_COMMITe vindo um/healthsem o campo, ela degradava silenciosamente — mas o deploy é assíncrono, então o container que responde segundos depois do POST costuma ser o anterior, de uma lib que ainda não publicacommit: verde provando menos. Agora a degradação paraversaoexigeEXPECT_VERSIONdeclarada; sem ela, a ausência do campo é vermelha.testa-smoke.pycobre os três casos novos (26 casos). - Duas âncoras mortas em
docs/decisoes/0031-*.mdedocs/infraestrutura/index.md, que apontavamcoolify.md#carimbar-o-commit-do-deploy— slug real termina em-nao-leia-o-githead-no-build.
0.38.5 - 2026-09-08 (constituição 1.4.1)¶
Corrigido¶
- Vendorizar o Bootstrap criou um prefixo que a segurança dos apps não cobria:
/js/**(templates/views-jte/kit/layout.jte, java-micronaut §UI, constituição 1.4.1). Regressão da própria 1.4.0: antes o JS vinha do CDN e nem passava pela segurança do app; vendorizado, olayout.jtepassou a linkar/js/bootstrap.bundle.min.js— e os estáticos são servidos pelo app, cuja config (intercept-url-map+ whitelist da regra de view) o kit não controla. Quem re-deriva sem acrescentar a regra serve 401 no bundle, inclusive na tela de login, onde por definição não há sessão. O sintoma engana: página estilizada e sem JS (navbar-toggler/collapsemortos), sem nada no log. O cabeçalho do kit passa a dizer "ao re-derivar, libere/js/**como já libera/css/**", com os prefixos que ele linka listados. - A guarda de estáticos do gate não cobre isso, e foi dito por escrito: ela afirma que o arquivo
existe, não que é servido a anônimo — e guarda estática não resolveria, porque as regras vêm
de duas fontes (o YAML do
intercept-url-mape a whitelist em Java da regra de view): seria cega a metade e avisaria em quem está correto. A trava mora no app: teste que lê oshref/srcdo própriolayout.jtee afirma que nenhum responde 401/403 sem credencial — na forma que não envelhece quando o kit linkar um prefixo novo. Receita em java-micronaut §UI.
0.38.4 - 2026-09-08 (constituição 1.4.0)¶
Corrigido¶
- Duas prescrições da casa se cancelavam: o
jteGeneratevaza no POM e mata ocompileOnly(java-micronaut §Native e §UI,docs/decisoes/0032-tela-de-login-servida-pela-lib.md). A página manda a lib que embarca view aplicar a extensãojteGenerate("gg.jte:jte-native-resources:…")— e manda o JTE entrar comocompileOnlypara não empurrar motor de view a adotante API-only. As duas juntas não funcionam: o plugin fazimplementation.extendsFrom(jteGenerate)(verificado no bytecode dojte-gradle-plugin3.2.4), então a extensão de build sai no POM como dependência de runtime de todo adotante. O defeito é invisível nochecke no jar — só o POM publicado revela. A cura (setExtendsFromfiltrando ojteGenerate) entrou colada à receita que a causa, com a instrução de conferir no POM gerado. - A pergunta em aberto da 0032 fechou:
compileOnlyBASTA, verificado no artefato (jar com aJte…Generatedsob namespace +reflection-config.jsongerado; POM sem entradagg.jte) — os planos B (implementation, ou o móduloxadm-seguranca-views) saem da mesa. Registrado com a condição: o POM só fica limpo porque o vazamento acima foi desfeito.
Adicionado¶
- Duas notas de view servida por lib (java-micronaut §UI): a view da lib renderiza no teste dela
(
TemplateEngine.createPrecompiled+render("<namespace>/<view>.jte", …), o mesmo caminho do adotante) — senão quem descobre template quebrado é o app em produção; e oContentType.Htmlescapa por contexto, inclusive dentro de<script>(um${x}em bloco JS sai com/como\/), que é o que impede valor hostil de fechar a tag — asserte o valor escapado, não o literal cru.
0.38.3 - 2026-09-08 (constituição 1.4.0)¶
Adicionado¶
- A tela de login vira da casa: servida pela lib, configurada por property (constituição 1.4.0;
java-micronaut §UI, emenda nadocs/decisoes/0025-views-server-render-com-jte.md,templates/views-jte/kit/layout.jte,templates/public/**). A frota tinha cincologin.jte— e o que estava duplicado era justamente o que não deveria: o mecanismo de auth (mesmo SDK, mesmo popup, mesmo callback com CSRF) era idêntico nos cinco, enquanto o que divergia era moldura — bloco de marca em 3, título em quatro formatos. Um bug de auth custava 5 PRs. Agora oxadm-segurancaserve a view e o app configuraxadm.views.login.*(marca,tagline, e título derivado doapp-nome, que mata os quatro formatos por construção); adoção é migração — sobe a lib e apaga a view, no mesmo PR. - Norma: view empacotada em LIB mora sob namespace, nunca na raiz. O JTE transforma subdiretório em
pacote, então
login.jtena raiz de uma lib gera a mesma FQCN que a view do app — duas classes iguais no classpath, uma sombreando a outra, sem erro e sem aviso. Amensageria.jtedaxadm-mensageriaestá nesse estado (colisão latente), a reconciliar. - Bootstrap VENDORIZADO no kit (
templates/public/css/bootstrap.min.css,templates/public/js/bootstrap.bundle.min.js, rastreados): olayout.jtedeixa de linkarcdn.jsdelivr.net— a tela de login é a página mais exposta do app e o<link>ia semintegrity. Versão 5.3.8 (a linha atual; a frota estava 5 patches atrás), baixada e conferida contra o hash SRI que o próprio projeto publica; o cabeçalho de cada arquivo registra versão, origem e hash, então o próximo bump é auditável. OsourceMappingURLfoi removido — sem o.mapservido junto ele produziria o mesmo 404 silencioso que esta leva conserta. O SDK do Firebase segue nogstatic, de propósito: vendoriza-se apresentação, não o SDK do provedor de identidade. - Decisão
docs/decisoes/0032-tela-de-login-servida-pela-lib.mdregistra a mudança de fronteira (a lib manda no fluxo e na tela) com as cinco alternativas descartadas — template rastreado no kit, lib-só-do-mecanismo, bean customizador, slot JTE e móduloxadm-seguranca-views. - Emenda à 0032, no mesmo dia: a view da lib é AUTOCONTIDA, não usa o
kit.layout. A decisão original dizia que a tela usaria o kit, como as telas dos apps — e isso não compilaria:@templateé resolvido em tempo de geração, não em runtime (oJteloginGeneratedde um app real contém a chamada Java diretakit.JtelayoutGenerated.render(...)), então a view empacotada no jar exigiria okit/layout.jtedentro do build da lib, que não o tem. Axadm/login.jtepassa a trazer o próprio documento mínimo — logo, nome e versão —, com a identidade vindo do CSS (custom-theme.css), sem dependência de template entre repos. Custo declarado: ~10 linhas de header duplicadas no jar, com divergência visual possível ao longo do tempo. Descartadas com o custo escrito: lib carregar o kit sob namespace próprio (skew quando o central atualiza o kit e a lib ainda não), mover o kit para a lib (98 views em 6 apps mudam a chamada, e correção no kit passa a exigir release da lib + bump por app) e kit como artefato de template publicado (mecanismo novo). - Guarda: estático que o kit linka e não existe reprova o gate (
templates/pipeline.yml). Olayout.jtelinka/css/app.cssincondicionalmente e o JTE não tem como saber, em render, se o arquivo existe — sem ele o app serve 404 em toda página, invisível sem abrir o DevTools. A/xadm-docsjá mandava criá-lo desde a 0.23.1: faltava cobrança, não prescrição (a frase da skill, que se lia como "não se preocupe com ele", passou a distinguir não re-derivar de tolerar ausente). A guarda deriva a lista do própriolayout.jte, então cobre também o Bootstrap vendorizado, o tema, o logo e o favicon — e não exige o asset novo de quem ainda tem o kit antigo (que linkava o CDN), o que evitaria falso-positivo na janela de migração.
Corrigido¶
-
Três guardas do
pipeline.ymlcorrigidas por feedback §6 de app (templates/pipeline.yml, constituição 1.3.1). (1) O cache-dance dobuild_jarestava pela metade — o template trazia obuildkit-cache-dancemas não oactions/cacheque persiste o diretório entre runs, nem oidnosetup-buildx(sem ele não dá nem para escrever obuilder:). São duas metades: sem a que persiste, a dance re-hidrata a partir do vazio e o--mount=type=cache,id=gradle-<slug>dodockerfile-javanasce frio em todo run — um step que não economizava nada. Adotada a forma completa, que um app já carregava. (2) A defesa anti-vácuo do ArchUnit só reconhecia AssertJ —Assertions.assertTrue(classes.iterator().hasNext())é forma legítima e caía em::warning::falso, ensinando a reescrever teste já correto; a alternação passou a aceitá-la, mas dentro de uma asserção (hasNextsolto não conta — qualquerwhile (it.hasNext())no arquivo silenciaria o aviso). (3) Guardas varriam diretório GERADO —build/resources/main/,bin/e afins guardam cópias dos mesmos arquivos: no runner (checkout limpo) é inócuo, mas o dev roda a guarda local antes de pushar e reprovava por artefato velho com osrc/já corrigido. Eram três guardas, não uma (placeholder do Micronaut, persistência micronaut-data e DDL destrutivo do Flyway) — todas passaram a filtrarbuild/bin/out/target/.gradle/node_modules. Verificado por sandbox da cópia extraída do YAML, com mutação (desligar o filtro deixa o caso dobuild/vermelho de novo). -
Três emendas ao smoke, vindas do PRIMEIRO ensaio real (
scripts/smoke.py,templates/pipeline.yml,docs/engenharia/smoke-producao.md, constituição 1.3.2). Oetc/testspassou a rodar o mesmosmoke.pydo pipeline antes do deploy, contra o binário native local — e a 2ª run ficou vermelha. (1)classé a CREDENCIAL que a rota exige, não o público-alvo da tela. Owebstorm-ecomdeclarou/comosessionsendo a home pública: com o seam funcionando a rota passa; no dia em que o seam degradasse, o smoke bateria a mesma rota sem credencial, veria200, classificaria como "rota exposta" e reverteria um deploy sem defeito. Além do aviso na página, o smoke passou a detectar sozinho: em rotam2m/sessionele sonda também sem credencial e trata200anônimo como defeito de manifesto — falha sem reverter, e a bomba de efeito retardado vira erro imediato. A sonda não roda empublic, onde200anônimo é o correto. (2) Seam ausente nem sempre é404: onde há regra de view global, ela é consultada justamente na rota inexistente e o404sai como401(ou302, conforme oAccept) — medido no mesmo binário. Gate sobre o seam afirma "não emite sessão" (404/401/403/302), nunca== 404. (3) Nome de env fora da norma:GT_URL/GT_TOKENeram abreviação inventada, contraseguranca §Segredos(nome pelo destino, paragrepachar o conjunto a rotacionar). AgoraGLITCHTIP_URL+GLITCHTIP_API_TOKEN, com o nome legado aceito por uma release, com aviso. - Declarar
smoke.glitchtipsem cadastrar o token passou a REPROVAR (era aviso). Declarar o bloco é ato explícito: sem o secret a camada 3 não roda e o job fica verde provando 2 de 3 — o "verde provando menos" que a página combate, e o estado real em que o piloto estava. É defeito de configuração: falha, não reverte. Quem ainda não quer a camada 3 não declara o bloco.
0.38.2 - 2026-09-08 (constituição 1.3.0)¶
Adicionado¶
- Smoke de produção pós-deploy: o pipeline só fica verde se produção responder (constituição §9,
decisão
docs/decisoes/0031-smoke-de-producao-pos-deploy.md, nova páginadocs/engenharia/smoke-producao.md,scripts/smoke.py+scripts/testa-smoke.py,templates/pipeline.yml,templates/app.json,templates/dockerfile-java{,-native}). OPOST /api/ci/deployé assíncrono: retornava sucesso e o pipeline ficava verde antes de o container novo servir — "deployado" sem nada ter verificado o resultado. Um jobsmokepassa a rodar depois do deploy, em quatro camadas: identidade (ocommitdo/healthé o desta entrega), rotas críticas declaradas emapp.json→smoke.routes, log-guarantee (nenhuma exceção NOVA no GlitchTip) e veredito — reprovou, reverte para a tag imutável<imagem>:<sha>-<target>, confirma a reversão e fica vermelho. Motivo:/healthverde é liveness, não "as telas funcionam" — a frota entregou tela nova em 500 com o apphealthy, e segredo ausente que só apareceu como 401 no cliente. A mesma lista de rotas serve dois gates: o e2e native afirma ≠404 no binário antes do deploy; o smoke afirma o status esperado depois. Critério do log-guarantee:firstReleasedesta entrega reprova; reincidência de exceção antiga avisa, não reprova (reprovar por atividade na janela faria ruído crônico deixar o smoke vermelho para sempre — e a reação previsível seria desligá-lo); com guarda de fail-open para o projeto que não marca release. Reversão conservadora nas bordas: não reverte sob deploy concorrente, divergência entre instâncias, ausência de alvo, erro de manifesto nem indisponibilidade do GlitchTip. - Identidade de build canalizada até a imagem (
SOURCE_COMMITcomo build-arg nos três jobs de build ARG/ENVnos dois Dockerfiles rastreados). A convenção da casa existia (CLAUDE.md §2) mas ficara órfã de mecanismo desde a migração do build para o GitHub Actions: nenhum template passava ou declarava a variável, e o campocommitdo/healthera documentado como "opcional útil" sem caminho para preenchê-lo. O mesmo valor é ocommitdo/health, areleaseno Sentry e o sufixo da tag imutável — um identificador atravessa pipeline, imagem,/healthe GlitchTip. OARGé declarado tarde (depois doCOPY, no estágio final): declarado no topo, invalidaria o cache de tudo abaixo a cada commit — no native, os ~13 min donativeCompileem todo build.- A tag imutável de imagem vira ativo operacional (constituição §9): política mínima de retenção —
10 últimas por (imagem, target), e nunca a que está no
/healthde um recurso em produção. Podar o alvo transforma o rollback em "não confirmado" no pior momento. -
smoke.routesno contrato doapp.json(templates/app.json,valida-frontmatter): bloco ausente é válido (self-skip da adoção em ondas); declarado, o shape é cobrado — campo torto vira gate que não roda, falha silenciosa em vez de erro. -
O contrato do seam de sessão nasce reconciliado com a lib (
docs/engenharia/smoke-producao.md§4, decisãodocs/decisoes/0031-smoke-de-producao-pos-deploy.md): a credencial vai emX-Smoke-Token, não emAuthorization: Bearer— naquela rota o Bearer entra na cadeia de autenticação por token antes do controller e vira 401 antes de chegar ao seam; a sessão volta emSet-Cookie; e o papel de smoke acompanha o papel de leitura em vez de substituí-lo (isolado, não passaria em@Securednenhum), com o "só-leitura" imposto por filtro de método — garantia por verbo, que não prova ausência de efeito colateral num@Get.
Corrigido¶
- O mecanismo do
/healthdo Flutter web nunca estivera prescrito (versionamento §Receita, flutter §Deploy web): a doc descrevia o efeito ("o nginx monta a resposta lendo oversion.json"), não o passo. Agora está escrito que o arquivo é gerado no build, de dois insumos —versaodoversion.jsonecommitdoARG SOURCE_COMMIT—, e por que oversion.jsonnão ganha campo (é o artefato que o app consome para auto-update, servidono-cachea todo cliente).
0.38.1 - 2026-09-08 (constituição 1.2.2)¶
Adicionado¶
- Política de glitch: todo 5xx e todo 404 AUTENTICADO vão ao GlitchTip (java-micronaut §Erros,
§Native; e2e-local §10; 0019). Handoff de incidente:
GET /incidentesdo integrador-sascar respondia 200 no jar e 404 no binário native — rota podada pelo AOT, sem erro de build — e o processor de erro da casa logava 404 emDEBUG, então o erro real ficou invisível (não chegou ao Sentry). O invariante do item 142 (só 5xx) passa a incluir o 404 autenticado, e o porquê do critério ser a credencial fica escrito: em runtime o app não pode decidir por inventário de rotas — a rota ter sumido é o bug —, mas cliente com token acreditava que a rota existe (bug real) e scanner não manda token (ruído). Descartadas allowlist de prefixos por app e cross-check com o OpenAPI (driftam / custam por request). O mecanismo é da lib (request.getUserPrincipal()ou headerAuthorization) e carrega uma premissa a verificar no binário: se o filtro de segurança popula aAuthenticationnuma requisição sem rota casada — se não popular, o principal vem vazio no exato caso que a regra existe para pegar. Classe nova nomeada em §Native: o AOT pode podar rota registrada (404 no native × 200 no jar), irmã doSentryAppendere doresource-config; a defesa de build é o e2e native afirmando as rotas-chave (≠404), que ganhou parágrafo próprio na §10. flavorno/healthentregue pela lib; o0019passa a registrar prescrito × entregue (0019, java-micronaut §Específico/§Erros, versionamento §Receita, e2e-local §10). Retorno de repo de lib: oflavor({status, versao, flavor},"native"|"jvm") está implementado naxadm-comum-webe sai na 0.7.0 — herdado por todo app que sobe a dependência; até a 0.6.1, a última publicada, o campo não existe no artefato. O/infonão ganhou o campo (decisão do dono da lib — a pergunta se faz contra o/health; em código,VersaoInfo.flavorAtual()). Refinamento absorvido na receita: a property lê-se dentro do método, nunca numstatic final— sob native-image o inicializador estático pode rodar no build, ondeorg.graalvm.nativeimage.imagecodevale"buildtime", e o valor cacheado passa a depender da política deinitialize-at-build-time. O e2e native ganha a asserçãoflavor == "native": é o único gate que prova o ramo native (a JVM não exercita).
Corrigido¶
CENTRAL_DEPLOY_URLdotemplates/pipeline.ymlcarregava o flavor de build — apontavacentral-backend-jar.xadm.biz, exatamente o antipadrão que a constituição §9 proíbe ("URL que outro sistema consome não carrega flavor de build nem instância"), regra escrita depois do incidente em que parar o jar docentral-backendnuma promoção a native derrubou oPOST /api/ci/deployda frota (503). O template rastreado seguia com o valor velho: norma escrita, artefato da casa não seguiu. Agora aponta o domínio estávelcentral-backend.xadm.biz. Constituição 1.2.2 (1 rastreado re-carimbado:templates/pipeline.yml).- Doc do central envelhecida nos dois sentidos sobre capacidade de lib (java-micronaut §Erros).
Verificado nos jars publicados: o seam de
code/titleestá entregue desde oxadm-comum-web0.3.0 (o processor lêgetRootCause()e honra aPortadoraDeProblema) e o log incondicional do erro desde a 0.5.0 (5xx emERRORcom a causa-raiz, 4xx emDEBUG) — mas as duas passagens seguiam escritas como "pendência de repo de lib" / "o que a lib deve entregar no 0.3.0", mandando app manter@Replacesde fork que já não precisa. Corrigidas in-place (padrão da casa para doc que mente). Causa comum ao caso doflavor(prescrito e não entregue): prescrição do central cujo código mora na lib não tinha rastro de entrega — agora o0019, dono das versões, carrega o bloco prescrito × entregue, com a versão em que se verificou a ausência quando ainda não veio. Primeiro caso registrado como ainda não entregue: a hint dereflect-configdoSentryAppender(0022, java-micronaut §Native) — ausente até a 0.6.1, então app confere oMETA-INF/native-image/da versão que usa antes de largar a cópia local. - Varredura completa prescrito × entregue nos 8 módulos do
xadm-commons(0019, 0021, 0022, java-micronaut §storage/§Erros/§Native, versionamento, e2e-local). Com o rastro criado, a auditoria dos CHANGELOG e das tags dos módulos achou seis divergências, além doflavor: (a) oflavorestava anunciado como entregue na 0.7.0, mas o commit não tem tag — a afirmação foi corrigida para implementado, sai na 0.7.0; (b) a0021dava o filtro daSAIDAcomo "prescrição nova, pendência de repo de lib; adotantes contornam por convenção" — está entregue noxadm-mensageria0.2.0 (relay reivindica sótiposRegistrados(),enfileirarfalha-rápido); (c) a receita de consumo daxadm-comum-storagemandava montar ponte@Factory+@Replaces(S3Config)para o namespacegarage.*e excluir onetty-nio-clienttransitivo — a 0.2.0 renomeous3.*→garage.*(BREAKING) e já exclui o Netty no POM, ou seja, a receita mandava escrever o que a lib faz há um mês (reescrita, com o seamObjetoStoragesomado); (d) o0019descrevia o guard dos controllers flat como@Requires(property=…)da 0.6.0, mas a 0.6.1 reverteu para incondicional (nem property sobrevive ao AOT) — emenda registrada; (e) o filtro de request-id/MDC segue não entregue, agora com a versão da verificação (nenhuma até 0.6.1); (f) idem oreflect-configdoSentryAppender. O bloco do0019passou a listar entregue / implementado mas não publicado / prescrito e não entregue, e diz como conferir (CHANGELOG do módulo, tagxadm-<módulo>-vX.Y.Z,META-INF/native-image/do jar).
0.38.0 - 2026-09-06 (constituição 1.2.1)¶
Adicionado¶
- Régua de teste sobe em componente compartilhado; comportamento dependente de tempo nasce com
relógio injetável (constituição §6, ci-testes, java-micronaut §Testes; constituição 1.2.1).
Handoff de cobertura da
xadm-seguranca. A casa já tinha o princípio "defeito na lib alcança todo adotante" (0019, seguranca §Segredos, java-micronaut) mas nunca tirou dele a consequência para teste — agora a §6 diz que em componente compartilhado o risco é multiplicado pelos adotantes (o mesmo defeito vira N incidentes, chega a quem nem usa a feature, e às vezes o adotante não consegue contornar), logo "ramo defensivo de baixo valor" deixa de ser desculpa no que é segredo, auth ou controle de abuso. Em ci-testes, a consequência prática ao priorizar cobertura numa lib — e a observação de que cobertura creditada por e2e não fecha essa conta: o e2e passa pelo caminho feliz e deixa janela, reset e bordas descobertos (um filtro de rate-limit de login ficou com 43% na lib enquanto "tinha teste" no e2e). Segunda norma, cross-stack: comportamento que depende de tempo (janela deslizante, TTL, expiração de sessão/token, backoff, cutoff) nasce com relógio injetável — sem ele o ramo de expiração, que é o que decide segurança, só se testa comsleep(lento, flácido, primeiro a ser desligado) ou fica descoberto; mecânica Java (Clock/InstantSourcecom defaultClock.systemUTC(), leituras viaInstant.now(relogio)) em java-micronaut, e o Flutter já tinha o_nowequivalente. - Control-plane de deploy alcança recurso COMPOSE (emenda à 0026); deploy sem gate vira dívida
nomeada; URL de serviço não carrega flavor (0026, deploy.md, constituição §9; constituição
1.2.0). Handoff da inclusão das stacks PowerSync no control-plane. Emenda in-place na 0026
(mesmo padrão do bloco Evolução que ela já tinha): a âncora deixa de ser "slug de imagem" e passa a
ser o slug do artefato que aquele recurso consome — a imagem, quando o recurso puxa imagem; o
slug do repositório git, quando o Coolify builda do git. Motivo: recurso
build_pack=dockercompose(as stacks PowerSync: mongo +powersync-service+ init) não tem imagem —docker_registry_image_namevem nulo, o reconcile o pulava e oPOST /api/ci/deploydava404 no_deploy_target, deixando como único gatilho o auto-deploy do Coolify no push, isto é, deploy sem gate (e sync rule inválida sobe healthy). Consequências registradas:targetganhacompose; o contrato M2M tem dois modos (comimage, ouapp_id+targetobrigatório, com400 missing_anchor/400 missing_target); o reconcile de compose pula recurso sem git e ignorabuild_packdesconhecido; e a assunção nova — um repo git = uma stack compose, sob pena de duas stacks do mesmo repo colidirem na âncora e uma sumir do mapa em silêncio (a segunda exigeinstance). Divergência registrada, não resolvida: o handoff descreve a unicidade como(registry_slug, target, …)e adeploy.mdcomo(app_id, target, …)— coincidem enquantoapp_id == registry_slug; fica anotado para conferir contra a migration. Norma nova na §9: recurso deployável é disparado pelo control-plane com gate verde, auto-deploy do Coolify desligado — com exceção declarada e datada dos dois que o Coolify ainda builda do git (o site de documentação e o scraper node), dívida nomeada e não isenção. Segunda norma: URL que outro sistema consome não carrega flavor de build nem instância — apontarcentral-backend-jar.…fez parar o jar numa promoção a native derrubar o deploy da frota inteira (503); vale para webhook, callback e JWKS. - Fake
@Replacesvaza para todo o source-set de teste; cobertura mede-se em invocação única (java-micronaut §Testes, ci-testes §Definição de pronto). Feedback de app, duas normas cross-repo. (1)@Singleton @Replaces(X)escrito numa classe de teste é bean comum — entra no contexto de qualquer@MicronautTestdo source-set, então o teste do vizinho, que existia para exercitar oXreal, recebe o fake e passa verde sem provar nada. Correção ao feedback: as curas são duas, não uma —@MockBean(método/classe interna do teste) é a via oficial de isolamento (a doc do micronaut-test: o bean "will be active only for the scope of the test"; aceita fake escrito à mão, não exige mock-framework), e@Requires(property=…)+@Propertyserve quando o fake é compartilhado por várias classes. (2) Quarto modo de o relatório de cobertura mentir (irmão do class-major, do up-to-date e do gerado no denominador): invocação parcial re-executa oteste sobrescreve o.execcom a rodada menor, enquanto o XML e o HTML da rodada anterior continuam no disco e são lidos como atuais — regra: uma invocação só, com tudo que o gate roda, e limpebuild/reports/jacoco/build/jacoco/*.execna dúvida. Nota de rota: o arranjo em que esse erro aparece (ligar a integração por-PdockerTests) já é anti-padrão na casa — sem flag não há invocação parcial a confundir. (3) O sintoma virou heurística transversal em ci-testes: classe com cobertura ~0% sob teste de integração verde é sinal de investigar, não ruído — cobrir de novo o que já deveria estar coberto empilha teste sobre o defeito. - Repo de configuração deployável também tem gate; e query de bucket gateado cita o parâmetro de
papel (constituição §6 No CI (config), powersync §Config/§Deploy, 0029, e2e-local §1;
constituição 1.1.17). Handoff do repo de stack PowerSync de um cliente — repo sem CI nenhum,
onde nasceu um bug de sync rule que só não foi a produção porque o e2e o pegou por acaso, um dia
antes do deploy. Já era norma (feito no ciclo anterior): a pegadinha do
IN, "healthcheck verde não atesta config válida", o grep de"type":"fatal"no pré-flight e o pin da imagem. Novo: (1) cláusula na §6 — repo que não tem código mas é deployado (stack self-hosted,powersync.yamlde cliente, compose com config em volume) tem gate em todo push, porque o artefato dele é a config: lint das formas que a tag pinada rejeita + smoke de carga (sobe a imagem pinada só para ler a config, reprova pelo log, não pelo healthcheck; sem Postgres/Mongo — a validação acontece no carregamento). Repo de config não usa opipeline.ymlde app; o que validar é da página da tecnologia — receita concreta em powersync §Deploy, com a ressalva de que a lista do lint é datada pela tag (subiu o pin e a forma passou a valer, a regra sai: lint que sobrevive à versão que o motivou vira superstição). (2) Fail-closed por construção: toda query de um bucket gateado por papel cita o parâmetro de papel — query nova sem o filtro não quebra nada e entrega o dado a quem não tem o papel (fail-open por esquecimento); é regra estática, cobrada no lint, e cobre parte do que ninguém prova em runtime hoje (0029 ganhou a obrigação no lado do dado). Reconciliado: powersync §Deploy e e2e-local §1 diziam que o tierlocal/é "o único lugar onde a config é carregada de verdade" — com o smoke passa a ser compila (smoke) × filtra o que devia (e2e); deixar como estava faria a doc mentir sobre o gate novo. Sem template de workflow (deferido, REGRA Nº 3): só um repo exercitou a forma — quando o segundo tenant repetir o esqueleto, reavaliar umpipeline-config.ymlrastreado. - JaCoCo: o
micronaut-serde3.x GERA serializer por tipo — a doc dizia o contrário; e "endpoint é config, nunca constante" sobe à constituição §6 (java-micronaut §Testes, constituição 1.1.16). Feedback de app. Correção de FATO na doc: o bullet de código gerado afirmava que "o micronaut-serde é dirigido pela introspecção, não gera*Serializerpor tipo" — verdadeiro na era 2.x, falso desde a 3.x: verificado porjavapnomicronaut-serde-processor3.0.0, que tem o pacotesourcegene a classeSerdeSourceGenClassNamingcomPREFIX = "Serde"+SERIALIZER_SUFFIX/DESERIALIZER_SUFFIX, emitindoSerde<Pacote_Tipo>Serializer/…Deserializertop-level (a 2.15.1 do mesmo cache não temsourcegen). Como são top-level, não casam nenhum dos marcadores$…da nossa lista — foi por isso que a lista ficou incompleta e dois apps independentes reportaram asSerde*dominando o ranking de não-coberto (uma delas mascarando ~13 pontos de %). Norma corrigida: osexcludeslevam**/Serde*além de**/*$Introspection*,**/*$Definition*,**/*$IntrospectionRef*e**/*$Intercepted*. Elevação: endereço de serviço externo é CONFIG, nunca constante — base URL, endpoint e JWKS em property/--dart-definecom default de produção — passa a ser cláusula da definição de pronto (§6), ao lado do seam de config, porque vale cross-stack e era o que impedia testar o transporte de produção. - PowerSync: a FONTE (replicação lógica do Postgres) ganha dono no central; Testcontainers com flag
de servidor (powersync §Fonte, coolify §Banco, java-micronaut §Testes). Handoff de cobertura do
bi-comercial-xls: a peça de pior cobertura do repo administra o slot de replicação lógica que o PowerSync consome — e o central nunca documentou a fonte (greppor replicação lógica/slot/wal_levelemdocs/= zero; só existia um meio-bullet sobreALTER ROLE … REPLICATIONno Coolify). Conhecimento sem dono, do tipo que já mordeu a casa (cache do nginx,container:do CI). Registrado, com os requisitos verificados na doc oficial do PowerSync:wal_level = logicalé flag do servidor (vale para todos os bancos dopostgresql18e exige restart — decisão de plataforma, não ajuste de app), publication de nome fixopowersync(FOR ALL TABLESsó em dev), role comREPLICATION/BYPASSRLS/SELECT, e o slot como peça frágil: ausente = replicação parada (sync não chega, o app não acusa erro) e inativo = WAL retido, enchendo o disco do servidor compartilhado — slot órfão de consumidor morto é incidente de plataforma, não do app. Reset de slot é destrutiva (runbook, nível 5). E, no lado do teste: alvo que exige flag de servidor sai do singleton — container próprio comwithCommand(... -c wal_level=logical), com a flag espelhando produção (ligar no singleton mudaria o ambiente de toda a suíte e faria o verde atestar um servidor que não existe). Nada mais do handoff era gap central: a "nota de ambiente" (@Tag("docker")atrás de-PdockerTests, "aceito hoje") contradiz norma vigente — é a cobertura órfã proibida em ci-testes/java-micronaut, com guarda nopipeline.yml, e a razão histórica (dind do runner Forgejo) morreu na 0027; "lógica só exercitada de raspão pelo e2e" e "cobertura não é meta de número" também já são norma. - Micronaut: default de placeholder com URL precisa de crases (bug shippado na nossa receita),
transporte próprio testa com fake em porta efêmera e URL de externo nasce em property
(java-micronaut §Testes/§Armadilhas, ci-testes, e2e-local,
templates/pipeline.yml; constituição 1.1.15). Handoff de cobertura docentral-backend. Achado principal (bug nosso): a receita do seam de JWKS mostrava${AUTH_FIREBASE_JWKS_URL:https://…}sem crases — e oDefaultPropertyPlaceholderResolverre-parseia um default que ainda contenha:como nova expressão (ESCAPE_SEQUENCE = (.+)?:entre crases). Medido em harness commicronaut-inject5.0.4 real e property ausente: sai//www.googleapis.com/x/y(ohttps:vira nome de property) e${DB_URL:jdbc:postgresql://localhost/x}sai//localhost/x. Com a env setada o default nem é usado — por isso passou despercebido: quebra só no fallback (dev, teste, deploy sem a variável). Corrigidas as duas cópias da receita (java-micronaut, e2e-local), armadilha nomeada ao lado da do placeholder aninhado, e a guarda dopipeline.ymlestendida: bloqueia default com://sem crase, avisa nos demais:(cascata${A:B:padrao}é legítima — confirmado no mesmo harness). Guarda exercitada em 11 cenários rodando a cópia extraída do YAML. Norma generalizada: URL de serviço externo vive em property com default de produção, nunca em constante — o seam deixa de ser privilégio do validador de JWKS (constante hardcoded é o que impede testar o transporte). Norma nova: transporte próprio (SMTP porSocket, HTTP à mão) testa-se com o servidor fake na própria JVM em porta efêmera (ServerSocket(0),com.sun.net.httpserver), sem docker — cobre ordem do diálogo, umRCPT TO:por destinatário, dot-stuffing, headers, 5xx e porta fechada. E o princípio que faltava (ci-testes): e2e como única prova de um componente é gap, não cobertura — o inverso do "escreva no nível mais baixo". Já era norma da casa (só ack): excluir o gerado do denominador do JaCoCo — a lista de marcadores ganhou**/*$Intercepted*(AOP). - Flutter:
fakeAsyncparaTimer/debounce,dev_dependenciesde transitiva e contrato de arquivo estático (flutter §Testes, 0029 §Consequências). Dois feedbacks de app, ambos de stack. (1) O seam do debounce é a ZONA, não um parâmetro — ofakeAsyncintercepta oTimerhardcoded da produção, logo não se injeta duração só para testar; verificado em sandbox comfake_async1.3.3 (elapse(299ms)não dispara, o milissegundo seguinte dispara). A armadilha do callbackasynctem duas faces, e o feedback só citava uma: semawaitno retorno, nada depois do primeiroawaitroda e o teste passa vazio (falso-verde — o pior caso); comawait, oFuturenunca completa e o teste trava até o timeout (as duas provadas em sandbox). (2) Package importado emtest/vai emdev_dependenciesmesmo vindo transitivo — regra nomeada, vale paramatcher/collection, não sófake_async: cadeia confirmada na fonte (depend_on_referenced_packagesestá nolints/core.yamlque oflutter_lintsinclui; acusa em info;flutter analyze --helpda 3.44.0 mostra--fatal-infosdefault on) → import que compila derruba o gate. (3) Teste de contrato de arquivo estático lê o arquivo real do disco (dart:io), nunca uma cópia emtest/— fixture duplicada testa a fixture e o artefato que vai ao ar drifta em silêncio (mesma família do "fixture capturada, não inventada", §6) — e afirma o conjunto de chaves, não os textos: amarrar rótulo de UI aolabeldo contrato faz de corrigir uma palavra na tela uma mudança no contrato do consumidor. A 0029 ganhou o ponteiro no lado do dono (o catálogo é artefato de deploy: trave-o por teste). - PowerSync: sync rules válidas ≠ serviço saudável — o pré-flight passa a ler o log (powersync
§Config/§Deploy, e2e-local §1,
templates/xadm-release-skill.md; constituição 1.1.14). Feedback de app, achado no e2e antes de um deploy: 15 queries de gate por papel iam para produção inválidas. Duas pegadinhas registradas. (1)auth.parameter('x') IN (…lista literal…)não existe — o README dopackages/sync-rulessó aceita<coluna> IN auth.parameter('array')eauth.parameter('x') IN <coluna array JSON>; parâmetro contra lista de literais é rejeitado comofatal, e a forma que vale é igualdade encadeada com parênteses. Correção ao feedback: a regra não é "INé proibido" —id IN auth.parameter('ids')é a forma canônica e continua valendo; a versão genérica mataria o uso certo. (2) Sync rule inválida não derruba o serviço — sobe, responde/probes/liveness200 e só loga o erro (as probes documentadas cobrem "serviço vivo", e o changelog do serviço confirma que erros de compilação ficam persistidos pós-deploy); o sintoma chega horas depois como "não sincronizou nada", com cara de bug de app. Logo odocker compose up --waitnão é o gate inteiro: o bloco Docker Compose do pré-flight ganhou o passo de ler o log da stack de pé (docker compose … logsbare + inspeção porRead/Grep, nunca pipe) e reprovar em"type":"fatal"/"errors"— escrito genérico ("serviço que carrega config em runtime"), não só PowerSync. E o e2e-local §1 fixa que config carregada em runtime só é validada no tierlocal/(mesma imagem pinada, config do cliente) → mudou sync rule, roda o e2e daquele cliente antes de taggar. Correção à sugestão operacional do feedback: grep em pipe fura o allow-list — a doutrina da casa é comando bare + inspeção pela ferramenta. Achado extra:npx powersync validateroda em self-hosted e dá linha/coluna do erro no YAML — registrado como atalho do dev, nunca substituto (quem decide é o binário da tag que produção roda).
Alterado¶
- Subject do commit de release do central passa a carregar as duas versões —
chore(release): vX.Y.Z site — C.C.C constituição(era sóchore(release): vX.Y.Z). As duas versões são desacopladas (§5) e ogit logsozinho não dizia qual release de site carrega qual constituição — agora diz, sem abrir o CHANGELOG. Sem risco de CI: o único consumidor do subject é odecidedotemplates/pipeline.yml, que casa prefixo ancorado (^chore\(release\)). Só o central —templates/xadm-release-skill.md(app) seguechore(release): vX.Y.Z, porque app não bumpa constituição.
0.37.0 - 2026-09-04 (constituição 1.1.13)¶
Adicionado¶
- Gate de numeração de seção —
checa-numeracao.py(constituição §6, build-site.yml, pré-flight da/xadm-release). Bug meu, achado ao renumerar ae2e-local.md: a página carregava duas seções## 12.havia meses e nenhum gate acusava — promkdocs --strictsão dois headings válidos com slugs distintos, ocheca-navolha arquivo órfão e ocheca-admonitionolha marcador cru; foi achada no olho, o gate que a §6 diz não servir (mesma classe do admonition indentado errado, item 92). O checker (stdlib) exige, na página que numera seções, sequência 1..n sem duplicata e sem salto, por nível de heading; ignora heading dentro de bloco de código (a doc exibe markdown de exemplo) e pula página sem numeração.testa-checa-numeracao.pycobre 9 casos e mata os dois mutantes óbvios (tirar o skip de cerca; achatar os níveis). Central-only, não publicado — a convenção de seção numerada é das páginas longas daqui; app não tem norma de numeração (decisão revisável se surgir caso). - Terceiro tier de e2e:
staging/— dado real de produção, avaliação humana (e2e-local §1/§2/§11, constituição §6, ci-testes). A casa passou a ter três tiers:local/enative/são zero interação humana (um comando, exit 0/1, schema fresco) e ostaging/é explicitamente manual — builda, sobe e para, para provar o que os outros dois não provam: a migração aplicada sobre o dado real (constraint violada por linha antiga,NULLonde o código novo assumeNOT NULL) e a jornada que só um humano julga. Não roda em CI, não propaga veredito. Não reabre o "staging" que a §6 veta — o vetado é ambiente deployado + clique de operador; este roda local/docker e toca produção uma vez, em leitura. Regras obrigatórias registradas: prod só-leitura no pull; dump cru é dado de produção em disco local (gitignored, apagado no fim do mask, nunca commitado, não sai da máquina — o artefato reutilizável é o seed mascarado); anonimizar antes de qualquer app subir, incluindo segredo de provedor (DSN, chave de analytics, id de recurso de deploy); guarda do mask em duas camadas (heurística anti-regressão + fail-closed em coluna nova não declarada, que é o que fecha o drift de schema); guard de prod fail-closed lendo estado efetivo (config + env herdada + env de cada app), nunca string montada de constante local;builds-latest/manifesto.mdversionado; e achado automatizável desce para o tier automático — o manual não acumula cobertura própria. A §2 passa a mostrar o layoutetc/tests/<tier>/<suite>/, e a numeração das seções deixa de ter duas§12. - Norma: edição em lote por script escreve BYTES, e o fechamento confere o
git diff --numstat(engenharia/agentes). Achado na própria sessão: reescrever.mdcompathlib.write_textno Windows troca a EOL do arquivo inteiro, e oCLAUDE.md— com duas linhas de mudança real — saiu como +3080/−3080. A causa de o git não absorver isso sozinho apesar docore.autocrlf=true: o arquivo tinha um CR solitário (CR sem LF), e nele o git desliga a normalização de EOL — o vizinho sem CR solitário não reproduz o sintoma. O CR solitário veio do próprio agente, ao escrever a sequência de duas letras num heredoc (numa linha que citavatr -dde CR); corrigido no acervo. A norma registra as duas defesas (montar a barra em runtime; conferir o byte, não ogrepnum pipe) e torna onumstatverificação de fechamento. - Duas decisões novas de auth de usuário no central + as normas §6 do staging de acesso
(0029, 0030, constituição §7, seguranca, java-micronaut, flutter, powersync). [0029] — o app
declara seus papéis (
roles-catalog.jsonservido por ele; JSON estático no Flutter web, rota anônima no Micronaut) e ocentral-backendingere por upsert: papel sumido inativa (não apaga — grants sobrevivem) e o sync reporta o que inativou; catálogo é público, logo sórole+label; o ingest exigeapp_idcasando o alvo, https/host doproduction_url, timeout e limite. Mata a curadoria manual, que era a fonte do drift (papel novo fora do picker, papel morto ofertável). [0030] — todo app que autentica pelo central usa um projeto Firebase (o da X-Adm): habilitar provider é config única — Google e Apple já em produção —, app novo registra app no projeto existente. Consequência que faltava estar escrita:iss/audidentificam o projeto, não o app ⇒ autorização por app vem sempre do grant, nunca do token; e Apple manda nome só no 1º login + e-mail podendo ser relay ⇒ identificar porsubject, UI tolerante. - Normas de acesso/UI vindas do staging: superfície
/api/**de app com UI em outra origem nasce com CORS, com origens em env (<APP>_CORS_EXTRA_ORIGINS), nunca hardcoded nem*com credencial (java-micronaut §Específico + piso §Authn); negativa de acesso carrega o contato do admin no corpo do 403 (usuário pendente não tem JWT para consultar) e pendente ≠ rejeitado; tela de acesso do Flutter mostra id/nome/versão, rota administrativa entra no menu, e a UI tolera nome ausente/e-mail de relay; o JWT de sessão grava-se SÍNCRONO no login — o credential-provider do PowerSync lê, não é o único a escrever (era 401 por corrida). - Login social no Flutter web é POPUP, e os headers de cross-origin isolation saem (flutter
§Deploy web). O
signInWithRedirecttrava o boot sobCOEP: require-corp(iframe de auth bloqueado ⇒ tela branca). Verificado na doc do PowerSync: a partir do SDK Dart/Flutter 2.2.0 o OPFS roda em Chrome/Firefox/Safari e "these headers can be removed" — cura é subir o SDK e tirar COOP e COEP, não o meio-termoCOOP: unsafe-nonecomCOEPmantido, que perde o isolamento (exige COOPsame-origine COEP) e mantém o custo. Se o COOP tiver de existir,same-origin-allow-popups;restrict-propertiesnão é opção (Chrome em hold desde abr/2025). -
Piso do ArchUnit vira guarda de CI + a armadilha do
@Requires(property=…, notEquals="")(templates/pipeline.yml, engenharia/java-micronaut). A norma do piso (ASM ≥ 9.8 / ArchUnit ≥ 1.4.1 em Java 25) e a defesa anti-vácuo (isNotEmptysobre o import) existiam desde o item 113 — nada as verificava, e um repo de app ficou meses com a guarda de fronteiras dormindo verde (ArchUnit abaixo do piso pula classe que o ASM não conhece: a regra roda sobre 0 classe e passa vácua). Guarda nova no jobgate: resolve a versão do ArchUnit (coordenada literal ou alias dolibs.versions.toml) e reprova abaixo do piso — fato objetivo,::error::; a ausência de asserção anti-vácuo na suíte só avisa (::warning::) — heurística, a defesa pode estar escrita de outro jeito. Versão não resolvível vira nota + segue (backstop humano no §3b da/xadm-docs). -
Extração de texto em Dockerfile = CR-safe +
gitattributes-flutter(constituição §3, templates.md, app-novo, /xadm-docs, engenharia/flutter). A cobertura de CRLF da casa era toda gradlew-específica (item 97: shebang#!/bin/sh\r); faltava nomear a classe — CRLF quebra qualquer extração de texto por shell (sed/grep/awk) de arquivo versionado numdocker build, não só o gradlew. Achado de app: o Dockerfile web do central-ui extrai a versão dopubspec.yamlporsedsem filtrar\r→ quebra em checkout Windows (CRLF); CI Linux passa,docker buildlocal no Windows quebra — falha frágil, invisível na CI (o irmão bi-comercial já lia oversion.jsongerado, CR-safe). A receita web do central usajq(tolerante a CRLF) e não shippa o bug; a norma cobre o próximo app. Cura em duas pontas (precedente item 155): cinto| tr -d '\r'no comando + suspensório novo template rastreadotemplates/gitattributes-flutter(pubspec.yaml text eol=lf), par Flutter dogitattributes-java. Nota na receita Flutter §Config, princípio nomeado em §3. -
Extração de texto do site vigente à prova de cache-CDN na
/xadm-docs(templates/xadm-docs-skill). O passo 1 tratavaconstituicao-versao.txtcomo fonte única da versão vigente; o CDN serviu essa.txtstale (1.1.4) enquanto omanifesto.jsonveio fresco (1.1.8) — sem defesa, o early-exit "em dia" dispararia na.txtstale e mascararia a defasagem inteira (gate JTE incluso). Cura em duas pontas (item 155): cache-bust obrigatório (?cb=<ts>+Cache-Control: no-cache) nos dois curls + cross-check.txtvs campoconstituicaodomanifesto.json(mesma publicação, carimbo autoritativo — item 151); divergência = aborta alto, nunca reporta "em dia" pelo menor. - CLI Python: bare primeiro,
python -m <mod>no fallback (engenharia/ferramentas, publicar-docs, /xadm-release). O entry-point bare (mkdocs, gates.py) nem sempre resolve no PATH — Windows com Python da Microsoft Store não põe oScripts\no PATH →mkdocs --versiondá "command not found" mesmo instalado. A/xadm-releaserodamkdocs build --strictlocal antes de pushar; bare-só trava a cerimônia numa máquina Windows (ou pula o gate em silêncio). O allow-list já listapython -m mkdocs *; faltava a prosa mandar preferir a forma-módulo quando a bare não resolve. Regra genérica em ferramentas §allow-list, nota em publicar-docs §Ambiente local, parêntese no gate da /xadm-release.
Alterado¶
auth→central-backendem toda a doc (constituição §7, central-de-apps §Papéis, ADRs 0005/ 0008/0022, powersync, java-micronaut, coolify, garage, publicar-docs,/xadm-setup).authera o nome antigo do mesmo serviço; a tabela §Papéis passa a ser indexada porcentral-backend.xadm.biz(host legadoauth.xadm.bizsegue roteado — deploy), e a linha docentral.xadm.biznomeia ocentral-uie registra que são origens distintas (a razão de o CORS existir).
Corrigido¶
@Requires(property=X, notEquals="")não exige a property — ausente ele PASSA (engenharia/java-micronaut §Armadilhas + §Específico). Verificado na fonte (MatchesPropertyCondition.matches,micronaut-inject5.x): property ausente e semdefaultValue, o ramoNOT_EQUALSdevolvetrue— o bean sobe e o@Valuesem default só estoura quando o bean é instanciado (singleton é lazy) → 500 no primeiro request, com o startup verde. O idiom só entrega "setada e não-vazia" com a chave declarada noapplication.yaml(mesmo vazia). Regra: feature opcional guarda-se compattern=".+"(já era a receita da casa, agora com o porquê e o contraste). Sem gate: cobrar "property declarada" exigiria caminhar YAML aninhado — ponto cego declarado.
0.36.4 - 2026-09-03 (constituição 1.1.8)¶
Adicionado¶
-
View JTE em app native: reflect-config auto-gerado + hard gate (constituição §5, 0025, java-micronaut, templates/pipeline.yml). A doc prescrevia a lista de
reflect-configdas templates JTE à mão e a chamava "não drifta" — falso: view nova esquecida = classe podada no AOT = 404 só no native, passando por unit/integração e e2e-native (que amostra telas). Foi 404 real em produção (int-sascar,atencao/incidentes). Martelo: o padrão da casa passa a ser a extensão oficial que auto-enumera todas as templates —jteExtension('gg.jte.nativeimage.NativeResourcesExtension')+ depjteGenerate('gg.jte:jte-native-resources:<versão>')(gerareflection-config.jsoncompleto no build); lista à mão proibida. Hard gate novo nopipeline.yml(jobgate, escopo JTE+native): reprova se falta a extensão (estático) ou se oreflection-config.jsonnão cobre toda*Generated(output-compare) — belt-and-suspenders porque prod-404 passou por todos os tiers. Corrige o claim "não drifta" em java-micronaut §UI/JTE + 0025. -
/xadm-releasescenario-aware — classifica o diff e roteia a ação (0027/0004, templates/pipeline.yml + xadm-release-skill.md). A skill vira o cérebro: classifica o diff desde a última release e recomenda — docs/auxiliar = não é release (push, sem bump/tag/binário); código = release por tag com trailerDeploy:; só-jar/só-native = release patch do alvo. Odecidedopipeline.ymlna tag passa a LER o trailer (que a skill já cravava e ninguém lia — a causa do maxsul-pied, em que fix só-docs disparou deploy completo): liga só os alvos do trailer, fallbackapp.json build.targetsquando não há (nunca "sem binário" — Flutter/central seguem deployando);decidecomfetch-depth: 0p/ ler o corpo do anotado. Fix docs-only republica a versão vigente além dedev/(o site vivo redireciona pra maior SemVer — refina o 0004: doc de versão vira mutável p/ correção de texto), sem rodarpublica-versao.py. Corrigida a referência stalebuild-deploy.yml→pipeline.ymlna skill. -
Higiene de nomes & disciplina de rename da frota (constituição §9, 0024/0026/deploy.md/coolify.md/ glossário). O nome do recurso Coolify — único dos 4 nomes por app sem norma — passa a ser derivável do slug:
<slug>-[<instance>-]<target>-pull, kebab-case,-pullmarca control-plane,<instance>só em app multi-instância (integrador). O reconcile (0026) casa por regra única^<slug>-(<instance>-)?(jar|native|web)-pull$no lugar dos 4 padrões ad-hoc que a frota nem seguia. Instrução determinística de normalização da frota (deploy.md, ação de infra §3 — executada por operador com API do Coolify: lista→computa→rename in-place, uuid estável). Nova seção §Renomear um slug ou recurso em coolify.md: slug rename obriga limpar orphan Garage + reapontar todo ponteiro pro slug velho + re-semear reconcile, no mesmo movimento; guard de orphan central-side declarado FUTURO (§3). Glossário desambigua "slug" (página × app/0024). -
Migração de banco backward-compatible — norma expand/contract + guarda que avisa (constituição §6, java-micronaut §Flyway). Flyway CE é forward-only e "rollback" na casa = redeploy da imagem anterior; a migration já rodou e não se desfaz, então o schema novo não pode quebrar o app velho. DDL destrutivo (
DROP COLUMN/DROP TABLE,RENAME,DROP CONSTRAINT) segue expand (para de usar, schema fica) → contract (dropa na release seguinte, só depois de estável); RENAME em fases (add-new→backfill→dual-write→drop-old). Marcador canônico-- destrutivo-ok: contract; ...no.sql. Guarda nova nopipeline.yml(jobgate): avisa (não bloqueia — o marcador é auto-declarado) quando acha DDL destrutivo inequívoco sem o marcador;SET NOT NULL/ALTER TYPEficam fora (seguros em coluna nova/widening). Elevado à definição de pronto (§6). Satélites: ci-testes (guarda irmã) e coolify §Rollback (o rollback recria a imagem, não o schema). Origem: incidente de prod (DROP de coluna, rollback não recriou). Frota adota via/xadm-docs.
Corrigido¶
- Reconcile do control-plane casa por SLUG DE IMAGEM, não por nome do recurso (deploy.md, 0026,
constituição §9). Auditoria empírica da Fase 2 em produção (2026-09-03) refutou o modelo: o reconcile lê
o
docker_registry_image_namede cada recurso e casa contraregistry_slug(==app.json slug, 0024) — o nome canônico<slug>-[<instance>-]<target>-pullé derivado/legível, não a chave. Prova:central-ui/onpetro-bicasavam com nome fora da fórmula. Consequência: os 20 renames de recurso foram higiene (legibilidade), não pré-requisito do reconcile — §Normalização remarcada CONCLUÍDA (frota 100% canônica,app_deploy_targets21/21). Propriedade nova explicitada: casar por slug de imagem desacopla o control-plane do nome do recurso (cutover/rename não quebra o mapa). Colunaregistry_slugadicionada ao esquema documentado doapp_deploy_targets. - Ponteiros stale pro slug velho
integradorno próprio central — o renameintegrador→integrador-server(0024) deixou 6 links servindo do orphan morto: o exemplo do_importado/empublicar-docs.mde 5 linksaplicacoes/integrador/projeto/ arquitetura/(integracao.md ×2, powersync.md, 0018, 0007 ×2). Reapontados pro slug canônico. Otemplates/pipeline.ymlganha bloco comentado de fetch_importado/(consumidor de contrato descomenta — o--8<--pendurava/quebrava o--strictsem o step). Sem pin (0014 intacta): fato importado é o atual do dono; estável = versão de API (/api/v1/).
0.36.3 - 2026-09-01 (constituição 1.1.4)¶
Corrigido¶
tinicomo PID1 nos Dockerfiles (dockerfile-java+dockerfile-java-native) — sem um init de PID1, ocurldoHEALTHCHECKreparenteia pro app (PID1) e vazacurl <defunct>sem teto (116 zumbis medidos numa VM de prod, ago/2026); JVM e binário native não colhem órfão de PID1.tinicolhe o zumbi e repassaSIGTERM(shutdown intacto). Guarda nova nopipeline.yml(jobgate):ENTRYPOINTem forma exec começa com um reaper de PID1. Nota cruzada em java-micronaut §Deploy, 0022 §Topologia e coolify §Baseline do host (o zumbi era pista falsa de Testcontainers/dind órfão). Frota adota via/xadm-docs+ redeploy.
0.36.2 - 2026-08-31 (constituição 1.1.3)¶
Removido¶
- Fábrica de imagens de CI (
ci-images) aposentada da superfície viva (constituição 1.1.3). Com o CI 100% GitHub Actions (docs/decisoes/0027-ci-github-actions.md), a toolchain vem das actions (setup-java/subosito/flutter-action) e amatriz-baseline.jsonnão gateia mais nada. Removidos: a pastaci-images/(Dockerfiles base/java/flutter,build-push.sh,computar-matriz.py,matriz-baseline.json), o publish depadroes/matriz-baseline.jsonnoDockerfiledo site, e o runbookinfraestrutura/fabrica-imagens-ci.md(+ nav). As decisões 0003/0022/0027 ficam como registro histórico (a fábrica resolveu problemas reais; 0027 é a superseding) — a purga é da superfície viva, não do histórico de ADR. - Workflows Forgejo legado removidos (constituição 1.1.3):
templates/ci.yml,templates/docs.yml,templates/release.yml— superados pelos jobsgate/docs/release-checkdopipeline.yml(0027) e agora que o runner Forgejo aposentou (0 apps), saem do manifesto (tirados deRASTREADOS). Varredura das páginas vivas trocandoci.yml/docs.yml/release.yml→pipeline.yml(os ADRs emdocs/decisoes/ficam como história;build-deploy*.ymlseguem legado). Opipeline.ymlsegue rastreado.
Alterado¶
- Refs vivas à fábrica removidas (constituição 1.1.3):
constituicao.md,glossario.md(entrada "Fábrica de imagens de CI"),coolify.md,forgejo.md,ci-testes.md,java-micronaut.md,publicar-docs.md,app-novo.md,flutter.md,java.md,0004, e os templates rastreadosapp.json/xadm-docs-skill.md/xadm-release-skill.md(a paridade Linux de teste OS-desabilitado passa a citareclipse-temurin:<v>-jdkno lugar da imagemci-javaapagada). Bump PATCH (3 rastreados re-carimbados).
Corrigido¶
manifesto.json:mudou_emfantasma0.38.0→1.0.0(templates/app.json,xadm-docs-skill.md,xadm-release-skill.md). Os 3 mudaram na migração 0027, carimbados0.38.0antes de a constituição ser renumerada de 0.x pra1.0.0(mesma leva); os hashes não mudaram depois, então ogera-manifestopreservou omudou_emhistórico — mas0.38.0nunca virou release da constituição (renumerada pra1.0.0). O CHANGELOG rotula a mudança(constituição **1.0.0**), então o passo-2 da/xadm-docs(relevância-por-stack, que procura a string demudou_emno CHANGELOG) buscava0.38.0, não achava, e caía no fallback de diff. Alinhado ao rótulo real. Sem bump: correção de metadado, nenhum hash de template mudou (gera-manifesto --checkverde). (§6, candidato do Gustavo.)
0.36.1 - 2026-08-31 (constituição 1.1.2)¶
Alterado¶
pipeline.yml: 3 otimizações de minutos faturados no GitHub Actions (constituição 1.1.2). (A)build_nativeganha um passo libera-disco em paralelo (&+wait) antes do build — native-image come ~6 GB e o/cheio arrastava o build até o timeout; (B)cache-from/cache-todo gha ganhamscope=por flavor (jar/native/web) — sem o scope os três dividiam um cache só e se evictavam no teto de 10 GB/repo; (C) jobdocscacheia os downloads de pip (wheels) + npm viaactions/cache(chave = hash do workflow, reinvalida no bump de versão de ferramenta). Não foi adicionado deps-cache-dance no native:mode=max+cache-dance ali já estourou o timeout (exporting to imagede stages GraalVM multi-GB) — o comentário do job foi reforçado com o caso. (§6, handoff de app.)
0.36.0 - 2026-08-31 (constituição 1.1.1)¶
Corrigido¶
- Bootstrap de deploy do central invertido (constituição 1.1.1; emenda à 0026, decorrência
da 0027). A cláusula original — "o central deploya direto na API do Coolify, sem auto-referência"
— ficou inexequível com a CI 100% GitHub (runner de IP arbitrário × API restrita ao IP da casa; a
ponte SSH que contornava foi removida). Invertida: o central se deploya pelo próprio
/api/ci/deploy(roda dentro da casa → alcança o Coolify; job byte-idêntico ao de qualquer app), com break-glass (central fora do ar → deploy manual pela UI/API do Coolify) como única exceção. Reconciliados 0026 (§Evolução), constituição §9,infraestrutura/deploy.md(§Bootstrap + runbook break-glass). Ocentral-backendremoveu a guardaself_deploy_refused(RD local). Também: token do/_sync(0028) renomeadoDOCS_SYNC_TOKEN→DOCS_API_TOKEN(alinha a<DESTINO>_API_TOKEN).
Adicionado¶
- Docs sync event-driven pelo central (constituição 1.1.0; decisão 0028). Mata o poll de
rsync do
sync-apps.sh(a cada ~90s, LIST/HEAD massivo no garage — o maior hog de CPU do host, item 134): o publish dispara o sync. O jobdocsdopipeline.ymlavisa o central (POST /api/ci/docs-published, best-effort não-fatal); o central cutuca o endpoint internoPOST /_sync?slug=do container docs (busybox httpd em:8080, BearerDOCS_API_TOKEN); osync-apps.shsincroniza só aquele prefixo (funçãosync_slug+ debounce trailing-edge + guarda de slug anti-traversal preservada). O poll de 90s vira backstop (reconcile no boot + poll de 60min). Novoscripts/sync-listener.{cgi,sh}+scripts/testa-sync-apps.sh(guarda de slug, no gate) + delta do Dockerfile e do jobdocsdopipeline.yml. Fase 2 (endpoint/api/ci/docs-published) = central-backend, outro repo. - CI/CD 100% GitHub Actions —
pipeline.ymlúnico (constituição 1.0.0; decisão 0027, emenda 0003/0026). Fundeci.yml+docs.yml+release.yml(Forgejo) +build-deploy*.yml(GitHub) num workflow à la carte com 3 variantes provadas (Java native-canary, Java jar-only, Flutter web) por bloco opt-in deapp.jsonbuild.targets; toolchain pelas actions (setup-java/setup-gradle/subosito/flutter-action), Testcontainers no docker nativo, deploy control-plane (0026, inclusive web), aviso de falha = notificação nativa do GitHub (email),DOCS_S3_*= secret por repo (conta user-wide, não org). Novotemplates/pipeline.ymlrastreado;ci.yml/docs.yml/release.yml/build-deploy*.ymlficam legado até 0 apps no runner Forgejo. A fábricaci-images/0003 é superseded para CI (semmatriz-baseline.json);forgejo.mdreescrita (git host + push-mirror + Release-artefato, sem CI),fabrica-imagens-ci.mdarquivada; o CI do próprio central migrou (.github/workflows/build-site.yml;.forgejo/workflows/removido).has_actions=falseno Forgejo é passo obrigatório da migração. Reconciliados constituição §2/§5/§6,ci-testes,app-novo,publicar-docs,templates.md,flutter,java-micronaut,stack,versionamento,glossario,app.json/xadm-docs-skill/xadm-release-skill(rastreados). Golden test Flutter normatizado fora do gate (visual regression; local + e2e). - Taxonomia de modos de deploy em
infraestrutura/deploy.md: dois eixos (-pulloff-host no CI ×-builderbuild no host do Coolify) × artefato — Java/Micronaut =jar-pull+native-pull, Flutter =web-pull(control-plane), auxiliar Node (ex. Sulplata HO Scraper) =node-builder, docs =web-builder(os dois-builderficam fora do control-plane por ora). - Guarda
ESCRITA_DECLARA_CONSUMESemengenharia/java-micronaut§Guarda de fronteiras: regra ArchUnit para app server-rendered — controller de view (com@View/@Produces(TEXT_HTML)) declara@Consumesnos write-endpoints (@Post/@Put/@Patch), fechando a classe do 415 silencioso no submit de<form>. Escopo estreito (só view; API/api/**puro-JSON fora); cobra a anotação presente, não o media type. Feedback §6.
0.35.1 - 2026-08-30 (constituição 0.37.3)¶
Adicionado¶
- Control-plane de deploy no central-backend (ADR 0026 + §9 nova) — o CI dispara o deploy por API
M2M, não por ponte SSH. O deploy off-host passa a ser orquestrado pelo central-backend
(
POST /api/ci/deploy, token dedicado por filtro próprio em/api/ci/**), que resolveapp_id+target → recurso Coolifypor um mapa auto-curável (tabelaapp_deploy_targets+ reconcile por convenção de nome, nunca por uuid à mão) e chama a API do Coolify; a ponte SSH + forced-command + allow-list saem. Motivo: o allow-list ficava órfão a cada cutover blue-green (uuid novo criado, uuid velho deletado), recusando a frota inteira — só não quebrou porque todo deploy foi via API direta. Constituição §9 (Entrega / Deploy) nova,docs/decisoes/0026-deploy-control-plane.md, páginadocs/infraestrutura/deploy.md, emenda adocs/decisoes/0022-native-image-alvo-de-deploy.md(topologia da ponte → ponteiro pra 0026). Bump PATCH constituição 0.37.2→0.37.3. - Regra "quem builda decide onde a config de build-time vive" (build off-host / pull-only).
Com jar/native buildando off-host e o Coolify só puxando (0.37.0), o build-arg do Coolify
morre pro que virou pull-only: config de build-time (
ARG,--dart-definedo Flutter web) passa a viver onde o CI alcança (ARGdefault no git /docs/app.json/ GitHub secret), env de runtime fica no recurso, e a build-time vestigial no recurso pull se limpa. Constituição §5 (corolário),coolify.md§Build Arguments (reescrita pros dois regimes: Coolify-builda × pull-only + receita de migração mover+limpar) + checklist,flutter.md. Bump PATCH constituição 0.37.0→0.37.1. (§6, feedback de app.) - Alvo
webdo build off-host —templates/build-deploy-web.yml(Flutter web → nginx). Provado por 3 repos (central-ui,vantroba-bi,onpetro-bi) com o workflow byte-idêntico exceto o slug → templatizado (par dobuild-deploy.ymldo server, single job). Builda off-host no GitHub, publica:web-amd64, dispara a ponte SSH com o UUID lido dedocs/app.jsondeploy.web.coolify_uuid(≠ do server, que usa secret). Odeploydoapp.jsonpassa a ser lido pelo web (era só forward-looking); scaffoldapp.json+ contratopublicar-docs,flutter.md(receita web→nginx),templates.md, mapa da/xadm-docs,coolify.md. Dockerfile web fica como receita por app (o fluxo de sourcemap difere GlitchTip×Bugsink — não templatizado). Bump PATCH constituição 0.37.1→0.37.2. (§6, feedback de app.) - Armadilha
java-micronaut§Banco — field-add emrecord @MappedEntityé breaking no ctor canônico posicional (o Micronaut Data binda esse ctor; somar campo quebra todonew Entity(...), 27 call-sites num piloto, maioria seed). Prescreve factory de test-data pra colapsar o raio a 1 lugar (norma, cross-link §Testes) + tática de migração pontual (sedno rabo uniforme). Página dev, sem bump. (§6, feedback de app.)
0.35.0 - 2026-08-29 (constituição 0.37.0)¶
Alterado¶
- Build off-host vira o default da casa (jar E native). O
shadowJarainda buildava no host de produção (Coolify Build PackDockerfile,docker buildna VM) — o mesmo build-spike (CPU + I/O + build-cache +13,4 GB/dia) que foi a face do freeze e que o native já evitava. Agora jar e native buildam off-host no CI (build-deploy.yml, jobs por alvo) e o Coolify só puxa (Build PackDocker Image); auto-deploy no push sai. Deploy passa a ser release-driven: a/xadm-releasecrava o trailerDeploy: <alvos>na tag por diff de path, o workflow builda só os alvos, a ponte SSH dispara. Constituição §5,0022 §Topologia(revista in-place),coolify.md,java-micronaut.md,app.json(build.targets). POCthoms-webstorm(jar off-host 3m13s, zero CPU/disco na VM). O template do workflow foi renomeadobuild-native.yml→build-deploy.yml(o nome antigo enganava — buildava jar num "build-native"). - HEALTHCHECK: tolerância a boot-longo mora nos
retries, não emstart-periodgeneroso. Migração Flyway grande (~3min) estourava o healthcheck no boot → rollback. Cadência nova nos dois Dockerfiles:start-period5s +interval15s +retries15 (≈225s) — boot rápido promove no 1º check, boot lento é coberto pelos retries (o Coolify espera a janelastart-periodinteira). Split/health/live+/readyrejeitado (Micronaut roda Flyway antes de aceitar conexão; liveness flat + retries resolve).versionamento.md,java-micronaut.md.
Adicionado¶
- Contrato
app.json:build.targets(default estático dos alvos off-host —[native]/[jar]/[jar,native]) +deploy(mapa por-alvo host/uuid, forward-looking multi-host).publicar-docs.md,templates/app.json. - coolify.md §Canário e troca blue-green — lições de 503 da POC: canário nunca por
proxy/dynamic/*.yamlà mão (ação de recurso zera o arquivo); blue-green transfere o fqdn pro novo antes de deletar o antigo (ordem inversa órfã router+cert).
0.34.7 - 2026-08-28 (constituição 0.36.5)¶
Corrigido¶
- Receita do
flavorno/health(versionamento.md§Receita) lia per-app: dizia "oflavoropcional detecta-se em runtime:System.getProperty(...)" como se cada app o adicionasse — quando/healthé servido peloHealthControllerdoxadm-comum-web(contrato flat único da casa).flavoré campo desse controller: acrescentá-lo é mudança na lib, feita uma vez e herdada por todos os apps — a única exceção era esse campo (todo o resto do contrato de/healthjá era edição da lib). Esclarecido emversionamento.md§Receita (a detecção mora na lib, não em cada app) e emjava-micronaut.md§Específico (oflavoré campo doHealthController, cross-link à §Shape mínimo). Páginas dev, não rastreadas → sem bump.
0.34.6 - 2026-08-28 (constituição 0.36.5)¶
Corrigido¶
- Ponte SSH de deploy native (
templates/build-native.yml): a chave privadaDEPLOY_SSH_KEYgravada com CRLF/BOM — o que o PowerShell no Windows faz (Get-Content -Raw | gh secret setconverte LF→CRLF) — chega ao runner com\rem cada linha e osshfalha comerror in libcrypto/Permission denied (publickey). Como oDeployé o último step, o build passa e só o deploy quebra (imagem publicada, recurso native não sobe) — mordeu prod (onpetro/vantroba, ago/2026). O step passou a filtrar\r(printf … | tr -d '\r'), imune à origem do secret; de carona, o secret vai porenv:(não expande a chave crua norun:) e é materializado em$RUNNER_TEMP/deploy_key(efêmero). Onboarding LF documentado (Git Bash, não PowerShell) no cabeçalho do template e emcoolify§Atualizar. Constituição 0.36.5.
0.34.5 - 2026-08-28 (constituição 0.36.4)¶
Adicionado¶
- Guarda de paridade
Dockerfile/Dockerfile.nativenotemplates/ci.yml: app native carrega os dois arquivos (0022 §Topologia) e os passos de filesystem/owner sob/app(mkdir -p /app/<x> && chown app:app /app/<x>antes doUSER app) têm de bater nos dois. Dir criado só num flavor =AccessDeniedExceptionsó naquele deploy (USER appnum/appdo root; build e teste passam) — bug real do/app/logsnobi-comercial-xls(ago/2026), que nasceu noDockerfile.nativesem omkdirque o JVM já tinha. A guarda compara o conjunto de dirs/app/<x>emmkdir/chown, ignorandoCOPY(paths de build divergem legitimamente) e comentário. Espelhado no/xadm-docs§3b (sweep de app que já diverge) e em nota de paridade nos dois templates +java-micronaut§Deploy. Constituição 0.36.3.
Alterado¶
- Deploy native:
publish-only→ publish + gatilho via ponte SSH. Com Build PackDocker Imageo Coolify não puxa a imagem nova sozinho e não há webhook de repo (não é push de código) — publicar deixava a imagem no registry e o recurso rodava a velha até redeploy manual. Otemplates/build-native.ymlganhou um stepDeployque fazsshnuma chave forced-command na VM da casa: o wrapper só aceitadeploy <uuid>(whitelist dos recursos native, sem shell, lista por vírgula p/ app multi-instância) e faz ocurlna API local do Coolify; o token do Coolify fica na VM, fora do GitHub (postura mais forte que exporCOOLIFY_WEBHOOK_URL). 0022 §Topologia reescrito (supera "não deploya"); receita da ponte emcoolify§Atualizar. Constituição 0.36.4.
0.34.4 - 2026-08-28 (constituição 0.36.2)¶
Adicionado¶
- Gate native PRÉ-TAG no pré-flight da
/xadm-release(feedback §6 de app): app comDockerfile.nativebuilda oDockerfile.native(docker build -f Dockerfile.native), não o JVM, como gate de container. RodanativeCompilenum container limpo antes da tag e fecha duas frestas que odocker buildJVM não fecha: (1) compila native pré-tag — ochecknão rodanativeCompilee obuild-native.ymlsó dispara na tag, então falha só-de-native aparecia pós-tag (verde local, vermelho pós-tag); (2) resolve toda dep pelo registry (semmavenLocal) — uma dep-SNAPSHOTlocal que o e2e não pega falha aqui. Reconciliado emjava-micronaut §Native-image,0022 §Gate,templates.md,e2e-local §1. - Armadilha OpenAPI: metadata usada como nome de arquivo tem de ser ASCII/fs-safe (feedback §6):
@Info(title=…)em pt-BR (acento/em-dash) vira path emMETA-INF/swagger/pelomicronaut-openapi→nativeCompilequebra; o build JVM passa (checknão compila native), a falha só aparecia no workflow native pós-tag. Regra:title/namede tag/schema = ASCII estável;descriptionsegue Unicode/pt-BR (não vira artefato). Não reabre o falso rastro do Unicode-em-render (item 146). Emjava-micronaut §Armadilhas. /healthganhaflavoropcional (native|jvm) (feedback §6): server que deploya JVM e native expõe o que roda de fato (System.getProperty("org.graalvm.nativeimage.imagecode") != null→ native), fechando metade do ponto cego "app mostra versão, não se é native nem o digest" (a outra metade, o digest, o app não conhece — confere-se no log do Coolify). Emversionamento §runtime.- Cobertura Flutter visível no
ci.yml(feedback §6): o gate Flutter rodaflutter test --coverage+ resumo portável dolcov.infoque imprime o % (sem depender delcovna imagem), honrando a norma "o gate imprime o %" (ci-testes §Definição de pronto), antes só cumprida no Java (JaCoCo). Alinhadas as listagens emci-testeseapp-novo.
Corrigido¶
- Regra da tag de imagem native no Coolify (feedback §6): o recurso native puxa
<slug>:native-amd64(== 0024),:latestproibida (apontar pra:latestpuxava imagem diferente da recém-publicada), tag:<sha>= auditoria imutável, conferir o digest no log do deploy. Emcoolify.md.
0.34.3 - 2026-08-26 (constituição 0.36.1)¶
Adicionado¶
/xadm-docspasso-2 ganhou fallback obrigatório (feedback §6 de app): CHANGELOG stale/sem carimbo (o site publicado atrasa o bump da constituição; ou entrada antiga) → diffar o template (raw vigente × local) e julgar pela mudança real, não pela prosa. Antes, não achar a string da versãomudou_emmascarava defasagem como "não é minha stack".- Convenção — heading de site carimba
(constituição C.C.C): a/xadm-release(central) passa a sufixar cada heading do CHANGELOG com a versão da constituição vigente, com gate no pré-flight (§8). Sem o sufixo a versão da constituição não aparecia em lugar nenhum do CHANGELOG e o passo-2 do/xadm-docsnão mapeava um templatemudou_emà sua entrada. Documentado emengenharia/versionamento.md§Duas versões. /xadm-docs§3c ganhou o tipo (C) — migração de stack por ADR/norma central (ex.: views em JTE,docs/decisoes/0025-*.md;micronaut-data; deploy native) adotada sem RD-ponteiro local vira candidato de autoria: cobra um RD curto que aponta o ADR central e data a adoção neste app (não re-litiga a decisão da casa). Mesmo guardrail heurístico do tipo (B).
Corrigido¶
- Guard "Integration no gate" do
ci.ymldava falso-positivo em app native (feedback §6):excludeTags("native")— a suíte e2e native roda nobuild-native.yml, não nocheck— era tratado como cobertura órfã. O guard passou a parsear os literais de tag e só dispara em tag integration-class (integration/docker/it/e2e);nativee qualquer outra tag não contam. Sandbox de 9 cenários. O guard do item anterior era anterior à norma de deploy native. - Backfill dos headings sem carimbo da constituição:
## [0.33.0](constituição 0.35.0) e## [0.34.0]/[0.34.1]/[0.34.2](constituição 0.36.0) — verificado pelo frontmatter no commit de cada release. Fecha a lacuna do passo-2 do/xadm-docspara os templates alterados em 0.35.0/0.36.0.
Alterado¶
templates/dockerfile-java-nativepina o builder por digest de índice (feedback §6, supply-chain): antes só o runtime (debian:12-slim) era pinado por@sha256; o builder (native-image-community:25-ol9) era tag móvel. Num deploy native o compilador é parte da cadeia de suprimento do binário (builder móvel = binário não-determinístico; adulterado = binário adulterado). Agora builder e runtime pinam o manifest list (índice multi-arch) viadocker buildx imagetools inspect <ref>:<tag>→Digest:— corrigido também o comando do runtime (docker pulldava o digest da plataforma do host, não o do índice).
0.34.2 - 2026-08-26 (constituição 0.36.0)¶
Corrigido¶
- Norma
/health+/infoflat sob native: o controller flat é INCONDICIONAL, não guardado por property (java-micronaut §Native, §Específico e nota do Dockerfile). O guard por@Requires(property="endpoints.health.enabled", value="false")documentado na 0.34.1 (fix daxadm-comum-web0.6.0) não sobrevive ao AOT: um@Requiresde classe num@Controlleré avaliado em build-time e a rota é podada sob native — vale tanto paramissingBeansquanto paraproperty(provado no canário native jar×native — o jar registrava o flat, o native não →/healthsemversao,/info404). Fix definitivo (xadm-comum-web0.6.1):HealthControllereInfoControllerincondicionais (sem@Requires), então a rota entra sempre no grafo do AOT; o app desliga os dois endpoints de management (endpoints.health.enabled: falseeendpoints.info.enabled: false) pra o flat incondicional não colidir com o management (default-on → duas rotasGET /health→ 400).
0.34.1 - 2026-08-26 (constituição 0.36.0)¶
Adicionado¶
- Infra de views JTE compartilhada no commons + fix native de observabilidade (feedback de app;
dobra no ciclo da aposentadoria do Thymeleaf, 0025).
Registrado em
java-micronaut §UI/JTE, na nota "Evolução observada" da 0019 e no baseline de libs (constituição §5):Formata(xadm-comum-util0.2.0) — formatadores null-safe de exibição pras views JTE (trim/data/abrevia,@import br.com.xadm.comum.util.Formata), substituindo os#temporals/#stringsque o JTE não tem; a frota@importa em vez de forkar umFmtpor app.XadmViewModel(xadm-seguranca0.6.0) — oViewModelProcessorde referência que injeta os globais dokit/layout.jte, opt-in porxadm.views.app-nomee tolerante a model imutável; o app apaga o seuGlobalViewModellocal. Mora em seguranca (lêAuthSupport/AuthSettings; comum-web seria circular)./health+/infoflat idênticos jar↔native (xadm-comum-web0.6.0) — o guard dos controllers flat passou de@Requires(missingBeans=…)(build-time, não sobrevive ao AOT quando a definição do bean existe no classpath) pra@Requires(property=…)(runtime); novoInfoControllerflat (GET /info→{versao}), com o management do Micronaut — frágil sob AOT — fora do caminho. Norma emjava-micronaut §Native, cross-link 0022.
0.34.0 - 2026-08-26 (constituição 0.36.0)¶
Adicionado¶
- ADR 0025 — views server-render usam JTE (compilado),
não Thymeleaf. Sob native (0022) o Thymeleaf/OGNL
reflete todo tipo de view (whack-a-mole ilimitado, drifta, falha em runtime — 15 tipos / 3 rebuilds
no piloto
int-sascar+ oMensagemM2mda lib). JTE compila o.jteem classe Java: type-check no build + native com reflexão FECHADA (só as classes geradasgg.jte.generated.precompiled.*, registradas à mão emreflect-config.json/ reachability-metadata na lib; no modogenerate()não há.bin— o conteúdo vai inline no.javagerado — logo sem tracing-agent; não drifta). Mantém o princípio da 0010 (UI server-render unificada), troca o motor. Rocker descartado (manutenção parada + XSS menos content-aware). Kit da casa vira JTE (kit/layout.jte+headerRight.jte). A norma "@ReflectiveAccess em todo tipo de view" (abaixo) fica legado (superada por JTE). - Três normas de native-image em
java-micronaut §Native(feedback §6 do e2e native de app): (1) método/propriedade de QUALQUER tipo (enum/record/entity/DTO, inclusive accessors aninhados) num template Thymeleaf é reflexão sob AOT →@ReflectiveAccessno tipo (@Introspected/@MappedEntityNÃO cobrem — verificado); enumerar todos de uma vez (miss = 1 rebuild); tipo compartilhado (mensageriaDirecao/Status/MensagemM2m) vai na lib; app view-heavy registra por PACOTE (Feature), não tipo-a-tipo. (2) recurso de classpath (version.propertieslido peloVersaoInfo) precisa deresource-config→ axadm-comum-webembarca a reachability-metadata (0.5.1), propaga à frota. (3) factory JAXP por service-lookup (DocumentBuilderFactory.newInstance()) usaFactoryFinder(reflexão) → usenewDefaultInstance(). Os três são a classe "compila verde, quebra em runtime" que o e2e native existe pra pegar.
Alterado¶
- Commit inclui o
.claude/settings.jsoneditado (REGRA Nº 2): se a sessão mexeu nosettings.jsonrastreado do repo (allowlist/hooks/env), ele entra no mesmo commit; osettings.local.jsongitignored (máquina-específico) fica de fora.
Removido¶
- Pegada Thymeleaf aposentada (a frota inteira migrou pra JTE, 0025):
apagados o kit
templates/views/layout.html, o gatescripts/checa-views.py+testa-checa-views.py(validavam o shape dos fragments.html; sob JTE o motor type-checa no compile, não há.htmlde view) e a receita "Kit Thymeleaf — LEGADO" dajava-micronaut. Removida a fiação docheca-viewsdobuild-site.yml,templates/ci.yml,Dockerfile,gera-manifestoe do pré-flight da/xadm-release. As ADRs 0015/0017 (mecânica do kit.html) ganharam nota de superadas pela 0025 (histórico preservado). App que ainda copiava o kit Thymeleaf re-deriva o kit JTE via/xadm-docs.
0.33.0 - 2026-08-25 (constituição 0.35.0)¶
Adicionado¶
- Constituição mais opinativa — fechados 4 casos de discrição app/dev. Dois gates novos no
valida-frontmatter.pye dois ajustes de texto normativo, para tirar do app/dev escolhas que divergiam em silêncio (a varredura separou discrição real de escopo condicional — este último, comomapeamento.md/flowchart/riscos/SVG opcionais, é aplicabilidade e ficou intacto):entregue:OBRIGATÓRIO em toda etapa deprojeto/(constituição §2) — o validador falha sem ele; isenta o livro (index.md) ediversos.md. Antes opcional, caindo num heurístico (status do DOC como proxy da entrega da FEATURE) que fazia o Resumo Executivo mentir quando o app omitia o campo. O heurístico sobrevive só como socorro a etapa legada não-migrada — ovisao-tecnica.pyfica intocado; app novo declara o campo.decidido_em:OBRIGATÓRIO em decisãostatus: aprovado(constituição §4) — cumpre o "retrato datado" (§1.4);rascunho/em-revisão/obsoletoisentos (legado/em progresso migra oportunista). Dogfood: a decisão0001do central era a única sem o campo — o gate a pegou.
Alterado¶
- Nav "recomendado" → "nav canônico" (constituição §2): a ordem do
mkdocs.ymlé normativa; desvio é decisão declarada (REGRA Nº 3), não escolha silenciosa — site consistente entre repos. - Contradição interna resolvida: anexos README passou de "recomendado" (§5) para obrigatório, reconciliando com §3.
- Contrato app↔app: o mecanismo agora é módulo tipado do receptor, não codegen (decisão 0020
atualizada in-place; não tinha entrado no ar). O
openapi-generator(codegen no consumidor) saiu — casa 100% Java/Micronaut server-server → o benefício poliglota vale ~zero e a capacidade do gerador era risco não-provado (0020 §1.2). Entra o módulo<svc>-apique o receptor publica do próprio repo (DTOs@Serdeable+ interface@Client, Maven Forgejo, groupbr.com.xadm), consumido por versão → enforcement em compile-time (rename incompatível não compila).micronaut-openapifica (spec +openapi.yamlversionado +openapi-diffrecomendado); duas classes de consumidor (Java → módulo tipado; não-Java/skill/externo → spec publicado É o contrato). Reconciliado em java-micronaut §Específico,stack.md, integracao.md §Arestas, publicar-docs.md §O contrato, nota na 0019 (<svc>-api≠xadm-commons). Escopo Java real medido = sóintegrador-server ↔ int-sascar(arestas A+B);central-backend= spec-only.
Corrigido¶
- Armadilha do rename de pacote na guarda ArchUnit (feedback §6, int-sascar
biz.xadm.intsascar → br.com.xadm.integracao.sascar). Renomear o pacote base deixa string-literals stale — sobretudo@AnalyzeClasses(packages=...)(master switch) eresideInAPackage("...")— que o./gradlew checknão pega: ArchUnit avalia 0 classes e passa vácuo (o mesmo verde falso do limite nº 2, por outro gatilho). Nomeada como limite conhecido nº 3 em java-micronaut §Guarda: varrer o literal a montante (grep -rl "<pacote>"sobre.java/.xml/.properties, não sópackage/import), oisNotEmptycomo defesa a jusante, + ripple do checkstyleLineLength(FQN mais longo;awkconta bytes). Página dev não rastreada → sem bump.
Adicionado¶
- Código gerado (Serde/introspection) sai do denominador do JaCoCo — round-trip é contract test, não
cobertura (feedback §6). Terceiro modo de falso-baixo de cobertura (irmão do falso-0% do
class-major e do re-exec só-unit): o processador do Micronaut emite
$Tipo$Introspection/$Tipo$Definitionque contam no denominador, então todo app Micronaut-Serde nasce com % artificialmente baixo pelo que não escreveu.io.micronaut.core.annotation.Generatedé CLASS-retention (JaCoCo ≥0.8.2 exclui a classe anotada), mas não as aninhadas (jacoco#1815) — e são as$…$Introspection/$…$Definitionque sobram. Norma: excluir o gerado do denominador viaexcludesdo JaCoCo (**/*$Introspection*,**/*$Definition*); o round-trip viaObjectMapperdo contexto não é a forma de cobrir o gerado (testa o framework, deixa ramos descobertos = "toca sem asserir"), mas vale como contract test do DTO que cruza fronteira. Norma em java-micronaut §Testes + linha na política de cobertura em ci-testes. Páginas dev não rastreadas → sem bump.
0.32.4 - 2026-08-25¶
Adicionado¶
- Cobertura tem ponto de coleta garantido (re-exec forçado) + integração pula por Docker-ausente com
aviso ALTO (feedback §6, refina o item 149). Fecha as duas camadas que o 149 não tocou. [coleta]
A cobertura (JaCoCo) roda com
--rerun-tasks/cleanTest test, nunca no up-to-date do Gradle: otestfresco é pulado, o.execmesclado que a ferramenta lê fica só-unit e a cobertura subconta — falso-baixo silencioso (caso onpetro: "41%" que virou 75,6% ao forçar o re-exec da integração), irmão do falso-0% do class-major do JaCoCo. O % só vale desse gate (o do CI que re-executa), jamais de um./gradlew testde dev (só-unit). Norma em ci-testes §Definição de pronto + mecânica em java-micronaut §Testes. [polaridade] Idioma recomendado: a integração roda por default e só pula com Docker genuinamente ausente, com aviso ALTO no log —@DisabledIfDockerUnavailablecustom (DockerClientFactory.instance().isDockerAvailable()+ log no skip), não odisabledWithoutDockerdo@Testcontainers, que cala. Complementa a guarda do 149 (não-exclua-por-flag) com "se pular, grite". Páginas dev não rastreadas (ci-testes, java-micronaut) → sem bump.
Corrigido¶
- Partição da fila outbox em schema compartilhado é só o registro de enviador — dos dois lados
(feedback §6, reverte o
MENSAGERIA_RELAY_ENABLED=falsedo item 149). O item 149 abençoou desligar o relay por flag como escape do receptor-puro; o feedback mostrou que é competing consumers mal-feito — resolver colisão de consumidores por config por-deploy não compõe (o app que envia E recebe não pode se desligar) e some no dia em que o app passa a enviar. O filtro portipo/EnviadorMensagemjá basta: receptor-puro registra zero enviador → seu relay ignora toda linha (no-op inócuo), sem flag. Novo: fail-fast no enqueue — só enfileira naSAIDAquem tem oEnviadorMensagemdotipo, senão grava linha órfã que ninguém roteia; a posse da mensagem fica cravada pelo registro de enviador nos dois lados (só enfileira quem sabe enviar, só drena quem sabe rotear).MENSAGERIA_RELAY_ENABLED=falsedemovido a otimização de loop-ocioso (nunca resolvedor de colisão);coluna de origemerelay-broker úniconomeados como anti-padrões. Norma em 0021 §Decisão + mecânica em java-micronaut §Banco. RD e página dev não rastreadas → sem bump.
0.32.3 - 2026-08-24¶
Alterado¶
/health= flat{status, versao}liveness é o contrato ÚNICO da casa (feedback §6, constituição 0.32.0 → 0.33.0). O/healthé servido flat (dois campos no raiz, 200-fixo, anônimo, liveness) peloHealthControllerdoxadm-comum-web; o management health nativo do Micronaut fica desligado (endpoints.health.enabled: false) pra não colidir com a rota (o incidente do 400 em toda request) — o nativo não é alternativa conforme (não carregaversao, agrega indicadores de dependência no raiz = readiness, DB-aware). Norma nova liveness ≠ readiness: o HEALTHCHECK do container é liveness ("o processo responde?"), nunca readiness ("o banco está up?") — dependência que pisca não pode reiniciar o container (restart em cascata); checagem de dependência é monitoramento separado. Reescrita a cláusula de versionamento §Health que dizia "o/healthnativo do Micronaut é conforme, sem remapear" (era enganosa: o nativo não temversao). Item 139 removido: com management off não há/health/liveness(grupo native), então a receita do indicador@Livenesspra curar oUNKNOWNno native ficou sem objeto — o flat é native-safe por construção (@Singletoncompile-time, semjava.lang.management); o e2e native afirma/health == UP. Ação (repo de app, §3):central-backendmigra do/healthDB-aware próprio pro flat (a libxadm-comum-webassume; já está na 0.5.0) — recupera oversao. Páginas dev/site não rastreadas (versionamento, java-micronaut, e2e-local); bump text-only da constituição.
Adicionado¶
- Build native padronizado (modelo GitHub Actions) + ADR 0024 slug de registry + IT dispara o
entrypoint real (dois feedbacks §6, constituição 0.33.2 → 0.34.0). [A] A frota (7 apps)
convergiu o build native pro modelo do app de referência: o repo do app carrega
Dockerfile(JVM, fallback) eDockerfile.native, e o native é buildado por.github/workflows/build-native.ymlno runner do GitHub (não no Forgejo self-hosted nem na VM de prod — native-image come ~6 GB), publicando a imagem no registry Forgejo (+ mirror GHCR); o Coolify puxa (Build Pack Docker Image). Isso substitui a 0022 §Topologia ("native builda em outro repo / uma cara JVM"). Novo template rastreadotemplates/build-native.yml(par dodockerfile-java-native). Reconciliações nodockerfile-java-native: binário fixo emapplication(imageName.set("application")obrigatório —rootProject.namediverge do slug e quebra o COPY); removido o--no-configuration-cache(era do caminho do plugin — onativeCompileé CC-safe, a frota builda com CC ligado; reconcilia o item 140);microdnf install findutilsno builder; SENTRY_DSN por ENV no native (ENTRYPOINT puro, sem ponte jq-do-app.json). [F] Novo ADR 0024: slug de registry =<cliente_id>-<projeto>(cliente) /<projeto>(xadm), == nome da imagem no registry, pode divergir doapp_id(identidade estável do broker); "nome de projeto de cliente é acordado comercialmente". [B] Regra nova em ci-testes + java-micronaut §Testes: código que roda por entrypoint sem-request (@Scheduled,@EventListener, thread, boot, CLI) testa-se disparando o entrypoint real — chamar o método sob o contexto de conexão do@MicronautTestpassa verde e estouraNoConnectionExceptionem runtime (falso-verde estrutural; caso canônico "IT verde, runtime vermelho");@Connectable/@Transactionalvivem no código de produção. Bump MINOR (novo template rastreado = obrigação nova ao app native; revisável). Sem gate em B (heurístico; ponto cego declarado). - Integration não fica órfã do gate + relay não compete em schema compartilhado (dois feedbacks
§6, constituição 0.33.1 → 0.33.2). [F1] A casa roda integration (
@MicronautTest+ Testcontainers) dentro docheck, que roda na CI com Docker (o runner injetaDOCKER_HOST, dind). Um app que esconde a integration atrás de@Tag("docker")+-PdockerTestsque a CI não passa deixa a suíte no repo mas fora do gate: cobertura órfã — verde no PR, só cai no e2e (o teste mais caro). Norma estrita em ci-testes §Integração: ou roda nocheck(default, gateado na CI via dind), ou — se genuinamente un-CI-able — vira e2e-local nomeada (outro repo); nunca atrás de flag não-gateado. Guarda nova notemplates/ci.yml(python3 stdlib, escopo Micronaut): reprova exclusão de tag de integration condicionada a-Pque a CI não passa (ou incondicional semincludeTagsque a re-rode); sandbox 6/6. [F2] Estende a decisão 0021: em DB/schema compartilhado, os doisRelayM2mdrenam a mesmaSAIDA; o relay do app que não registra otipopega a linha e logaSem EnviadorMensagem registrado. Norma: o relay filtra aSAIDApelos tipos que registra (ignora o alheio, não pega+ERROR) — default robusto; receptor-puro podeMENSAGERIA_RELAY_ENABLED=false. Também foldado no 0021 o ponto de posse única de migração (item 121). Filtro é prescrição nova (impl noxadm-mensageria, §3); F1 ci-testes/java-micronaut são páginas dev não rastreadas. Bump PATCH (1 rastreado:ci.yml). - Persistência da casa fixada:
micronaut-data-jdbcobrigatório + guarda de CI (feedback §6, constituição 0.33.0 → 0.33.1). Um app Micronaut nasceu com SQL cru viaDataSource.getConnection()("sem micronaut-data") — deslize de criação, não decisão: todo server Micronaut que persiste usamicronaut-data-jdbc(@JdbcRepository,@Transactional), sem Hibernate/JPA; SQL cru como camada de persistência é anti-padrão (bulk JDBC à mão num@Singletonsegue OK). O pego silencioso: semannotationProcessor('io.micronaut.data:micronaut-data-processor')os interceptors de@Transactionalnão são gerados → vira no-op, quebra com "No current connection present" em runtime, sem erro de compilação. Custo do deslize: adotar lib da casa que arrastamicronaut-data-jdbc(ex.xadm-mensageria) torna oDataSourcetransaction-aware e quebra o raw-SQL em massa, forçando migração no meio do projeto. Fixado em java-micronaut §Banco (bullet-líder); guarda nova notemplates/ci.yml(python stdlib: app Micronaut +datasourcesconfigurado + semmicronaut-data-processor→ FALHA no push; exceção stateless por ausência dedatasources) e no/xadm-docs§3b (conformidade). Origem: feedback de app (int-pied → convergência mensageria micronaut-data). - Toda listagem Micronaut é paginada —
Pageable+Page, default 20 (feedback §6). Endpoint de API ou tela server-render que lista tabela que cresce recebePageablee devolvePage/Slice, nuncafindAll()cru — evita o travamento com o Postgres compartilhado crescendo. Config da casa:micronaut.data.pageable.default-page-size: 20+max-page-size: 100(teto anti-?size=999999). Norma em java-micronaut §Específico (mecânica Micronaut,PagevsSlice, ponto cego doOFFSETprofundo → keyset, pergunta de review) e §UI (face server-render, prev/próxima no template). Sem gate (heurístico, falso-positivo em lookup pequeno). Página dev não rastreada. -
Pirâmide de testes: e2e é o mais caro — audite o que já existe, escreva no nível mais baixo (feedback §6, constituição 0.32.0 → 0.33.0). Antes de cobrir um gap, auditar os testes que já existem no repo e escrever no menor nível que o prova (unit antes de integration antes de e2e); o e2e não re-testa o que unit/integration prova mais barato (ex.: negativos do validador Firebase e fail-closed de
/api/admin/**fecham abaixo; "health com banco down → 503" fecha em integration comPostgreSQLContainer.stop()). Toda suíte e2e-local carrega umCOBERTURA.mdcom a tabelagap → nível → onde fecha(norma). Disciplina em ci-testes §Definição de pronto; artefato em e2e-local §11. Origem: auditoria dos e2e-local que cobriam o que já é unit/integration. -
Constituição §5: "adoção é migração, no mesmo PR" + escopo do
xadm-seguranca(feedback §6, constituição 0.31.4 → 0.32.0). Bumpar um módulo doxadm-commonsque promove capacidade antes local exige remover o código local no mesmo PR (dois@Controller("/")com/logincolidem no boot); o contrato de migração mora no CHANGELOG do módulo (§Migração); o e2e-local pega o drift de contrato antes da produção. O bulletxadm-segurancada §5 passa a nomear o fluxo de login Google server-side das views (não só primitivas); a evolução observada (0.5.0 login flow + rate-limit, 0.5.1 Bearer fail-closed, dep. dexadm-comum-web) foi registrada na tabela do 0019. Checklist operável de adoção em java-micronaut §Específico. Origem: feedback de app (varredura e2e-local + promoção do login paraxadm-seguranca). - Duas armadilhas Micronaut de config de adoção (feedback §6,
[Unreleased], páginas dev não rastreadas): (1)java -jardeduz o perfildeve carregaapplication-dev.yml, sobrepondo env de produção em silêncio — cura =MICRONAUT_ENVIRONMENTSexplícito oudeduceEnvironment(false); (2) e2e-local crava a imagem de terceiro na versão de produção, nunca:latest(o skew esconde bug de versão até o deploy).
Corrigido¶
- Thymeleaf:
th:if="${x == '1'}"comparachar, nãoString— prefira boolean no modelo (feedback §6). Dentro deth:if="${…}"as aspas duplas do atributo forçam aspas simples na expressão, e aspas simples em OGNL é literal de char ('1'=char) →${x == '1'}comparaString == char, não casa, e o elemento não renderiza sem erro (build verde). Cura = boolean no modelo +th:if="${flag}"(o que o${appModo}/podeAgirdo kit já fazem). Bullet nomeado no cluster §Armadilhas (família OGNL/record) + reforço do gancho em §UI. O ponto 2 do feedback (não-ASCII notitlequebra render) foi descartado como falso rastro (decisão do Gustavo): o em-dash/acento é válido em UTF-8; a causa do span não-renderizado era a comparação de char, não o não-ASCII. Sem bump (java-micronaut.md é página dev, não rastreada).
0.32.2 - 2026-08-20¶
Corrigido¶
-
Receita de leitura XLSX com FastExcel-reader:
withCellFormaté obrigatório com datas Excel reais (feedback §6). O recipe (java-micronaut §Específico, item 137) detecta data pelogetDataFormatId()/getDataFormatString(), mas omitia que o fastexcel-reader não resolve o formato da célula por default: sem abrir o workbook comnew ReadableWorkbook(is, new ReadingOptions(true, false))(withCellFormat=true), esses getters vêmnull, a detecção de data nunca dispara, e uma data Excel (serial, ex.46083) sai crua. Sintoma traiçoeiro: fixture sintética com datas como texto passa verde, mas golden-master/e2e com dado real dropa linha em silêncio — achado da migração POI→FastExcel do bi-comercial. Adicionado o flag ao recipe §Específico; a armadilha de fixture do §Testes ganhou nota distinguindo as duas causas do mesmogetDataFormatString()==null(roundtrip do writer × flag de leitura ausente). Sem bump (java-micronaut.md é página dev, não rastreada). -
Native no repo do app =
nativeCompileverde; runtime é gate do repo de e2e — sem e2e/boot-smoke native no repo do app (feedback §6). Feedback propunha um boot-smoke native in-repo (TestcontainersGenericContainerda imagem native + Postgres,@Tag("native")). Decisão do Gustavo: não — o repo do app só precisa donativeCompilecompilar; os bugs de runtime native são pegos pelo e2e native no repo de e2e de fluxo (separado), coerente com o native buildar fora do repo do app (0022 §Topologia). Um smoke in-repo seria redundante (mesmos bugs, repo errado). Cravada a fronteira em java-micronaut §Deploy §Native-image e no 0022 §Gate, pra o próximo app não reinventar o smoke. Também reforçado o hint dereflect-configdoSentryAppender: confirmado byte-idêntico em 3 apps native (bi-transporte-xls, central-backend, webstorm-ecom), a hint sobe pra reachability-metadata da libxadm-comum-web(META-INF/native-image/…no jar → native-image detecta sozinho, elimina a cópia local na frota; pendência de repo de lib §3). Sem bump (páginas dev e decisão não rastreadas). -
checa-nav.pydivergia da validação nativa do mkdocs emREADME.md— alinhado (feedback §6, constituição 0.31.4). Ocheca-nav.pypulava todoREADME.mdincondicionalmente (convenção "README de pasta não é página de nav"), mas a validação nativa do mkdocs (validation.nav.omitted_files: warn, item 135 / 0.30.4) não tem essa exceção: um README fora donav:é órfã e reprova o--stricta menos que esteja emnot_in_nav:/exclude_docs:. Divergência: um README órfão passava nocheca-nave reprovava no--strict— foi por isso que o site pôsanexos/README.mdnonot_in_nav. Decisão do Gustavo (AskUserQuestion): remover o skip-README docheca-nav(não documentar a divergência como intencional, nem aposentar a detecção de órfã): agora um README é OK só se casarnot_in_nav/exclude_docs— idêntico ao mkdocs. Verificado: o central segue verde (os dois README —anexos/README.mdnonot_in_nav,anexos/privado/README.mdsobexclude_docs: privado/— já caem nos globs, sem depender da exceção). Ocheca-navmantém a detecção de chave YAML duplicada (única dele, o mkdocs não pega — item 94). CasoREADME-nao-e-orfaodotesta-checa-navtrocado por dois (README órfão reprova; README emnot_in_navpassa — 19 casos). Bump PATCH (1 rastreado re-carimbado:scripts/checa-nav.py). Nota:checa-navé central-only (item 132) — apps não o rodam; o alinhamento fecha a divergência-de-lógica quando um app adotar ambos. -
Hook
SessionStartdocheca-constituicaofica presente porém nunca acionado numa instalação verbatim (feedback §6, constituição 0.31.3). Otemplates/settings.json(0.29.1) traz sóenv+permissions, mas a constituição §6 afirmava que osettings.jsonversionado carrega o hookSessionStart, e ocheca-constituicao.sh/app-novo.mdmandavam registrá-lo à mão — passo fácil de pular: o.shacaba copiado, o hook nunca cabeado, o aviso de defasagem nunca dispara. Decisão do Gustavo (AskUserQuestion): o/xadm-docscrava o hook ao instalar (o template segue só env+permissions — pessoal/wiring fora da base). A skill passa a fundir o arraySessionStartao sincronizar osettings.json— garante a entrada docheca-constituicao(adiciona se ausente) e preserva as demais do app (caveman/token-check) +additionalDirectories/grants locais. Reconciliado o cabeçalho da skill,app-novo.md(passo obrigatório),ferramentas.md §higiene(o template não trazhooks; a instalação crava) e a §6 da constituição (nuance: o template traz só env+allow-list). Bump PATCH (1 rastreado re-carimbado:templates/xadm-docs-skill.md). Pendência (repos de app): re-derivam a/xadm-docse ganham o hook cabeado; quem instalou verbatim sem o hook passa a tê-lo no próximo sync.
0.32.1 - 2026-08-19¶
Adicionado¶
-
Receita: monitorar o host/Docker read-only de um container Coolify (feedback §6). Split infra+Micronaut. Postura de segurança em coolify §Monitorar o host: nunca o socket cru (= root no host) —
docker-socket-proxycom allowlist mínima (CONTAINERS=1,POST=0,EXEC=0) + mount read-only de superfície mínima (/proc:ro+ probe de 1 arquivo público pro disco). Mecânica de leitura em java-micronaut §Monitor de host: Engine APIstats?stream=false(one-shot,precpujá populado — sem 2 leituras), CPU% = (cpuΔ/systemΔ)×online_cpus×100, RAM =usage − inactive_file(cgroup v2), disco viaFiles.getFileStore(Java semstatvfs), envelope de degradação parcial (200 + fontes/erros, nunca WARN mudo), e recon de fixture real antes do parser (§6). Páginas dev/site, sem bump. -
Gotcha serde:
@Serdeableomite mapa/lista vazios no JSON de saída (feedback §6). Coleção vazia some do JSON serializado → o consumidor recebe a chave ausente (null), não{}/[], e estoura NPE ao iterar (custou 1 NPE de teste). Regra em java-micronaut §Armadilhas: o contrato trata ausente como vazio (consumidor coalesce; teste de contrato afirma ausente ≡ vazio), ou o produtor garante a emissão se o contrato exige a chave. Página dev, sem bump. Pendência (repos de app): consumidor que presume{}/[]no fio passa a coalescer via/xadm-docs. -
Liveness native = UP: registre um indicador
@Livenessexplícito (feedback §6, medição A/B em rollout canário native)./health/livenessdiverge JVM×native — na JVM é UP, sob GraalVM AOT fica UNKNOWN: o grupo liveness agrega só osHealthIndicator@Liveness, e o default (detector de deadlock viajava.lang.management/ThreadMXBean) não reporta no native → oDefaultHealthAggregatordevolve UNKNOWN pro conjunto vazio./healthe/readinessnão sofrem (têm indicadores JDBC/disk), HTTP segue 200 e o HEALTHCHECK do container usa/health— não quebra deploy, mas não é paridade 100%. Cura native-safe (bean@Singleton= compile-time, semreflect-config): umLivenessHealthIndicatorque afirma UP ("responde, logo vivo"; liveness ≠ readiness). Recipe + snippet em java-micronaut §Native-image; o e2e native passa a afirmarstatus==UPno liveness. Página dev, sem bump. Pendência (repos de app native): adicionar o indicador + a asserção via/xadm-docs.
Corrigido¶
-
Invariante de observabilidade do processor de erro da casa — 5xx mapeado pelo seam não pode sair silencioso (feedback §6). A norma de níveis de log (java-micronaut §Erros) presumia "exceção não-tratada já estoura e o
SentryAppendercaptura sozinha" — premissa que fura quando o app adota o seam: exceção de domínio que o seamPortadoraDeProblema/ExceptionHandlermapeia a 5xx é tratada (devolve resposta), logo oRouteExecutornão a loga como "Unexpected error" → o 5xx sai silencioso, invisível ao GlitchTip; era a observabilidade que o app perdia ao trocar o handler dedicado que logava pelo processor da casa. Registrado o invariante (decisão do Gustavo): todo 5xx chega ao GlitchTip exatamente 1×, com contexto de request (method/path/request-id via MDC); 4xx = culpa do cliente, não vai a ERROR. Default-on, não flag opt-in (o feedback pedia opt-in — 5xx silencioso é justo o modo de falha que ninguém quer poder desligar por config). Central fixa o invariante; o mecanismo (processor por default × ponto de mapeamento) é desenho doxadm-comum-web— segunda metade do que a lib entrega no 0.3.0, ao lado do seam decode/title(pendência de repo de lib). Sem bump (java-micronaut.md é página dev, não rastreada); sem gate (comportamento de log não é detectável estático — ponto cego declarado). -
App native "com duas caras": topologia da migração definida no 0022 (feedback §6, constituição 0.31.2). A 0.31.0 publicou o
templates/dockerfile-java-native(à mão), mas apps reais entregaram native pelo plugindockerBuildNative(base wolfi-base, imagem empurrada pelo próprio repo) — um app native "conforme" podia ter duas caras, e a página não dizia se o caminho do plugin era equivalente ou divergência. Resolução (decisão do Gustavo): durante a migração, o repo do app mantém oDockerfileJVM (.jar); a imagem native é buildada por um build à parte, em OUTRO repositório (a fábrica de imagens, 0003), que roda onativeCompilecom a receita da casa e empurra a imagem; o Coolify puxa (Build PackDocker Image). Isso dá uma cara só (JVM) no repo do app; o native vive na fábrica, com o Dockerfile à mão como receita única (debian-slim + curl pro HEALTHCHECK §5 — a wolfi-base do plugin sem curl quebra o §5). App que hoje builda native no próprio repo via plugin = caminho interino a reconciliar. Nova seção §Topologia no 0022 + bullet em java-micronaut §Deploy + nota no header do template. Bump PATCH (1 rastreado re-carimbado:templates/dockerfile-java-native). Pendências declaradas (§1.6): reconciliar o header "destino: raiz do repo" (é o estado final) e o status exato do caminho plugin nos apps que já entregaram assim (ação de repo de app, §3). Correção factual ao feedback: o native não é buildado pelo Coolify — builda off-host e o Coolify puxa a imagem pronta; a hint de reflect-config doSentryAppenderfoi confirmada pelo app. -
config-cache × native colidem por default:
--no-configuration-cacheno build native (feedback §6, constituição 0.31.1). A base de perf (gradle.properties, 0.30.0) ligaorg.gradle.configuration-cache=true, mas as tasks native do plugin Micronaut (dockerBuildNative/dockerfileNative) não são CC-safe — oNativeImageDockerfilelê a saída dogenerateResourcesConfigFilesem dependência declarada e estouraQuerying the mapped value of :generateResourcesConfigFile outputFile before task has completed. Com a 0.31.0 tornando native o default do server elegível, as duas bases colidem sem aviso. Cura: odockerfile-java-nativepassa--no-configuration-cachena linha donativeCompile(o CC dá zero ganho num compile cold único — container efêmero, sem daemon, cache nunca reusado — então dodge do bug sem perda); a nota dogradle.propertiese o novo bullet em java-micronaut §Deploy nomeiam a colisão como o caso concreto do opt-out. A base mantémconfiguration-cache=true— o app JVM se beneficia; só o caminho native abre mão. Bump PATCH (2 rastreados re-carimbados:templates/dockerfile-java-native,templates/gradle.properties). Pendência (repos de app native): re-derivam odockerfile-java-nativevia/xadm-docs(ganham a flag); quem builda/roda native localmente passa--no-configuration-cache. -
Porta de teste fixa quebra no Windows (faixa excluída) +
TEST_DB_URL/DB_URLdivergentes (feedback §6). App fixouTEST_DB_URL=localhost:55432noapplication-test.properties;55432cai numa faixa reservada do Windows (Hyper-V/WSL/Docker Desktop reservam blocos dinâmicos — bind falha sem nada ouvindo), Test Resources não binda →initializationErrorem massa (verificado:55432∈55429–55528vianetsh interface ipv4 show excludedportrange protocol=tcp). A norma da casa já era a cura — a URL de teste vem injetada pelo Testcontainers/TestPropertyProvider(porta efêmera), não de env — mas não avisava contra a porta fixa nem nomeava o hazard. Documentado o hazard em agentes §Loop (afeta toda porta fixa:explicitPortdo shared server, docker-compose publish, debug) e a regra de DB de teste + naming (DATASOURCES_*, nãoDB_URL/TEST_DB_URL) em java-micronaut §Testes. Páginas dev, sem bump. Pendência (repos de app): quem fixou porta de teste migra pra URL injetada e larga oTEST_DB_URLvia/xadm-docs.
0.32.0 - 2026-08-19¶
Adicionado¶
- native-image é o padrão de deploy do server Micronaut elegível; ler XLSX com FastExcel, não Apache
POI (constituição 0.31.0, feedback §6 do piloto
bi-transporte-xls). A frota já tinha vários servers em GraalVM native-image (RSS ~3× menor, imagem ~9× menor, sem build-spike de RAM no host — a causa do freeze da VM de produção), mas o central nunca documentou o padrão: conhecimento nos repos de app, sem dono citável. Ratificado em duas decisões — 0022 (native é o alvo de deploy do server Micronaut elegível; Java-8integrador-cliente Flutter fora por stack; JVM = exceção declarada) e 0023 (ler XLSX comorg.dhatim:fastexcel-reader, não POI: XMLBeans quebra native comClassCastExceptionnoStylesTable, sem caminho estável na CE; POI só emtestImplementation). Novos: camponative: truenodocs/app.json(discriminador do gate e do e2e), template rastreadodockerfile-java-native(builder GraalVM CE +nativeCompile+ runtime glibc com curl), guarda noci.ymlque reprovapoi-ooxmlem escopo não-test de app native (discrimina pornative:true/ Dockerfile native, não pelo plugin — oio.micronaut.applicationtraz o GraalVM transitivamente). Recipes em java-micronaut §Deploy (perfil GraalVM CE —-Os/-Ob/--gc=serial; G1/PGO/build-report são Oracle-only;-O3==-O2na CE), §Específico (leitura FastExcel), §Observabilidade (reflect-configdoSentryAppender), §Armadilhas (POI silencioso no build) e §Testes (a fixture do ramo "numérico-data → data" vem de ferramenta externa — o roundtrip FastExcel writer→reader perdegetDataFormatString); seção e2e native em e2e-local;app-novonasce native por default. Bump MINOR 0.30.5 → 0.31.0 (obrigação nova sobre o app server elegível — native default + e2e native na definição de pronto). Pendências humanas (repos de app): migrarbi-comercial-xlseintegrador-server(POI → FastExcel, cada um com golden-master + e2e native); a libxadm-comum-webregistra oreflect-configdoSentryAppender(§3). Refino (feedback §6 do appcentral-backend): a receita ganhou os números medidos em produção (boot ~1 s, RSS ~43 MB, imagem ~217 MB), a confirmação de que o stack da casa (AWS SDK v2,json-smart, Flyway, Hikari, serde, JCA stock) roda semreflect-configmanual, e o contrato de deploy no Coolify — o native usa Build Pack:Docker Image(imagem buildada off-host, empurrada pro Forgejo registry, o Coolify puxa pronta), nunca oDockerfiledefault, que buildaria native no host (freeze). -
Auth e serviço externo também são e2e-local — não "staging manual" (constituição 0.30.5, feedback §6). O piso da definição de pronto (§6-(1)) já cobrava "runtime real", mas não fechava o caso de auth: serviço nosso (ex. o login do central-backend) roda de verdade, local e automatizado, em CI — não existe "a auth é real, então tem que ser staging". Ambiente deployado + clique de operador é smoke de deploy, não nível de teste mantido verde; "staging semi-manual com dado de produção" polui o prod e não atesta lógica (é a mesma do caminho automático que o e2e-local já dirige — ex. "Baixar Manual" e a macro chamam o mesmo
DespachoService.despachar). Serviço externo não-executável entra por emulador oficial ou, quando o emulador não assina (validador exige token RS256 e o emulador emitealg:none), por JWKS próprio com a busca repontada por um seam de config (default = produção intacta, override só em teste, ex.auth.firebase-jwks-url). App que valida token de terceiro por URL/JWKS hardcoded ganha esse seam mínimo (retrocompatível) em vez de exigir ambiente deployado — e o override tem de ser de config (o harness do e2e é externo ao processo; umif (teste)in-process não conta). Piso curto na constituição §6-(1); receita em e2e-local § Serviço externo que assina (princípio + o seam) e mecânica Micronaut (property override, emulador × RS256) em java-micronaut § Testes. Bump PATCH 0.30.4 → 0.30.5 (refino do piso §6, text-only; nenhum template rastreado muda — a norma propaga por leitura). Sem gate (seam é heurístico, régua dos itens 93/103/108). -
Gate de docs pré-tag: nav-drift vira bloqueador + regra "sem link relativo pra fonte" (constituição 0.30.4, feedback §6 — 2 releases seguidos do BI Comercial OnPetro quebraram no
docs.ymldepois da tag). Metade do feedback (espelhar os gates de docs no pré-flight da/xadm-release) já estava feita (item 132 tornou o Gate de DOCS drift-proof: "rode cada gate que o teudocs.ymlinvoca", incluimkdocs --strict); recorrência = skill stale. Furo real que sobrava: nav-drift (.mdfora do nav) saía como INFO e o--strictnão bloqueava — app comum não temcheca-nav(central-only). Agora otemplates/mkdocs.yml(e omkdocs.ymlcentral, dogfood) ligavalidation.nav.omitted_files: warn+links.not_found: warn→ órfã e link pra fora dedocs/viram bloqueador de--strictem todo app, sem script novo. Regra de autoria nova (prevenção) na constituição §1 + publicar docs §Validação no CI: docs referenciam fonte por code span (`lib/.../foo.dart`) ou URL absoluta do Forgejo (https://fonte.xadm.biz/xadm/<repo>/src/branch/master/...), nunca link relativo pra fora dedocs/(o--strictaborta). Bump PATCH 0.30.3 → 0.30.4 (templates/mkdocs.ymlrastreado re-carimbado). Lint dedicado descartado:--strict validationjá cobrem a detecção; a norma é prevenção.- Baseline de operação do host do Coolify (constituição 0.30.3, feedback §6 —
incidente de freeze 2× da VM de produção
bi-ubuntudurante deploy, 2026-08-18). O central documentava o app (Dockerfile, envs, libs), nunca o host onde o Coolify roda. Nova seção §Baseline de operação do host em Operação no Coolify: swap dimensionado (≥ RAM/2,swappiness=10),earlyoomque mata antes do livelock e poupa os bancos, limite + reserva de memória por recurso (o gotcha: recurso nascelimits_memory=0= ilimitado; app Java sem teto derruba a VM — tabela por perfil: Server Micronaut 768M/256M, XLS 1G/384M, Ingest 1.5G/512M), não buildar no host de produção (dind = pico de RAM, o gatilho do freeze; cross-link decisão 0003) e runbook de diagnóstico. Norma-ponteiro na constituição §5 ("app deployável declara limite + reserva; build fora do host") — doc de plataforma, não de app, como os três mecanismos de entrega. Bump PATCH 0.30.2 → 0.30.3 (norma-ponteiro, sem obrigação nova de conteúdo;coolify.md/constituição são prosa, não template rastreado — a norma propaga por leitura, não por download do manifesto). - Rodar testes Micronaut/Gradle sem travar (concorrência + performance) (constituição 0.30.0,
pesquisa a pedido do Gustavo: vários
gradlewjuntos travam — pile-up de container, colisão de porta do embedded server, daemon wedged). Núcleo em java-micronaut § Testes:- Test Resources COMPARTILHADO como default da casa —
sharedServer = true+explicitPortnobuild.gradlede cada app Micronaut: 1 server e 1 conjunto de containers reusado entre builds/repos concorrentes. Norma nova de ciclo de vida (server sobrevive; parada limpa =stopTestResourcesService), reconciliada com a §Armadilhas de órfão (compartilhar é intencional ≠ órfão obsoleto); hazard de kill no Windows + reaper do caso compartilhado; pin de container compatível para reuse cross-repo. - Novo
templates/gradle.properties(rastreado, opcional Java/Gradle): base de performance (parallel/caching/configuration-cache/daemon/jvmargs), cada chave comentada, opt-out local do CC. Fork policy documentada (unit paraleliza; integração modesta por causa do Postgres compartilhado + seed committado). Split unit×integração clarificado como por environment, não source set Gradle.
- Test Resources COMPARTILHADO como default da casa —
Corrigido¶
-
/xadm-release— Gate de DOCS do pré-flight vira drift-proof: fonte da verdade = odocs.ymldo repo, não uma lista fixa (constituição 0.30.2, feedback §6 de app: pré-flight rodou só o gate de código, tag pushada, pipeline de docs estourou pós-push). O gate de docs já existia desde ≤0.18.4 — a causa deste caso foi skill stale (app não re-sincronizou). Mas o feedback expôs fresta de clareza real: o pré-flight enumeravacheca-admonition.pysob "se o repo os tiver (central)", quando todo app o roda nodocs.yml(curl da toolchain), e punhagera-manifesto.py --check(conceito central-only) no bucket "sempre". Agente app-side lendo a lista podia achar o gate quase-central e pular. Decisão do Gustavo (AskUserQuestion): refino drift-proof — o Gate de DOCS agora lidera com "abra teu.forgejo/workflows/docs.yml(build-site.ymlno central) e rode cada gate que ele invoca" (o CI real do repo é a fonte da verdade; enumerar aqui driftaria); o conjunto de todo app (valida-frontmatter+lint-mermaid+mkdocs --strict+checa-admonition) fica explícito, e o superset rastreado (gera-manifesto --check,checa-nav,checa-dogfood,checa-views,testa-*) vira soma do central — "app comum não tem, não os invente". Só o template (templates/xadm-release-skill.md); a cópia local.claude/skills/xadm-release/SKILL.mdé especialização de repo único (enumera os scripts central corretos) — sem a confusão app-side, não espelhada (precedente itens 110/111).[Unreleased]→ seção versionada, deixando conteúdo já-lançado órfão e sem## [1.5.2]no corpo). Causa: a opção "Pular CHANGELOG" do passo 5 (só bump+tag) era oferecida incondicionalmente e, com[Unreleased]cheio, estranhava as notas e taggava sem seção; a rede de segurança (valida-release.py) só rodava norelease.ymlpós-push da tag (tarde e não-gating, item 62). Decisões do Gustavo (AskUserQuestion ×2): gate local + reformar a opção e remover o "Pular CHANGELOG".valida-release.py --tag v<nova>passa a rodar no passo 8 (guarda final, pré-tag, todos os repos) ao lado dogera-manifesto --checkdo central — vermelho = não taggar; e o passo 5 perde a opção de pular (constituição §5 obriga a seção; intervalo vazio = release não deveria ocorrer). Template + cópia local da skill. complementa a cura durável dogitattributes-java, item 97). O bullet do gate já documentava causa e cura; ganhou a frase de diagnóstico:git ls-files --eol gradlew— blobi/lf= artefato local do autocrlf (Coolify checkout LF no Linux, produção OK) = não bloqueador; blobi/crlf= bloqueia. Template-only (a cópia local da skill não tem gate de container). -
Base rastreada do
.claude/settings.json—templates/settings.json(constituição 0.29.1, feedback §6 de app: bootstrap Java/Micronaut sem allow-list base, agente reinventava e prefixavaJAVA_HOME=…inline, quebrandoBash(./gradlew *)). Decisões do Gustavo (AskUserQuestion): template rastreado mínimo (não só prosa) e duas curas JAVA_HOME paralelas (não eleger primária).- Novo
templates/settings.json(rastreado, core):env.PYTHONUTF8+ allow-list base universal por-ferramenta em Bash e PowerShell (git, rtk, python×3, node, mkdocs, docker, curl escopado). Adições por stack documentadas (JSON não aceita comentário): Java./gradlew/jar/javap/unzip, Flutterflutter/dart, Nodenpm./xadm-docsre-deriva a base e preserva hooks/additionalDirectories/grants locais (customizado-por-app, como oci.yml). - Correção ao feedback (loop §6):
env.JAVA_HOMEnão pode ir no versionado — o valor é path absoluto de máquina, difere por dev → vive no.claude/settings.local.jsongitignored (coerente com a doutrina §Higiene). O grant de leitura da memória do agente idem. Só o allow-list base (ponto 2 do feedback) é versionado; JAVA_HOME (ponto 1) e memory-read (ponto 3) são regra de arquivo local, documentada. - Regra viva em ferramentas § higiene do settings.json: base +
adições por stack +
env.JAVA_HOMEcomo cura paralela aosdk default java 25do §rtk (aenvé per-repo, osdk defaulté global — qualquer uma dispensa o prefixo inline que fura o allowBash(./gradlew *)e o hook do rtk). §rtk Java/Gradle reconciliado. - Satélites (§8): constituição §6 (ponteiro à base + exemplo
python3 *corrigido do brittlepython3 scripts/*), templates.md (linha + seção## Config do agente), app-novo passo 3 (bullet de cópia + nota do JAVA_HOME local), xadm-docs-skill (mapa de destino). Bump PATCH 0.29.0 → 0.29.1 (template novo sem obrigação de conteúdo sobre apps existentes — revisável; 2 rastreados carimbados:templates/settings.jsonnovo,templates/xadm-docs-skill.md).
- Novo
- Padrão e2e-local — suíte de teste ponta a ponta local de fluxo de integração
(constituição 0.29.0, feedback §6 de app: a suíte
e2e-maxsul-piedprovou a forma; a casa não tinha norma nem receita citável para teste de fio multi-app). Decisões do Gustavo (AskUserQuestion): norma normativa na constituição + bump, âncora §8, sem ADR (promovível), receita doc-only (scaffold rastreado deferido — REGRA Nº 3 — até uma 2ª suíte provar o esqueleto idêntico), processo de integração §6 direta.- Nova página engenharia/e2e-local.md com o padrão completo
(11 seções): princípios (apps reais/fake só nas bordas; build local carimbado por commit;
tudo por arquivo; zero interação; exit
0/1; runtime descartável), estrutura de pastas canônica, config por projeto,builds-latestcarimbado, fakes nas bordas, blocos de script +run.ps1, as 4 verificações obrigatórias e as armadilhas de PowerShell/Micronaut. - Constituição §8 ganha o bullet "Verificação ponta-a-ponta local" (norma: fluxo de
integração tem suíte e2e-local; projeto novo nasce com, existente adota por migração
oportunista §1.6). Bump MINOR 0.28.3 → 0.29.0 (obrigação nova ao fluxo de integração;
text-only, nenhum template rastreado mudou —
gera-manifesto --updatere-carimba o campoconstituicao). - Manifesto de build committado (recomendado) no §4 (2ª leva de feedback §6, mesma
sessão): arquivo pequeno versionado que registra
<app> SHA-full assuntopor build, pra o git guardar a proveniência que o jar gitignored perde. Decisões do Gustavo (AskUserQuestion): deriva do nome do.jarembuilds-latest\(prova qual jar está no disco, fecha o risco git-HEAD-ao-vivo ≠ binário), opção recomendada (não norma), sem timestamp volátil (evita diff sujo a cada run)..gitignoredo §9 ganha o!builds-latest/manifesto.md. - Ponteiros (satélites §8): ci-testes §E2E, glossário (termo
e2e-local), integracao.md §Entrega garantida, engenharia/index.
- Nova página engenharia/e2e-local.md com o padrão completo
(11 seções): princípios (apps reais/fake só nas bordas; build local carimbado por commit;
tudo por arquivo; zero interação; exit
Corrigido¶
- Exceção
@Asyncna norma de seed de teste Micronaut (feedback §6 de app; refina o item 103). O bullet de seed de teste (java-micronaut.md§Testes) dizia "não cure comtransactional=falsepor reflexo" sem ressalva — armadilha pra quem testa pipeline@Async: a thread do TaskExecutor tem tx própria (thread-bound), não vê o seed não-commitado do corpo do teste e commita os próprios writes de verdade; abort de FK envenena a conexão se o pipeline segue sem rollback. Decisões do Gustavo (AskUserQuestion): refinar o bullet do 103 inline (a exceção fica colada à regra, impossível ler uma sem a outra) e ponto cego declarado (sem gate — "write cross-thread sobtransactional=true" é heurístico, mesma régua do 103/108/113). Adicionada a exceção: teste de@Async+ commits reais usatransactional=false+ limpeza por conexão direta (não há tx ambiente pra rollback — mesma disciplina doNoConnectionExceptionque o 103 já cita). Ponteiro em ci-testes §E2E estendido (a página não pode implicar que@BeforeEaché o único padrão). Sem bump (páginas dev, não rastreadas).
0.31.3 - 2026-08-11¶
Adicionado¶
- Check de conformidade doc-de-projeto ↔ construído nas skills da casa (constituição 0.28.1,
feedback §6 de app:
/xadm-docsaudita template/engenharia/frontmatter mas nunca cruza o conteúdo do desenho —docs/projeto/,docs/decisoes/— contra o que o app construiu; dois drifts reais passariam batidos — app com decisão sem registro,arquitetura.mdprevendo PowerSync enquanto o build fez poll+poke). Decisões do Gustavo (AskUserQuestion): host duplo, escopo local, 3c sinaliza os dois tipos, sem RD, bump PATCH./xadm-docsganha o sub-passo 3c (templates/xadm-docs-skill.md): varredura repo-wide, passe leve em toda rodada (roda mesmo com a versão em dia — drift doc↔build independe da versão; o early-exit "em dia → parar" só dispara depois de 3c limpo). Sinaliza (A) desenhostatus: aprovadoque o build contradiz e (B) decisão de design no código semdocs/decisoes/0NNN. Guardrail: sóstatus: aprovadode estado-atual cobra bater; roadmap isento. Não é gate — item de julgamento humano, saída = propostas de autoria deferíveis (passo 5 não auto-aplica). Escopo local (sem repos irmãos, §3)./x-documentarreforça o passo 3 (templates/x-documentar/SKILL.md): fecha o delta não-coberto — decisão que a sessão tomou e não viroudocs/decisoes/0NNN(RD faltante, não só desatualizado) + nomeiadocs/projeto/(arquitetura) entre os docs a conferir. Bump PATCH 0.28.0 → 0.28.1 (2 rastreados re-carimbados:xadm-docs-skill.md,x-documentar/SKILL.md; refino de skill, sem obrigação nova ao app).
- Receita: consumir a
xadm-comum-storage(lib da casa) emengenharia/java-micronaut§storage (feedback §6 de app). Camada acima do consumo de Garage/S3 com o SDK cru: injete oS3Clientbean (aS3ClientFactoryé@Factoryde método package-private, não é para chamar direto); prefixo fixos3.*→ ponte pro namespace semânticogarage.*via@Factory+@Replaces(S3Config.class); exclua onetty-nio-clienttransitivo da lib quando usaurl-connection-client(senão dois HTTP clients, o 2º Netty colide com o do Micronaut). Código da lib mora noxadm-commons(outro repo, §3) — norma registrada aqui. Sem bump (java-micronaut.mdé página dev, não rastreada).
Corrigido¶
- Toolchain da
/xadm-docs: dois furos de determinismo no passo 2 (constituição 0.28.2, feedback §6 de app). Decisões do Gustavo (AskUserQuestion): A1 = prosa da skill; B/A2 = receita completa + fix mecânico.- Scaffold copy-once fora do manifesto era descoberto por tentativa. O kit linka
/css/app.css(404 sem o arquivo), masapp.css/gitignore-fluttersão copy-once e não entram no manifesto por desenho (re-derivar apagaria trabalho do app) — o passo 2 mecânico, que lê só o manifesto, os perdia. Correção: o passo 2 da skill ganha a lista explícita dos copy-once + raw path (<raw_base>templates/<nome>) + a regra "copie SE AUSENTE, nunca sobrescreva".templates/app.cssjá era publicado raw (Dockerfile) — o furo era só de discoverability. - CHANGELOG da constituição sem URL pública. O passo 2 manda checar a entrada do CHANGELOG da
versão
mudou_empara decidir se um defasado multi-stack toca a stack, mas nenhum path resolvia (/toolchain/*= 404: a página/changelog/é nav top-level, fora de/padroes/, não cai no dual-publish). Correção: o Dockerfile publicaCHANGELOG.mdraw em/toolchain/CHANGELOG.md(ao lado daconstituicao.md); a skill ganha a URL + a nota de que os cabeçalhos são versão de site (desacoplada), então procura-se a string da versão da constituição no corpo, não um heading. Bump PATCH 0.28.1 → 0.28.2 (xadm-docs-skill.mdre-carimbado).
- Scaffold copy-once fora do manifesto era descoberto por tentativa. O kit linka
- Checkstyle
ConstantNamereprovava os campos@ArchTestdo ArchUnit (constituição 0.28.3, feedback §6 de app). As regras do ArchUnit são camposstatic final ArchRuleem camelCase (é como a lib as descobre) — declarações de regra, não constantes semânticas; oConstantName(UPPER_SNAKE) as reprovava e deixava ocheckstyleTestvermelho em todo app com teste de arquitetura assim que o cache invalidasse. Correção:templates/suppressions.xmlsuprimeConstantNameno teste (test-wide, espelhando oMethodNameque a casa já relaxa — naming de teste ≠ produção), em vez de relaxar oConstantNamenocheckstyle.xml(valeria pra produção). Nota cruzada emengenharia/java-micronaut§Guarda de fronteiras. Bump PATCH 0.28.2 → 0.28.3 (suppressions.xmlre-carimbado).
0.31.2 - 2026-08-11¶
Adicionado¶
- Três normas de extração/adoção de lib compartilhada (feedback §6, dois incidentes de adoção:
xadm-comum-web0.3.0/health;xadm-seguranca0.2.0StaticBearerTokenValidator). Raiz comum: componente compartilhado que regrediu frente à origem, ou ocupou recurso global sem o adotante poder sobrepor — nenhum pego no code review por falta de norma citável. Decisões do Gustavo:- Norma 1 — extração não pode REGREDIR segurança/robustez. Byte-idêntico é gatilho de
revisão, não meta: copiar fielmente copia os defeitos da origem, que uma vez na lib alcançam
todo adotante. Reconcilie pro baseline endurecido (não o mais simples); classe de segredo/auth
roda o checklist do revisor na extração (diff semântico, não textual). Emenda no
0019 + item 7 no checklist de
engenharia/seguranca.md. - Norma 2 — bean de lib em recurso global é condicional + sobreponível. Bean vindo de lib que
ocupa rota/
ErrorResponseProcessor/filtro/contrato não pode ser incondicional: guarde com@Requires(missingBeans/property) — cede por default, ativa por ausência.@Replacestroca o bean mas não desfaz a rota; método executável (@Get/@Post) nuncaprivate/package-private (quebra subclasse do adotante). Incidente:@Controller("/health")incondicional + management transitivo = duas rotasGET /health= 400 em toda request. Norma emengenharia/java-micronaut.md§Específico + armadilha do método executável em §Armadilhas. - Norma 3 — validador de segredo: três invariantes numa classe. Constant-time (
MessageDigest.isEqualsobre bytes) · fail-closed no segredo vazio (esperado ausente/branco NEGA, jamais casa com entrada vazia — a metade auth-bypass do incidente) · config que não quebra boot (@Valuecom default ou@Requires, validador dormente quando a feature não é ligada). Reforça o bullet constant-time deengenharia/seguranca.md§Authn/authz (constant-time já era norma; as outras duas eram implícitas). Sem gate central — detecção (equals em segredo;@Controllerde lib sem@Requires) é heurística e/ou mora no CI doxadm-commons(outro repo); cura = teste do adotante + checklist. Sem bump —0019/seguranca.md/java-micronaut.mdsão RD/páginas dev, não rastreadas no manifesto.
- Norma 1 — extração não pode REGREDIR segurança/robustez. Byte-idêntico é gatilho de
revisão, não meta: copiar fielmente copia os defeitos da origem, que uma vez na lib alcançam
todo adotante. Reconcilie pro baseline endurecido (não o mais simples); classe de segredo/auth
roda o checklist do revisor na extração (diff semântico, não textual). Emenda no
0019 + item 7 no checklist de
0.31.1 - 2026-08-11¶
Corrigido¶
- Norma: o processor de erro da casa (
xadm-comum-web) honracode/titleda exceção, não só do status. Dois feedbacks §6 convergiram no mesmo defeito doUnifiedErrorResponseProcessor0.2.0: fixacode = status.name()(auth — perde ~28 machine-codes consumidos pela Central) etitle = status.getReason()(int-sascar — perde o contrato de erro titulado), sem ler a causa-raiz → ambos forkam o processor via@Replaces, o oposto do que a lib existe pra eliminar. A metadecodejá contradiz a norma escrita emengenharia/java-micronaut.md(§Corpo de erro:codeestável e uniforme por classe). Decisões do Gustavo: (1) ponto de extensão na lib (não documentar processor local como caso especial); (2) seam honracodeetitle(título de tipo, RFC 9457 §3.1.4; ocorrência fica nodetail); (3) registro emjava-micronaut.md§Corpo de erro + nota no 0020. Sem bump — páginas dev/RD não rastreadas. Pendência humana (repo de lib): fix noxadm-comum-web0.3.0 (seam via interface portadoracode()/title()ou exceção-base); em 0.2.0, app de taxonomia rica mantém o processor local até o 0.3.0.
0.31.0 - 2026-08-11¶
Adicionado¶
- Constituição ratifica
xadm-commonscomo baseline da casa + entrega garantida M2M + maven público (constituição 0.28.0; passo CONST da plataforma libs+contrato, Fase 0/itens 112). A Fase 0 criou o mecanismo (0019) e o contrato (0020); as Fases 1–3/5 publicaram 8 libs (br.com.xadm, registro públicofonte.xadm.biz). Agora a constituição fecha a norma:- Baseline (§5): app Micronaut elegível consome as libs da casa (
xadm-seguranca,xadm-comum-web,xadm-mensageria,xadm-comum-util,xadm-comum-storage,xadm-comum-teste,xadm-comum-powersync,xadm-ingestion-core), não recopia infra transversal. Elegível = server Micronaut;integrador-client(Java 8) e os Flutter ficam fora por stack (fronteira, não débito). A constituição fica com o princípio + nomes; as versões são donas do 0019 e o operável (blocorepositories{}) é da Engenharia (§1.9). Obrigação nova sobre os apps → bump MINOR. - Decisão 0021 — entrega garantida M2M at-least-once: a casa entrega via outbox at-least-once
(relay sem lock/dedup) → receptor idempotente por chave natural é precondição, não opção;
exactly-once (dedup/lock na lib) deferido (REGRA Nº 3). Os 3 mecanismos de entrega
(HTTP M2M · fila PowerSync · ZIM/dXpEnvio) e o quando cada um entram em
documentacao/integracao.md§Entrega garantida, com ponteiro do §8 da constituição. - Decisão 0019 alinhada ao maven público (in-place): o registro migrou de Forgejo Packages
privado para público — consumir é leitura anônima (bloco
repositories{}semcredentials{}); publicar ainda usaFORGEJO_PACKAGES_*. Tabela dos 8 módulos+versão como prova; nome de módulo reconciliado (xadm-armazenamento→xadm-comum-storage). Receita de consumo emjava-micronaut.md§Específico; assimetria publish×read emseguranca.md§Segredos. URL confirmada contra o consumidor real (integrador-server). - Glossário: termos novos At-least-once e xadm-commons.
- Baseline (§5): app Micronaut elegível consome as libs da casa (
- Engenharia (Micronaut): recipe de arquivos Garage/S3 (consumidor) (feedback §6 de app). Lacuna:
java-micronaut.mdnão tinha receita de S3-consumer. Padrão validado em produção agora em §Específico — AWS SDK v2s3com path-style (forcePathStyle(true); o Garage não resolve bucket por virtual-host DNS) eurl-connection-clientno lugar do async Netty (onetty-nio-clientdo SDK conflita com o Netty do Micronaut no classpath), credencial de feature opcional via@Requires(property=…, pattern=".+")(sem a env, o bean doS3Clientnão sobe e o contexto não estoura), envsGARAGE_*via placeholder single-level noapplication.yaml, e teste comadobe/S3Mock(não MinIO). Página dev, não rastreada no manifesto → sem bump por si. - Engenharia (Micronaut): guardrail das duas ordens no registry de handler (feedback §6 de app). A seção
Espelho X-Adm — registry de handler por tabela ganha o caso em que a ordem de aplicação (upsert,
pai-FK antes do filho) e a de reversão (delete, filho antes) diferem: o handler expõe
ordemPuteordemDeleteseparados (umordersó quando coincidem). Trava por teste — a reversão por soft-delete não dispara constraint de FK (só marca a coluna), então umordemDeleteerrado passa verde e o reorder quebrado só aparece quando um delete real bate no FK em produção; o teste afirma a ordem de reversão explicitamente. Amarra ao anti-deadlock do §Banco (DELETEtrava em ordem de scan, não a de put). Impl-referência:integrador-server.ia/010. Página dev, não rastreada → sem bump por si. - Engenharia (Micronaut): posse de migração em DB compartilhado — consumidor não migra em prod
(feedback §6 de app, iniciativa
xadm-mensageria). O owner-writes cobria consumidor que lê via PowerSync; faltava o caso do outbox: consumidor que lê a tabela compartilhada direto (JDBC/Micronaut Data). Regra nova em java-micronaut §Banco — o dono único cria e migra o DDL (remetente/outbox = integrador); o consumidor não tem migração de produção para a tabela alheia (dois Flyways na mesma tabela divergem por checksum;CREATE/ALTERdo consumidor recria schema que não é seu). O schema dos testes do consumidor vem de migração test-only (flyway.locationspor ambiente: prod = próprias tabelas; teste adicionadb/migration-testcom o DDL emprestado), e o deploy é coordenado (dono migra primeiro; consumidor sobe depois). Cross-link emintegracao.md§Fronteiras (posse de escrita ≠ posse de migração) + Fase 0 (0019/0020). Páginas dev, não rastreadas → sem bump por si.
Corrigido¶
- Infra/Skill: envs do Garage e contrato do
/provision(feedback §6 de app). (1)infraestrutura/garage.mdlistava as envs do app comoS3_*; o broker do/xadm-setup(auth) injetaGARAGE_*— derivação<PROVIDER>_<KEY>doCoolifyClient:GARAGE_ENDPOINT/GARAGE_BUCKET/GARAGE_REGION/GARAGE_ACCESS_KEY_ID/GARAGE_SECRET_ACCESS_KEY(sufixos AWS SDK) — a tabela foi corrigida para os nomes reais. (2) o payload do/provisionexigenomeeproduction_url, mas o §3 da skill/xadm-setupe o contrato emcentral-de-apps.md§Auth listavam sóapp_id/feature/grupo/cliente_id: semnome→400 nome_obrigatorio; semproduction_url→ secret não injetado (o app cai no gate "mobile/local" e o server fica sem write-key silenciosamente). Ambos declarados como obrigatórios nos dois lados — mesma classe do drift(app_id, feature)que já deu 500 em produção; fecha a inconsistência com o §Auth, que já usavaproduction_url. Bump PATCH da constituição 0.27.6 → 0.27.7 (1 rastreado re-carimbado:templates/xadm-setup-skill.md;garage.md/central-de-apps.md/java-micronaut.mdsão páginas de site, não rastreadas). O buggarega/typo do broker eAUTH_CIPHER_KEYde 0 bytes são do repocentral-backend(Camada 2) → relay ao mantenedor, sem mudança no central. - Engenharia (Micronaut):
checkque pendura por Testcontainers órfão + regra anti-reuse (feedback §6 de app). O bullet de órfão da casa cobria só o Test Resources (sintomaservice not available); faltava o modo de falha vizinho:checkque arrasta por minutos (mata-e-roda "resolve"), de container do Testcontainers empilhado + daemon Gradle wedged. Verificado que o pile-up real só ocorre em reuse mode (.withReuse(true)/testcontainers.reuse.enable=true— o reusável é excluído do Ryuk de propósito) ou por SIGKILL repetido; na norma da casa (singleton JVM-static) o Ryuk reapa e nada empilha (a atribuição do feedback a "Ryuk órfão" foi corrigida).java-micronaut.md§Armadilhas ganha o reaper de último recurso (./gradlew --stop+docker rm -f $(docker ps -aq --filter "label=org.testcontainers=true")+ re-run em camadas;--offlinese as deps já estão cacheadas) e a regra nomeada: não usar.withReuse(true)(quem opta aceita o pile-up como custo). Página dev, não rastreada no manifesto → sem bump da constituição. - Engenharia (PowerSync): write-back precisa de watchdog (feedback §6 de app). Bug upstream do
PowerSync Kotlin core (≤1.14.1): o connector não re-agenda o
uploadDataapós uma falha — o loop de write-back para e só volta ao reconectar, a fila de CRUD acumula e o campo editado nunca sobe, sem erro visível (reproduzido com e2e nos dois clientes Kotlin da casa; mesma classe já corrigida no SDK JS). Contorno da casa elevado a norma empowersync.md§Cliente: cliente Kotlin de write-back roda um watchdog que re-dispara ouploadDataenquanto houver CRUD pendente — com marca de remoção quando opowersync-kotlincorrigir upstream. Página dev, não rastreada no manifesto → sem bump da constituição. - Engenharia (Micronaut): enriquecedor global de view deve tolerar modelo imutável (feedback §6 de
app). O próprio
ViewModelProcessorde referência da casa (java-micronaut.md§UI) tinha o bug: faziaputIfAbsentno modelo do controller quando presente, e um controller que devolveMap.of(...)imutável — default de@Controllervindo de lib (xadm-mensageriae futuras, Fase 0/decisões 0019/0020) — estouravaUnsupportedOperationException→ HTTP 500, só naquelas rotas. A regra antiga "view controller sempre devolve modelo mutável" é inaplicável a controller de lib (não se impõe a@Controllerde terceiro). Elevado a princípio geral (§Armadilhas): todo enriquecedor global de view copia para umLinkedHashMapantes de mutar (new LinkedHashMap<>(mv.getModel().orElse(Map.of()))+setModel) — a tolerância mora no enriquecedor, que a plataforma controla; "controller mutável" vira higiene secundária. Snippet de referência corrigido. Página dev, não rastreada no manifesto → sem bump da constituição.
Alterado¶
- Engenharia: observabilidade multi-tenant + PII como padrão de casa +
javapp/ verificar API (feedback §6 de app).java-micronaut.md§Observabilidade — para tag global de tenant e controle de PII a app fazSentry.initprogramático (SentryOptions) e oSentryAppenderdo logback anexa ao hub:setTag("cliente", …)carimba todo evento → um projeto GlitchTip/Bugsink filtra N tenants (régua: só quando 1 processo = 1 tenant; ponto cego declarado — app compartilhado 1-instância-N-tenants precisa de tag por request, não prescrito).seguranca.md§Segredos —setSendDefaultPii(true)é padrão de casa deliberado (o SDK vemfalse; a casa liga porque IP/headers aceleram o debug ebug.xadm.bizé self-hosted → o dado não sai da infra, LGPD por não-transferência); numa lib compartilhada (Fase 0) o default alcança todo adotante transitivo → mora como padrão explícito e no CHANGELOG do módulo, nunca flag silenciosa (mesma lógica do enriquecedor global de view).ferramentas.md§javap — verificar assinatura de lib de terceiro sem sources jar (javap -cp <jar-do-cache-gradle> <classe> | grep), a disciplina "verifica antes de afirmar". Páginas dev, não rastreadas no manifesto → sem bump da constituição. - Engenharia (Micronaut): threading do controller + guarda ArchUnit que dorme em Java 25
(feedback §6 de app).
java-micronaut.md§Específico — controller roda no event loop por default (thread-selection: MANUAL); JDBC (Micronaut Data) e SOAP (JAX-WS) bloqueiam o loop em silêncio (o detector do Micronaut é só doBlockingHttpClient), então "corrigir no aviso" cobre 1 de 3 fontes de I/O. Casa 100% imperativa → defaultmicronaut.server.thread-selection: AUTO(offload automático do imperativo;@ExecuteOn(TaskExecutors.BLOCKING)vira override local), com o ponto cego declarado (AUTO decide pelo tipo de retorno). §Guarda de fronteiras — limite conhecido nº 2: ArchUnit lê bytecode via ASM; em Java 25 (major 69) com ASM < 9.8 / ArchUnit < 1.4.1 a classe é pulada no import e todas as regras de fronteira passam vácuas (guarda dormindo, verde) — piso de versão na baseline + defesaassertThat(importedClasses).isNotEmpty(). Página dev, não rastreada no manifesto → sem bump da constituição. - Engenharia (Micronaut): 6 armadilhas de montar lib com Data JDBC + teste de plataforma
(feedback §6 de app).
java-micronaut.md§Testes —TestPropertyProviderexige@TestInstance(PER_CLASS)(senão as props não entram, silencioso); TestcontainerswithInitScripté frágil (shading do commons-io regride, visto no 1.20.4 e reincide na 2.0.x) → aplique DDL por JDBC direto, version-agnóstico; lib de servidor que sobe contexto no teste precisa demicronaut-serde-jackson(runtime), não sóserde-api(senão "No bean of type JsonMapper"). §Armadilhas —@ConfigurationPropertiesaninhado tem de ser construtor-injetado (newinline ignora overrides em silêncio); rota@Viewprecisa de@Produces(MediaType.TEXT_HTML)(senão 406).seguranca.md§Authn/authz —SecurityRule REJECTEDmapeia o status sozinho: 401 sem auth × 403 autenticado. Guarda nova notemplates/ci.yml(decisão do Gustavo): FALHA se módulo Micronaut declaraserde-api+ tem@MicronautTest(sobe contexto) + nenhuma impl de runtime — lib API-only sem@MicronautTesté pulada (ponto cego declarado). Bump PATCH da constituição 0.27.5 → 0.27.6 (1 rastreado re-carimbado:templates/ci.yml; páginas de engenharia não são rastreadas).
0.30.0 - 2026-08-04¶
Adicionado¶
- Decisão 0019 — bibliotecas compartilhadas da casa. Infra idêntica cross-app (28 classes
duplicadas na auditoria de 2026-08-03) passa a viver num único repo
xadm-commons, cada lib = módulo Gradle com SemVer próprio, publicada como artefato Maven no Forgejo Packages; o app consome por versão (implementation("br.com.xadm:xadm-<lib>:X.Y.Z")+ registro Forgejo por<VAR>/env). Base de pacote/groupbr.com.xadm(int-sascar migra debiz.xadm). Rejeitados repo-por-lib e git submodule (versão=SHA). Fim do copy-paste de infra; reduz o risco de drift do IdP Firebase compartilhado. - Decisão 0020 — contrato app↔app. Estende a 0012 (rota/método) fechando o corpo: quem
recebe dita o contrato — o receptor publica OpenAPI (
micronaut-openapi), o consumidor gera@Client+DTOs (openapi-generator) no build; versionamento com compat (janela de deprecação); envelope M2M (Bearer<APP>_API_TOKENpor callee, RFC-7807+code, 1 token/instância); status por caso (200-sempre onde PowerSync/fila exige, status real no par novo sem fila); request-id/MDC propagado entre hops; contract test consumidor×spec. Riscos §1.2 (capacidade doopenapi-generator, janela de deprecação) declarados para a Fase 3.
Alterado¶
- Engenharia: padrões de plataforma da leva 0019/0020.
java-micronaut.md— HTTP app↔app dentro de Micronaut é@Client+@Retryable(OkHttp só em plain-Java), resolvendo a contradição com o transversal; camadas de lib embr.com.xadm.comum.*(pt-BR); nova seção registry de handler por tabela (XadmTableHandler<E>) como a forma anti-god-node de modelar o espelho X-Adm (refactor da Fase 4); o "gap declarado" de request-id/MDC vira norma (filtro noxadm-comum-web).integracao.md— nova tabela Arestas concretas (call-graph) (caller→callee : transporte : endpoint : auth).stack.mdepowersync.mdalinhados ao 0020 (OkHttp×@Client; o callback sempre-200 é o caso que exige 200 no status-por-caso). Constituição 0.27.5 (PATCH — sem artefato rastreado alterado; ADRs e páginas de engenharia são conteúdo de site, não templates do manifesto). /xadm-release: a lista de venenos da skill passa a nomear redirect (2>&1,> arquivo) ao lado do pipe — comando com redirect é composto e não casa allow-list por ferramenta. O gap era só a skill rastreada: a 0.27.3 documentou o sonde-bare emagentes.md/ferramentas.md(páginas de site, que não vão pro repo de app), mas o agente no repo de app só enxerga a SKILL, onde o veneno "pipe" não mencionava o2>&1.java -versiontenta o reflexo de anexar2>&1(escreve versão no stderr) — o bare já casajava *e o harness captura o stderr. Espelhado na cópia local.claude/skills/xadm-release/SKILL.md. Constituição 0.27.4 (PATCH —templates/xadm-release-skill.mdre-carimbado).
0.29.7 - 2026-07-31¶
Adicionado¶
/xadm-release: pré-flight passa a detectar teste OS-desabilitado e avisar sobre CI-verde.@DisabledOnOs(OS.WINDOWS)/@EnabledOnOs(OS.LINUX)é pulado nochecklocal (dev = Windows), o gate fica verde cego e a divergência só aparece na CI Linux, pós-tag (caso real:v0.0.5tagueada, CI vermelha depois). Agora o pré-flight Java procura essas anotações e, se acha, declara no relatório que N testes não rodaram (degradado, não silêncio) + recomenda rodar./gradlew checkno containerci-java:<v>(paridade Linux). O gate CI-verde é aviso, não bloqueio (o Coolify auto-deploya no push, item 62 — gatear o deploy é futuro): o relatório lembra de confirmar a run verde. Allowlist ganhoudocker run *. Doutrina nova emci-testes.md(§«Teste OS-desabilitado é gate cego no pré-flight») + ponteiro emjava.md§Testes. Constituição 0.27.2 (PATCH —templates/xadm-release-skill.mdre-carimbado).
Alterado¶
/xadm-release: allowlist do.claude/settings.jsonpassa a ser por ferramenta (git *,node *,npm *,python */python3 */py *,mkdocs *,docker *,rtk *), espelhado nos namespacesBash(...)ePowerShell(...), no lugar do allow-list por prefixo exato de argumento. O prefixo exato era quebradiço — cada variação de forma (interpretadorpython/py,npm installvsnpm i, flag antes do subcomando,npmde dependência dolint-mermaidausente) voltava a pedir prompt e travava o release no meio. Wildcard por ferramenta mantém o escopo (só as ferramentas do release) e elimina a fragilidade; ocurlsegue escopado de propósito. A garantia "só commita no release" continua sendo a REGRA Nº 2 (conduta), não o prompt. Venenos de comando composto (pipe,$(...), multi-linha) seguem valendo:ferramentas.md§Higiene eagentes.md§Loop de verificação ganham o mesmo padrão (sonde toolchain com comando bare, um por chamada —java -version, nãojava -version 2>&1 | head). Constituição 0.27.3 (PATCH —templates/xadm-release-skill.mdre-carimbado).
0.29.6 - 2026-07-31¶
Adicionado¶
/xadm-release: gate de container para stack Docker Compose (serviço self-hosted, ex. PowerSync) no pré-flight.docker buildprova que a imagem compila, não que a stack sobe — bootstrap (load de sync rules, migração) e healthcheck só rodam noup, e o primeiro a subir a stack de verdade é o Coolify em produção. Gate:docker compose -p <slug>-preflight up -d --wait(volume novo, healthchecks que a constituição §5 já obriga) +down -v; substitui odocker buildse a imagem é pull (sem Dockerfile), soma se o repo builda. Opcional e degradável (sem Docker / falta.env/ porta em uso = ambiente, não bug — declara no relatório). Allowlist ganhoudocker compose *nos dois namespaces. Ponteiro empowersync.md§Deploy. Constituição 0.27.1 (PATCH —templates/xadm-release-skill.mdre-carimbado).- Kit de UI: dois slots opcionais no navbar (
layout.html) —${appModo}(pill de ambiente à direita, renderiza quando presente) e${appVersao}(versão discreta) — nos dois fragments (navbarenavbarComMenu), com estilo nocustom-theme.css. Mata o hand-roll que já produzira drift (um app forkou olayout.html, outro pôs no conteúdo/navMenu). Receita emjava-micronaut.md§UI com oViewModelProcessorde referência que popula os dois em toda view:appModosó quando o ambiente ≠ produção (o layout não compara string),appVersaoda config; detecção de prod e versão ficam app-specific. Constituição 0.27.0 (MINOR — funcionalidade nova, compatível;layout.htmlecustom-theme.cssre-carimbados). - Armadilhas de render Thymeleaf em
java-micronaut.md§Armadilhas: Thymeleaf standalone não tem os objetos implícitos de web (param/session/request, são do Thymeleaf-Spring) — controller lê do request e passa ao modelo; link com variável de path é@{/x/{id}(id=${..})}, não concatenação de String. Cura da família: bullet em §Testes prescrevendo o teste@Clientque fazGETe renderiza de verdade (o./gradlew checknão renderiza template) + ponteiro emci-testes.md§E2E (view server-render é fronteira de contrato).
Alterado¶
- Publicidade de view sob
micronaut-security:@Secured(SecurityRule.IS_ANONYMOUS)na rota vira o idioma primário (rota-exata por construção, sem allowlist paralela pra driftar);isPublicView()naSecurityRulecustom fica para allowlist programática/dinâmica; segue proibidointercept-url-mapgreedy. Rota de view sem decisão explícita = 401. Atualizado emjava-micronaut.md§UI,seguranca.md§Authn/authz e no checklist do revisor.
0.29.5 - 2026-07-30¶
Adicionado¶
- Armadilha Gradle 9.5 + shadow +
application: jar base e shadowJar com o mesmoapp.jarquebramstartScripts/build. Com o pluginapplication,startScripts/distZipconsomem o output dojar; dividir o nomeapp.jarentrejarbase eshadowJarfaz o Gradle 9.5 acusar "uses this output of task ':shadowJar' without declaring … dependency"../gradlew checké imune e o Coolify usa o shadowJar direto (produção não quebra) — morde só o dev que roda./gradlew build. Cura:app.jarsó no shadowJar (dê nome/archiveClassifierdistinto ao jar base), oustartScripts { dependsOn(shadowJar) }. Bullet emjava-micronaut.md§Armadilhas + nota cruzada no §Deploy; ponto cego declarado (sem gate — nada no pipeline rodabuild). Página dev não rastreada → sem bump.
Corrigido¶
- Guarda de placeholder aninhado (
templates/ci.yml) reprovava comentário — falso-positivo. A regex\$\{[^}]*\$\{rodava em toda linha doapplication.yaml/.properties, inclusive comentário: um app que documenta a armadilha (# não use ${A:${B}}, full-line ou inline) fazia o CI falhar com::error::— o gate contra a armadilha proibia falar dela. Agora o step tira o comentário antes da regex (# no início ou pós-espaço) — o Micronaut não resolve comentário, então ignorá-lo é semanticamente correto;#literal em value (url http://a#b, propertiesk=a#b) fica intacto, e placeholder aninhado real (mesmo com comentário na cauda) segue reprovando. Constituição PATCH 0.26.6→0.26.7 (re-carimbo detemplates/ci.yml).
0.29.4 - 2026-07-30¶
Adicionado¶
- Norma "servidor local de verificação pega porta efêmera" (loop de verificação): subir servidor
para conferir — app em modo debug, E2E local, preview — não fixa porta (
8080/8000); pede porta efêmera e lê a URL que o processo imprime. Porta chutada colide com processo vivo ou zumbi e o sintoma engana (bind falha, ou responde outro servidor com build velho — episódio domkdocs servena 8000). Norma emagentes.md(§Loop de verificação) + ponteiro emci-testes.md§E2E; mecânica por stack: Micronaut (MICRONAUT_SERVER_PORT=-1, lerServer Running:;EmbeddedServer.getURI()no E2E) emjava-micronaut.md, Flutter web (flutter runjá é random + imprime a URL; antipadrão é fixar--web-port) emflutter.md. Páginas dev não rastreadas → sem bump.
Alterado¶
- Release da
/xadm-releaseroda sem prompt de ponta a ponta — tag de 1 linha + allowlist Python por SO: a tag anotada era multilinha (string com\nnão casa regra de prefixo) e pedia autorização em todo release — agora é resumo de uma linha (git tag -a v.. -m "v.. — resumo", o detalhe fica no CHANGELOG), casagit tag *, zero prompt. E o allowlist passou a cobrir o interpretador Python por SO (python3no Linux,python/pyno Windows) nos dois namespaces (Bash(...)/PowerShell(...)) — sem isso os gates do pré-flight pediam prompt um a um na máquina do dev. Vale para todos os apps: re-derivam a/xadm-releasevia/xadm-docse alargam o próprio allowlist. Constituição PATCH 0.26.5→0.26.6 (re-carimbo detemplates/xadm-release-skill.md).
0.29.3 - 2026-07-30¶
Adicionado¶
- Seed de teste de endpoint Micronaut:
@BeforeEach/@Sql, nunca no corpo do@Test+ HTTP (loop §6, feedback de app):@MicronautTestdefault (transactional=true) faz rollback do corpo do teste, e o servidor embutido atende em outra conexão → não vê o não-commitado; o defaultSEPARATE_TRANSACTIONScommita o@BeforeEach, por isso seed lá fica visível e seed no corpo some (teste verde medindo vazio). Regra + mecânica em java-micronaut §Testes e fixtures, ponteiro em ci-testes §E2E.transactional=falsenão é a cura reflexa (quebra JDBC cru do@BeforeEach). Ponto cego declarado: nenhum gate estático pega. Páginas dev não rastreadas → sem bump.
0.29.2 - 2026-07-30¶
Corrigido¶
- Placeholder aninhado no
application.yamldo Micronaut NÃO funciona — receita da 0.29.1 corrigida (loop §6, bug de produção): o item 101 documentou o fallback de migração de env inter-app como${NOVO:${ANTIGO:}}"o mesmo doSENTRY_DSN" — mas oSENTRY_DSNmora nologback.xml(motor que aninha, operador:-) e o Micronaut é OUTRO: oDefaultPropertyPlaceholderResolvercasa o primeiro}, não o balanceado, então${A:${B}}resolve pra lixo (token/URL inválido) sem erro de build → quebrou apps em produção. Correção: a receita de migração de env agora prescreve rename atômico ou resolve-in-code (duas props single-level), nunca cascata no yaml; armadilha nomeada em java-micronaut §Armadilhas (com a distinção logback×Micronaut); guarda nova notemplates/ci.yml(grep de${…${só em app Micronaut — Spring aninha certo) pega reincidência no CI do próprio app. Constituição PATCH 0.26.4→0.26.5 (re-carimbo detemplates/ci.yml).
0.29.1 - 2026-07-28¶
Adicionado¶
- Convenção — ENV de auth/URL inter-app nomeada pelo DESTINO (loop §6): um Bearer/base-URL entre
apps mora em
<APP>_URL+<APP>_API_TOKEN("como chamar o<APP>"); cada app valida o próprio<SELF>_API_TOKEN, cada chamador seta<DESTINO>_*. Token tem nome global com mesmo valor no callee (valida) e nos callers (mandam) → mapa de segredo coerente e sem colisão num app que é chamador e chamado (oAPI_BEARER_TOKENhomônimo com valor diferente por servidor foi o bug N-hop da integração Thoms). Um token por callee (trade-off declarado: revogar 1 caller rotaciona B pra todos). Migração de env sem quebrar produção por rename atômico ou resolve-in-code.
Regra no piso (seguranca.md §Segredos), receita de config em java-micronaut
§Específico, ponteiro em integracao.md. Escopo: só HTTP inter-app (+ terceiros PARCEIRO_*); DB/
Firebase/observabilidade seguem como estão. Páginas dev — sem bump.
- Java/Micronaut — 3 refinamentos vindos de feedback de app (loop §6): (a) convenção de rota
/api/<origem>/<recurso> para callback/poke de um app a outro (<origem> = app chamador; integrador
é o caso comum → /api/integrador/produto): /api/** é Bearer (ROLE_API), então o prefixo dá
auth uniforme e contrato de rota previsível (§Específico + ponteiro em integracao.md §Transporte);
(b) view pública se libera na SecurityRule da própria
view (isPublicView(), rota exata), nunca por intercept-url-map greedy — o
ConfigurationInterceptUrlMapRule roda em ordem -100 e ganha da SecurityRule custom, então
um pattern /*//** com ALLOWED vaza rota irmã (ex. /admin) sem erro nem sinal (refina o piso em
seguranca.md §Authn/authz + checklist do revisor; receita em java-micronaut §UI); (c)
read-model de tabela de terceiro marca @Nullable o campo que a fonte pode deixar null — sem
isso o Micronaut Data estoura "non-null constructor argument" e derruba o job inteiro (1 registro
anômalo mata o sweep); trate ausência como anomalia (Sentry/WARN), não falha fatal (§Banco).
Páginas dev, não rastreadas no manifesto → sem bump.
- Constituição §6 — ponteiro para a higiene do .claude/settings.json (0.26.4) — um app
perguntou se a constituição recomenda allow-list de permissões e concluiu "não" por olhar só a
constituição-arquivo; a regra existe, mas vive em engenharia/ferramentas.md + app-novo.md
(versionar o settings.json com padrões LARGOS dos comandos comuns — Bash(git *),
Bash(mkdocs *), Bash(python3 scripts/*)… —, pessoal em settings.local.json gitignored;
grant hiper-específico solta a tag da release via --amend). §6 ganhou um bullet-ponteiro (com a
âncora da regra) para fechar a fresta de descoberta de quem só lê a constituição. Bump text-only.
0.29.0 - 2026-07-28¶
Adicionado¶
- Gradle assado na imagem-fábrica
ci-java(constituição 0.26.2) — feedback §6 (bi-transporte-xls v2.1.3): em cache frio o./gradlewbaixava a dist do Gradle de host externo e umUnknownHostExceptiontransiente pintava o gate de vermelho aleatório — a exceção acidental ao "zero download por run" (decisão 0003). Agora aci-javapré-aquece a dist do wrapper (versão escalargradlenamatriz-baseline.json, casa-inteira) sobGRADLE_USER_HOME=/opt/gradle, no caminho-hash canônico; oci.ymlpassa a cachear só deps. Egress do runner medido aberto (Maven Central, plugins, dist alcançam) → sem 2º furo. Adendo na decisão 0003: pareamento comgradle-wrapper.properties(versão E variante-bin), premissadistributionBase, ordem de bump anti-deadlock. Validado no runner (cold-cache não baixa; o smoke da fábrica assere a dist assada em/opt/gradle/wrapper/dists/); os 8 apps Java já pinam a URL canônica. - Manifesto EOL-agnóstico + guarda pré-commit da
/xadm-release(constituição 0.26.3) — ogera-manifestohasheava bytes crus (read_bytes), então gerá-lo no Windows (autocrlf/CRLF) divergia do Linux (LF) e deixou um drift subir silencioso no 0.26.1 (a entrada doxadm-release-skill.mdnão batia nenhum commit; o CI não-gating do item 62 só avisa). Agorahash_conteudonormaliza\r\n→\nantes de hashear (rastreia conteúdo, não bytes; binário preservado), comtesta-gera-manifesto.pynovo no CI. E o §8 da/xadm-releaserodagera-manifesto --checkcomo último passo pré-commit, fechando a race carimba→edita→commita. - Norma anti-deadlock no batch upsert (java-micronaut §Banco) — feedback §6 de app
(Onpetro bi-comercial-xls v1.4.5, deadlock
40P01real em produção): o diff-aware (ON CONFLICT DO UPDATE … WHERE … IS DISTINCT FROM) tira row lock na arbitragem do conflito, antes de avaliar oWHERE— linha "inalterada" trava igual, e duas transações concorrentes com dimensões sobrepostas em ordens diferentes deadlockam. Bullet novo: todo batch upsert ordena as linhas pela chave natural antes de executar (executeBatch→sortedcomnullsFirst;COPY + TEMP TABLE→ORDER BYnoINSERT … SELECT), com a nuance multi-tabela e o teste proporcional (capturador de batch afirmando a ordem). Segundo feedback (bi-transporte-xls v2.1.3, mesmo risco corrigido preventivamente) completou o bullet: transação de mescla multi-fase (upsert +DELETEseletivo) serializa a fase de banco compg_advisory_xact_lock(<chave fixa do app>)— oDELETEtrava em ordem de scan, queORDER BYnão controla — e a declaração de que o deadlock em si não é testável deterministicamente: testa-se a pré-condição estrutural. Página não rastreada — sem obrigação nova. - Constituição 0.26.0→0.26.1 — allowlist Windows na
/xadm-releaseegitattributes-java(feedback §6 de app: release rodado em Windows nativo). (a) O bloco "Permissões — release sem prompt" do templatexadm-release-skill.mdsó tinha regrasBash(...); no Windows o harness usa a ferramenta PowerShell, cujo namespace de regra é próprio (PowerShell(...)) — nenhuma regra Bash casa, e todos os gates promptavam na mão. O bloco Bash também não cobria os gates (só git + echo) — os gates promptavam no Linux idem. Agora os dois blocos são simétricos: git + gates de código/container/docs por stack ("mantenha só as da sua stack";curlescopado emdocs.xadm.biz). Cortados da proposta original osGet-Content/Select-String/etc. — o template já manda inspecionar arquivo pela ferramentaRead, nunca shell. Mais dois venenos Windows na lista: prefixo de env inline ($env:X='...'; cmdé composto — cura estrutural:"env"nosettings.local.json) e sufixo; echo "EXIT=$LASTEXITCODE"(idem; o harness já reporta exit code). Segundo feedback (bi-transporte-xls, ~15 prompts num release Windows) confirmou o diagnóstico e somou a regrapython -m mkdocs build *(Windows raramente temmkdocsno PATH), o parcurl … http://localhost:8000/*pro preview local (o outro gerador recorrente de prompt) e a nota "URL antes do-o" no download de validadores; a receitagit archive … | docker build -proposta lá foi rejeitada com o porquê no template (pipe é veneno de prompt, PowerShell 5.1 corrompe stream binário no pipe, e buildaria o HEAD em vez da árvore em curso) — a cura canônica do CRLF é o.gitattributes. (b) Template novo rastreado e opcionaltemplates/gitattributes-java(gradlew text eol=lf): checkout Windows comautocrlfdeixa ogradlewcom CRLF, odocker buildCOPYa a árvore como está e o/bin/shdo Alpine morre com./gradlew: not found— o--chmod=0755do Dockerfile cura o bit de execução, não o fim de linha. Referenciado em constituição §3, templates.md (tabela + seção Container), app-novo.md §7, java-micronaut §Deploy, gate de container do release e mapeado pela/xadm-docs(gitattributes-java→.gitattributes). Bump PATCH; 4 rastreados re-carimbados (xadm-release-skill.md,xadm-docs-skill.md,dockerfile-java,gitattributes-java).
0.28.0 - 2026-07-23¶
Adicionado¶
-
Constituição 0.25.1→0.26.0 — plataforma de integração ganha topologia e fluxos em SVG autoral, o livro ganha capa obrigatória, e a taxonomia um 5º papel: app compartilhado. Pedido interno: a diretoria precisa ver a plataforma em uma tela (X-Adm no cliente, nuvem, terceiros). A
documentacao/integracao.mdera só prosa — sem figura, o chefe tinha que ler taxonomia + tipos + fronteiras para montar a topologia na cabeça. Adicionado no topo da página: §Topologia — depois de rodadas contra o layout automático (Dagre inverte ranks por ciclo/aresta;block-betadá grid mas sem rowspan nem título de composite), a figura final é SVG autoral (docs/assets/topologia-integracao.svg— base visual desenhada pelo Gustavo, paleta re-tokenizada para X-Adm royal#064490+ tipografia Inter, decisão 2026-07-22), embutida via--8<--, com Mermaid-base colapsado logo abaixo (??? info—pymdownx.detailshabilitado nomkdocs.yml) como fonte semântica lintada: mudou a topologia, atualiza o base primeiro e espelha no SVG. Norma nova: bullet "Figura autoral (SVG) — a exceção de capa" na constituição §5 + receita completa em templates.md §"Figura autoral em SVG" (as armadilhas reais: linha em branco ou<svg>sem wrapper<div>fatiam o SVG em<p>— python-markdown não trata<svg>como bloco;<style>sem prefixo vaza pro site;<img>não herda a webfont, daí--8<--), com o ponto cego declarado: nenhum gate valida o SVG em si — quem é lintado é o Mermaid-base. CSS auxiliar emdocs/stylesheets/xadm.css:.md-typeset .mermaidcomoverflow-x: auto(vale para os demais diagramas Mermaid do site). Rótulos da figura (por pedido interno): Hub = "Plataforma de Integração" +int.<cliente>.xadm.biz+ps.<cliente>.xadm.biz; banco = "PostgreSQL Cliente ¹" com nota de rodapé "Um banco de dados por cliente"; apps específicos = "Apps Específicas" (agrupapiedeexcel); app compartilhado = "Apps Compartilhados". Papel novo — App compartilhado: 1 instância multi-tenant que nasce quando o parceiro é global (mesmo endpoint para N clientes, cada um com sua credencial); não escreve no barramento (mantém §Fronteiras "um dono por tabela" intacta), conversa com o hub, que grava. Combina tipos ③+④ quando o parceiro é bidirecional. Motivo concreto:sascar.xadm.biz— o hub envia MDF-es abertos (chave/placa) ao app compartilhado, que faz polling na Sascar do cliente com credenciais próprias; quando o motorista chega ao destino, devolveencerradoao hub, que atualiza o status; o cliente Java lê via PowerSync e importa no ERP. Reator removido da taxonomia — reservado para uso futuro. O único caso concreto (thoms → e-commerce) foi confirmado como REST puro ao ler a UI do app rodando (POST /sync/erpemwebstorm-ecom.thoms.xadm.biz, sem PowerSync no servidor). A definição "não recebe de fora, consome o banco via PowerSync, empurra para fora" contradizia o caso real; thoms virou app específico (ecommerce.<cliente>) que recebe do hub via REST e fazPOSTno parceiro (WebStorm). O papel Reator fica reservado para casos futuros de notificação (email, push, alertas Telegram) — quando aparecer, volta à taxonomia. Aplicado em: figura (zonas Servidor X-Adm Cliente com ERP X-Adm ↔ Integrador Cliente — as saídas partem do Integrador, label "push tempo real"/"write-back" — / Terceiros / Nuvem 2x2 / Usuários com mockups de navegador e celular; Sascar → Apps Compartilhados viapull · MDF-e, odômetroe Plataforma ↔ Compartilhados viaREST · baixa MDF-e, odômetro; PowerSync PG → Usuários e PG → Integrador; write-back dos Usuários por fora; sem badges numerados — os 4 tipos são numerados no texto, não na figura); §Taxonomia (linha Reator removida, App consumidor menciona Web/Mobile); §Os 4 tipos (item 4 reescrito; admonition "Reator — reservado para uso futuro"); §Fronteiras (regra de tabela de status separada agora fala em app específico); §Transporte (PowerSync serve consumidor + cliente Java; hub↔apps específicos é REST); tabela de prefixos (<app>_citaxls_*, thoms_envio); constituição §8 (Taxonomia sem reator; PowerSync bidirecional distingue consumidor de app específico REST; fronteira §7 sem reator; contrato sem "packaging de reator"); glossário (seção Reator removida);engenharia/powersync.md(2 lugares). Rejeitado replicarsascar.<cliente>(N deploys idênticos batendo na mesma API, isolamento sem benefício), rota no hub (reintroduz o erro (a) da §integracao.md), e app compartilhado escrevendo no barramento (dois donos à tabela-espelho — quebra §Fronteiras). Terminologia — rename da taxonomia: "vertical" → app específico; "adaptador compartilhado" → app compartilhado (feedback interno: a taxonomia da plataforma agora fala em apps por papel, não em jargões técnicos). Aplicado em integracao.md (§Taxonomia + §Os 4 tipos + §Fronteiras + §Transporte), constituição §8, glossário (2 seções + Owner-writes), decisão 0018 (título + corpo; slug preservado0018-adaptador-compartilhado.mdpara não quebrar links; nota de terminologia dentro do arquivo) e nav do mkdocs. Também trocado "shape" por "tipo de fluxo" na página e no §8 — "shape" era jargão a mais para a equipe. "Shape" segue em outros contextos da constituição (formato de resposta HTTP, shape de fixture), onde não colide. Trocado "pola a API" por "faz polling na API" — anglicismo técnico consagrado, "pola" era gíria. Removidobi-commonsda doc normativa (integracao.md, constituição §8, glossário, powersync.md): a biblioteca não foi implementada ainda, e a doc presumindo sua existência viola §1.6 (não inventar). Decisões 0007 e 0014 preservam a menção como registro histórico da intenção — o dia que a biblioteca existir, volta à norma. Adicionado: decisão 0018 + linha na tabela §Taxonomia + nota "combinação ③+④" em §Os 4 tipos; §8 da constituição incluiapp compartilhadona taxonomia (anti-drift §8). Detalhe operacional (auth por cliente, schema do banco de trabalho, contrato exato com o hub) fica na arquitetura-alvo do integrador (§1.9: concreto no repo dono). §Fluxos novo na integracao.md: 3 SVGs autorais verticais (um por fluxo, cima→baixo:fluxo-saida.svg,fluxo-entrada.svg,fluxo-ciclo-mdfe.svg, mesma UI da topologia; escolhidos contra a variante combinada horizontal, que perdia legibilidade no downscale da coluna — a vertical cabe inteira, fonte no px desenhado) — saída X-Adm→Usuários (rotas tempo real e batch XLSX), entrada Externo→X-Adm (PIED, com devolução de status) e o ciclo completo do MDF-e (Sascar) — cada um com a faixa COMO ACOMPANHAR (exceções de software vão automaticamente ao GlitchTip com notificação à equipe; exceções de operação geram e-mail à equipe do cliente). Capa obrigatória no livro (cap. 0): a constituição §2 passa a exigir que a Documentação Completa abra com figura de topologia + fluxos principais com "como acompanhar" (Mermaid por padrão, SVG autoral permitido via §5);templates/projeto-index.mdganha a seção "0. Capa" com o marcador HUMANO — projeto novo nasce com capa, app existente adota por migração oportunista (§1.6). Rename editorial: "hub" → "Plataforma de Integração" em toda a doc normativa (integracao.md com desambiguação plataforma-conjunto × Plataforma-serviço, constituição §1.9 e §8, glossário, powersync.md, decisão 0018 — decisões 0007/0014/0016 ficam, são histórico) e "empurra" → "envia" (registro). Bump MINOR — a capa é obrigação nova sobre o livro (o resto é permissão + esclarecimento + rename); 0 rastreados mudaram (projeto-index.mdé scaffold de uma vez, não rastreado — quem o cobra é a constituição §2, não o manifesto). -
engenharia/java-micronaut.md§Banco — bullet novo: chave-de-mudança single-writer via@DateUpdated. Feedback de app (§6). A doc já tinha o par multi-writer (trigger Postgres, entidade não mapeia — evita null-wipe) mas nada para o caso simples: app dono único da tabela onde@DateUpdateddo Micronaut Data resolve de graça (carimba a cada write, rejeita valor externo, serveORDER BY updated_ata consumidor externo). Bullet novo entre§Espelho fiele§multi-writerfecha a matriz "dono da escrita → onde vive o bump" (single → ORM mapeia; multi → trigger, entidade não mapeia). Sem bump — página não rastreada; mudança de conselho, não de contrato. -
/xadm-releasepré-flight — aviso soft do bloco 'Publicar Release' comentado. Feedback de app (§6). Otemplates/release.ymltraz o bloco de asset e opermissions: contents: writecomentados por default (OPT-IN "só app que distribui artefato"). App que copiava o template e distribuía artefato mas esquecia de descomentar →valida-release.pyverde, tag pushada, aba Releases vazia, jar sem asset — descoberto só quando o operador ia baixar. Irmão silencioso do item 95 (descomentar sem trocarcontainer:é barulhento — guarda + 403 — ; NÃO descomentar quando deveria era o cego). Adicionado bullet no pré-flight dotemplates/xadm-release-skill.md: se.forgejo/workflows/release.ymlexistir, lê pela ferramentaReade procura DOIS marcadores literais (# - name: Publicar Release no Forgejoe# contents: write); se algum aparecer comentado, avisa no relatório final (não bloqueia — app só-deploya/doc-only ignora legitimamente). Semrelease.yml→ skip. Rejeitada a alternativa "cobrar campodistribui_artefatonoapp.json" (bandeira nova a manter, sem gate para cobrar); o próprio release.yml carrega os marcadores do template quando foi copiado → detecção sem inferência, sem falso positivo.xadm-release-skill.mdre-carimbado; cópia do central (.claude/skills/xadm-release/SKILL.md) inalterada — o central não distribui artefato (site MkDocs, deploy por push).
0.27.2 - 2026-07-20¶
Alterado¶
- Constituição 0.25.0→0.25.1 — o
checa-rotas.pyconfere rota, não forma: a fronteira agora é princípio, não nota de rodapé. Feedback de app: publicar um arquivo-fato de contrato ao lado de um OpenAPI cujo schema mentia (required/enum/nullability≠ runtime) passou os dois gates — quem pegou foi o teste de fio, não a toolchain. O ponto cego já estava declarado ("o check não sabe nada sobre corpo — só rota e método"), mas como nota mecânica. Avaliada e recusada a extensão a um check de schema-drift prosa×spec: o spec gerado é oráculo confiável de rota (path/verbo saem fielmente das annotations) mas frágil de forma (não vê validação imperativa, default, serialização custom), então um gate prosa×spec passaria verde com ambos mentindo juntos (o teste tautológico do item 88 por outro caminho) ou acusaria a prosa correta quando ela acerta o runtime e o spec erra — falso conforto. O oráculo independente de forma é o runtime = teste de fio, que já é piso §6 (E2E na fronteira de contrato). Reforço em três lugares: §Contrato REST (o princípio "rota é checável contra o spec, forma não é"), a docstring docheca-rotas.py, e o padrão do arquivo-fato empublicar-docs.md(o autor que escreve schema em prosa "a partir do código" atesta a forma por fio, não por gate). Sem mudança de comportamento — esclarecimento do escopo já existente; ocheca-rotas.py(rastreado) foi re-carimbado.
Adicionado¶
-
Constituição 0.24.1→0.25.0 — o kit de UI ganha
app.css: CSS de componentes do app, sempre linkado. Ohead(title)dolayout.htmlpassa a linkar/css/app.cssdepois docustom-theme.css, e a/xadm-docsscaffolda o arquivo detemplates/app.cssna criação do app. Dois CSS, dois donos (§1.9): ocustom-theme.cssé a identidade da casa — rastreado, verbatim, não se edita por app; oapp.cssé dos componentes do app (timeline, tiles, stepper, badges de domínio), não é rastreado e o central nunca o re-deriva. A 0015 fechou o slot de<head>com razão (dois<head>quebram toda view) mas deixou o app sem lugar para o CSS dele, e a saída prescrita — "escreva o<head>à mão nessa tela" — não escala para 16 telas. Não era hipótese: dois apps já contornavam a lacuna, de formas diferentes — oint-piedemitindo o<link>no<body>via fragment local, e obi-comercial-xlsforkando olayout.html. O fork é o dado que decide: o layout forkado ainda carregava um fragment morto que o central removera há duas versões — fork sai do radar do/xadm-docse para de receber correção. Os dois apps escolheram o mesmo path (/css/app.css) independentemente: o kit passa a prescrever o que já era fato. Detalhe em 0017 e na receita Java/Micronaut §UI.Nenhum app quebra: as views seguem chamando
head('Título')com um parâmetro, e não há segundo<head>— a 0015 continua valendo, slot de markup arbitrário no<head>segue não existindo. Migração: app que usa o kit e re-derivar olayout.htmlprecisa terpublic/css/app.css, nem que vazio (sem o arquivo, 404 em toda página). Os dois contornos atuais continuam funcionando — ninguém precisa correr.
Corrigido¶
-
Constituição 0.24.1→0.25.0 —
templates/mkdocs.ymldeclaravaexclude_docs:duas vezes: oprivado/sumia em silêncio. Bug introduzido na 0.24.0 (arquivo-fato): o segundoexclude_docs:, 48 linhas abaixo do primeiro, sobrescrevia o bloco de cima — e aquele bloco excluíaprivado/, o segmento reservado a confidencial de cliente (§5). Todo app que re-derivasse o template verbatim passaria a publicardocs/privado/no site. YAML não avisa sobre chave duplicada, e o--strictfica verde por estar certo: para o MkDocs a chave apagada nunca existiu. Repro:python3 -c "import yaml; print(yaml.safe_load(open('mkdocs.yml'))['exclude_docs'])". Cura: os dois blocos fundidos num só, com o porquê no topo. -
O
checa-nav.pypassa a reprovar chave YAML repetida nomkdocs.yml— a classe, não o sintoma: doisnav:, doisplugins:, doistheme:falham igual. Parser de indentação stdlib (o check roda no gate de código do app, onde não há PyYAML — eyaml.safe_loadnão serviria: ele é quem come a duplicata em silêncio; para vê-la é preciso olhar o texto). Verificado nos dois sentidos: reprova otemplates/mkdocs.ymlreal de antes da correção (exclude_docsna linha 91, já declarada na 43) e passa nos 7mkdocs.ymlreais da casa, sem falso positivo. A guarda deprivado/dodocs.ymlcontinua sendo a rede de baixo, mas só pega o sintoma daquela vez, depois do build e só nos apps que a têm. Detalhe em CI, gates e testes. -
Constituição 0.24.0→0.24.1 —
docs.ymlerelease.yml: ocontainer:e o bloco Java agora se amarram. Ocontainer:(topo) e o bloco de geração de API (~120 linhas abaixo, comentado) eram acoplados em silêncio: descomentar o bloco sem trocar ocontainer:deci-baseparaci-java:<v>derruba o workflow no push comJAVA_HOME is not set. O defeito é invisível a todos os gates — oci.ymljá vem comci-javae passa verde, e o pré-flight da/xadm-releasebuilda na máquina do dev, onde há JDK; só o push descobre, e norelease.ymla tag já está no ar. Pior: a mensagem nativa do gradlew manda setarJAVA_HOME, que é o antipadrão da casa (decisão 0003 — a toolchain vem da imagem, semsetup-java). Agora o comentário do bloco cobra a troca docontainer:e uma guarda (command -v java) transforma o erro numa instrução que aponta ocontainer:. Orelease.ymllevou a mesma guarda: ele já tinha o comentário e o mesmo defeito.
0.27.1 - 2026-07-17¶
Corrigido¶
- Constituição 0.24.0→0.24.1 — o Dockerfile canônico manda o app rodar como
USER appnum WORKDIR que é deroot. OWORKDIR /appcria a pasta como root (o--chowndoCOPYé do jar, não dela); depois doUSER app(uid 1001) qualquer escrita sob/app— log em caminho relativo, cache, upload, diretório de trabalho — falha comAccessDeniedExceptionem runtime, na produção: o build passa, o teste passa e odocker builddo pré-flight também. Reproduzido com Docker de verdade (mkdir /app/logsetouch /app/x→Permission denied). Nobi-comercial-processador-xlsisso derrubou todo o processamento (logback emlogs/app.log,createDirectories(/app/logs)falhando antes de qualquer trabalho). Agora otemplates/dockerfile-javatraz, no ponto exato antes doUSER app, a regra + a receita comentada (RUN mkdir -p /app/logs && chown app:app /app/logs), com o aviso de não usarchown app:app /appinteiro (o app sobrescreveria o próprioapp.jar— metade do motivo de rodar não-root) e de conferir o arquétipo antes (servidor loga em stdout, quem loga em arquivo é CLI/worker —logs/num servidor costuma serlogback.xmldo arquétipo errado). Ojava-micronaut.md§Deploy ganha o bullet-dono; ocoolify.mdtroca o exemplo/app/logsdeStorages(arquétipo errado) e documenta a herança de dono: volume nomeado herda o dono do diretório da imagem — se o diretório não existir, nasceroote quebra igual —, bind mount não herda (o dono no host é que precisa ser1001:1001).
0.27.0 - 2026-07-16¶
Adicionado¶
-
Constituição 0.23.3→0.24.0 — arquivo-fato ganha hospedeira obrigatória, e sai do build com
exclude_docs:. A 0.23.0 mandou o dono publicar o fato, mas a receita dizia que ele "vive emdocs/public/como qualquer página" e levanot_in_nav:— duas metades que não fecham: sem uma página que o embuta, o fato só existe noraw/, invisível no site do dono. Enot_in_nav:não esconde, só cala o aviso de órfã: o fragmento continua sendo buildado, ganha um H1 inventado do nome do arquivo ("Xadm ingest"), fica alcançável por URL e entra na busca — procurar "Envelope" devolve a hospedeira e a órfã, com o mesmo texto. Agora: a hospedeira (public/contratos/index.md, nonav:, com frontmatter) é obrigação do dono, e os fatos saem do build comexclude_docs:+!reincluindo a hospedeira. O--8<--e orclone/cpdoraw/seguem funcionando — leem do disco, não do build. Vale nos dois lados:_importado/também éexclude_docs:, senão o site do consumidor serve o contrato do dono como se fosse página dele — a cópia que o mecanismo existe para matar.not_in_nav:fica para o que deve sair no site sem estar no índice (o Javadoc emdev/api/). Decisão 0016.Migração — quem já publica fato troca
not_in_nav:porexclude_docs:Um repo publica arquivo-fato hoje (o hub de integração) e já tinha escrito a hospedeira por conta própria. Trocar o
not_in_nav:peloexclude_docs:dotemplates/mkdocs.ymltira a órfã do ar. Sem a troca nada quebra: o site só segue servindo a página órfã e duplicando o texto na busca. -
Página em
docs/public/leva frontmatter — inclusive a da API. Havia duas convenções: otemplates/public-api.mdsem frontmatter, a hospedeira do hub com. Governança por documento (§6) não abre exceção por a página ser pública, e para quem consome um contrato o cabeçalho é o que importa:atualizado:diz se o contrato está vivo,status:se dá para confiar,responsavel:a quem perguntar. O hook expõe o nome, nunca o e-mail. -
Constituição 0.23.1→0.23.2 —
checa-admonition.py: admonition cru vira gate. Omkdocs build --strictnão pega marcador mal indentado: para ele não há erro, só um parágrafo que começa com!!!— renderiza literal e sai verde, e quem paga é o leitor. Mesma classe do Mermaid que não parseia (a razão dolint-mermaid.mjs), e foi achado no olho de um humano, que é o gate que a §6 diz não servir. O check roda depois do build e confere o HTML, não o Markdown: ali não há ambiguidade —!!!em<pre>é código exibido (esta plataforma mostra orunbook.md, que tem um!!! warningde exemplo), em<p>é bug. Nobuild-site.yml, nodocs.ymldos apps e no pré-flight da/xadm-release.
Corrigido¶
-
checa-nav.py— a negação!deexclude_docs:era ignorada, e os globs vinham sem ordem. Dois bugs que só apareceram ao prescrever a hospedeira, e que a teriam transformado numa órfã silenciosa: o veredito parava no primeiro glob que casava (então/public/contratos/*.mddava a hospedeira como excluída e ela podia sumir donav:sem ninguém acusar), e os globs eram acumulados numset— sem ordem, num formato em que a ordem decide (última que casa vence, como no.gitignore). Um terceiro: o regex que varre omkdocs.ymlcapturava os próprios globs de exclusão como "menção de nav", dando passe livre a fato que nenhum exclude cobria. Três casos novos de regressão; mutação confirmada (o_excluidoantigo deixa caso vermelho). -
testa-checa-views.py— a fixture agora é o kit real, com guarda de mutação. OLAYOUT_OKera cópia à mão do layout e já tinha divergido dele uma vez (ficou com o layout quebrado da 0.21.0 e a suíte seguiu verde durante todo o bug). Agora lêtemplates/views/layout.html. A guarda existe porque ler o real só é melhor se a mutação for garantida:muta()exige que o alvo apareça exatamente uma vez — pega tanto o no-op (o kit mudou, o alvo sumiu) quanto o alvo ambíguo. Não é hipótese: ao trocar a fixture, os dois casos da regra G passaram a injetar o<head>defeituoso dentro do comentário de cabeçalho do kit (onde o parser não olha) e ficaram verdes testando nada — a guarda os pegou. -
Constituição 0.23.2→0.23.3 — o kit de UI da 0.21.0 estava quebrado no ar: slot de head removido. O fragment
headComExtras(title, extras)era um segundo elemento<head>nolayout.html, e um documento HTML tem um só: o parser funde o segundo no primeiro, e ohead('Título')passou a arrastar o${extras}do irmão —Error resolving fragment: "${extras}", 500 em toda view, inclusive nas que não usavam o slot. A afirmação da 0.21.0 de que "as telas que já chamamhead('Título')seguem intactas" era falsa. Reproduzido com Thymeleaf 3.1.5 antes de decidir: não é colisão de nome (renomear não cura) nem "head dentro de body" (dois<head>irmãos quebram igual) — basta serem dois. O slot foi removido em vez de consertado: 0 views o usavam, 126 chamavamhead(). Tela que precise de markup próprio no<head>escreve o<head>à mão e traz o caso ao central. Decisão 0015.Migração — quem copiou o kit 0.21.0/0.22.x re-deriva com
/xadm-docsO
layout.htmlvolta a ter sóhead(title). Nenhum app usava o slot, então não há chamada a corrigir: quem estava com o kit quebrado volta a renderizar; quem nunca adotou não faz nada. -
checa-views.py— regra G:<html>/<head>/<body>duplicado no mesmo arquivo reprova. É a guarda contra a reincidência do bug acima. Vale mesmo com nomes distintos e mesmo fora do<body>— o que quebra é o parser fundir elemento, não o nome (a regra A já cobria nome). Só declaração real conta: o cabeçalho do kit documenta o uso com um<head th:replace=...>dentro de comentário, e os três apps Micronaut reais têm isso — a regra lê o parser, nunca regex. Verificada nos dois sentidos: reprova olayout.htmlreal da 0.21.0 e passa nos 46 views reais dos três apps. -
testa-checa-views.py— a fixtureLAYOUT_OKera o layout quebrado. O "layout conformante mínimo" da suíte trazia o segundo<head>: o teste do checker atestava contra um defeito, que é a fixture inventada que a §6 nomeia — e o motivo de a suíte não ter pego nada. Corrigida, mais quatro casos da regra G (o bug real, os dois<head>irmãos,<body>duplicado e o negativo do<head>em comentário). -
checa-rotas.py— a mensagem de erro dizia onde pôr o escape de forma ambígua. Dizia "<!-- checa-rotas: ignorar -->na linha"; um dev de app leu como "na linha de cima" e perdeu um ciclo de gate. Agora diz na MESMA linha da rota, e que o marcador não aparece na página (é comentário HTML). Só a string mudou — o escape sempre foi por linha, e segue. -
Nota de migração da 0.23.1 renderizava crua na
/changelog/— o!!! noteestava indentado com 2 espaços dentro do bullet e saiu como texto literal na página publicada. Python-Markdown usatab_length = 4: parágrafo de continuação de item de lista aceita 2 espaços (lazy continuation), mas bloco novo — que é o que o admonition é — exige 4; com 2, ele cai fora da lista e vira parágrafo. O texto ao redor sai perfeito, então o erro não chama atenção no diff.
0.26.0 - 2026-07-16¶
Adicionado¶
- Constituição 0.21.0→0.22.0 — o contrato REST passa a ser checado contra o OpenAPI
gerado:
checa-rotas.py(loop §6, feedback de app). A §6 mandava a doc mudar no mesmo PR que muda o contrato, e nada detectava quando isso não acontecia — a própria §6 admitia não ter "detector automático de doc seguiu código". Num app, uma rota de upload foi renomeada no controller; o OpenAPI, que omicronaut-openapideriva dos@Controllerao compilar, acompanhou sozinho; a prosa não. Por 10 dias omkdocs build --strict, ocheca-nave oci.ymlficaram todos verdes enquanto o site publicado servia o contrato novo e a prosa com a rota morta, lado a lado. Ocheca-rotas.py(rastreado, stdlib, baixado fresco, tolerante) confere prosa → spec e roda noci.yml(depois do./gradlew check, que é quando o spec existe — o rename é evento de código) e nodocs.yml(prosa editada sem tocar código, inclusive pela web do Forgejo). Só cobra rota cujo primeiro segmento existe no spec, para não reprovar API de parceiro que o app só consome nem rota de app vizinho; aceita prefixo de família em diagrama; pula documentostatus: obsoleto; e tem escape de linha<!-- checa-rotas: ignorar -->para a decisão de remoção, que não tem como nomear a rota removida sem escrevê-la. Aplicado aos dois apps que geram OpenAPI, achou o drift do feedback e um segundo bug inédito (doc cuja seção diz "Envie um PUT" e cujo exemplo dizPOST, numa rota que só expõePUT/DELETE). Decisão 0012; detalhe em CI, gates e testes.
Corrigido¶
-
Constituição 0.23.0→0.23.1 — o kit de UI mandava o CSS para um diretório que não o serve (loop §6, feedback de app). A
/xadm-docsmapeavatemplates/views/**→src/main/resources/views/**, mastemplates/views/tinha dois arquivos com destinos diferentes: olayout.htmlé view, ocustom-theme.cssnão — quem serveviews/é o Thymeleaf, e o CSS precisa dostatic-resources(public/css/), como o próprio arquivo dizia no cabeçalho e o@{/css/custom-theme.css}do layout referenciava. Seguir o glob ao pé da letra sobe a tela sem estilo. Bug latente, nunca exercitado: os dois apps Micronaut reais têm o CSS no lugar certo porque nasceram antes do kit — o primeiro app novo a seguir a skill é que quebraria. A cura foi a origem, não o mapeamento: ocustom-theme.cssfoi paratemplates/public/css/, e a árvore do kit passa a espelhar a do app — os dois globs viram verdade (views/**→views/**,public/**→public/**), sem exceção por arquivo a manter em sincronia. Listar arquivo-por-arquivo na skill (o que o feedback pedia) duplicaria a tabela que já é dona nojava-micronaut.md— recriando o segundo dono que causou o bug. Guarda nova: ocheca-views.pyganha a regra F (arquivo não-.htmlemviews/reprova, apontandopublic/), que mata a classe e não só este arquivo.Nota de migração — só o path de ORIGEM muda
O destino no app não muda: quem já tem
public/css/custom-theme.cssestá correto e nada precisa fazer. Mudou o caminho no manifesto (templates/views/custom-theme.css→templates/public/css/custom-theme.css), então a/xadm-docsre-baixa do raw novo. App que tenha o CSS emviews/(nenhum conhecido): mova parapublic/css/— a tela está sem estilo.
Alterado¶
-
Constituição 0.22.1→0.23.0 — fato de outro repo: o dono publica em
raw/, o consumidor importa no build (loop §6, feedback de app). A 0.22.1 abriu transclusão para fato do mesmo repo e manteve "cross-repo é link, sempre". O caso que sobrou é o runbook de implantação de um vertical, que vive entre dois apps: o que ele implanta é seu, o contrato de ingestão que ele consome é do hub. O diagnóstico mudou a pergunta — o hub não publica o OpenAPI que ele já gera (step comentado), não temdocs/public/, e seu contrato vive espalhado em dez arquivos de prosa. Não havia o que linkar: a cópia era sintoma, o dono que não publica é a causa, e a regra parava no consumidor. Agora: o--8<--não atravessa repo, mas o build atravessa — o dono publica o.mdcru na raiz do seu prefixo (docs-sites/<slug>/raw/, aditivo, todo push, pelo mesmo critério que já põe oapp.jsonlá: é contrato entre apps, não conteúdo versionado), e o consumidor fazcurl -fparadocs/_importado/(efêmero:.gitignore+not_in_nav:) e embute. O fato chega fresco a cada build;curl -ffalha o build se o dono despublicar. Publicar é obrigação do dono, e quem rege a evolução de um repo não vira dono dos fatos dele — o projeto-mãe escreve o PR no dono e importa aqui, senão um hub compartilhado ganha um dono por consumidor. Decisão 0014; receita em Publicar docs. -
Constituição 0.22.0→0.22.1 — o runbook transclui do arquivo-dono; re-digitar é que é proibido (loop §6, feedback de app). A 0.20.2 fechou "o livro não entra no runbook" e mandou o runbook linkar; um dev aplicou a regra e reverteu, porque a versão conforme ficou pior para o uso real — mostrar a integração à equipe numa página. O ponto procede por um motivo mais forte que o custo ao leitor: a regra contradizia o princípio que deveria implementar (o §1.9 já dizia "linkam ou embutem (
pymdownx.snippets), não copiam"). Agora: onde o operador precisa do fato na mesma página, o runbook transclui do arquivo-dono — não há cópia para envelhecer. Transclui-se do arquivo-fato, nunca "da seção do livro" (o livro é narrativa e ele próprio embute os fatos); cross-repo continua link, que é onde o exemplo-que-mente nasceu. O §2 passa também a nomear o que fica no runbook (topologia de operação com diagrama, superfície, variáveis, passos/verificação/ reversão): ele só enumerava proibições, e a migração revertida cortou junto o Mermaid de topologia que o próprio molde prescreve — regra que só diz o que sai é aplicada cortando demais. Registrada a armadilha do--8<--(é pré-processador de linha: transclui dentro de bloco de código e de comentário HTML; escape é;) em Templates. Runbook que linka continua conforme — transcluir é permissão, não obrigação. Decisão 0013. -
Piso §6 — "o teste não reimplementa a lógica que atesta" (loop §6, feedback de app). A definição de pronto cobrava "teste proporcional ao risco" e já nomeava a fixture inventada como falso atestado, mas não o irmão dela: o teste que refaz no próprio corpo a conta da produção passa por construção — o
expecteddeixou de ser oráculo e virou um segundo palpite da mesma cabeça que escreveu o código. Caso real: um teste de agregação somava as linhas noteste comparava com a soma do serviço, verde sobre uma agregação errada. O piso agora exige oráculo de fora (valor literal, saída capturada, invariante independente), e o ci-testes registra o teste de mutação (PIT) como ferramenta de diagnóstico sob demanda — não gate, pela mesma razão de "cobertura é visível, não bloqueante". -
Constituição 0.20.2→0.21.0 — o kit de UI ganha o gate que faltava:
checa-views.py(loop §6, feedback de app). O kit de UI foi o primeiro template rastreado executável (os demais são Markdown, ou Python com teste de regressão), e para ele o central não tinha guarda nenhuma: o manifesto atesta que a cópia do app é idêntica à do central (sha256), nunca que ela funciona. Foi exatamente essa fresta que deixou a 0.19.0 publicar umlayout.htmlcom doisth:fragment="navbar"homônimos — sha íntegro,--checkverde, 500 em toda view de quem adotasse. O novo check é stdlib e offline (sem JVM): reprova fragment homônimo, chamada a fragment que o layout não declara, aridade divergente, chamada posicional contra fragment sem signature (o defeito da 0.19.2) e markup passado declarado fora do host doth:replace. Roda noci.ymlde todo app com view (tolerante: semsrc/main/resources/views/, sai 0) e no CI do central contratemplates/views/— na origem, antes de publicar, que é onde o defeito da 0.19.0 teria morrido. Verificado contra olayout.htmlreal da 0.19.0 (reprova) e contra as 38 views reais do integrador e do int-produtos-ecom (verde, sem falso positivo). Acompanhatesta-checa-views.py(12 casos, central-only). headComExtras(title, extras)no kit de UI — a tela que precisa de markup próprio dentro do<head>(típico<noscript><meta http-equiv="refresh">de fallback sem JS, inválido e ignorado fora do<head>) não tinha lugar canônico e empurrava o conteúdo para o<body>. Irmão nomeado dohead(title), como onavbarComMenué donavbar: sem overload por aridade, e as telas que já chamamhead('Título')seguem intactas.
Corrigido¶
- A receita não dizia ONDE a página declara o markup que passa (
~{::navMenu}): oth:replacesubstitui o elemento host inteiro — host e conteúdo somem do output. Declarado solto no<body>, oth:fragmentrenderiza duas vezes; o certo é aninhá-lo dentro do elemento que faz o replace, e isso só aparecia no exemplo para quem já soubesse. É silencioso (não dá 500, só sai errado na tela). Agora está no bloco de código da receita, no cabeçalho dolayout.html, e ocheca-views.pyreprova. th:fragment="styles"morto no<head>do kit — fragment vazio herdado do layout pré-kit do integrador na templatização da 0.19.0. Zero uso nos dois apps Micronaut, não documentado em lugar nenhum: sinalizava um slot que não entregava. Removido; quem precisa de markup no<head>usa oheadComExtras.
0.25.1 - 2026-07-16¶
Adicionado¶
- Constituição 0.20.1→0.20.2 — runbook de implantação vira molde, e o §2 ganha as três
regras que o pior dia cobra (loop §6, feedback de app). O nível 5 promete "runbooks
técnicos: deploy, recuperação, destrutivas", mas o único template era o
runbook.md, de procedimento pontual (operação única, disparada por incidente, num app só) — quem precisava documentar subir um sistema não tinha base, e a diferença não é de tamanho, é de eixo: o procedimento responde "como executo esta operação"; a implantação, "como este sistema fica de pé, e como eu o desligo". Agora hátemplates/implantacao.md, irmão dorunbook.md(não substituto): topologia de operação, superfície de operação, variáveis por servidor com os pares que têm de casar, passos, verificação, reversão. - Três regras no piso §2 (bullet Runbook) — cada uma de um defeito real que passou pela
revisão humana num doc de implantação escrito sem base: (1) a Reversão enumera
todas as vias e o que cada desligar isolado não para (sistema com webhook + varredura
precisa da tabela caminho → efeito, com os caminhos que não funcionam marcados — kill
switch não conferido contra o código é kill switch imaginário, e é lido no pior dia
possível); (2) todo passo de Verificação declara o resultado esperado conferido no
código — mesmo payload ≠ mesma versão: onde a chave-versão é bumpada por escrita
(
@DateUpdated, sem dirty-check), idempotência prova-se com uma escrita e vários ciclos do consumidor, nunca com duas; (3) valor de contrato externo sem fonte do outro lado é<PLACEHOLDER>+ bloco "A confirmar" com o efeito de errar — fixture nossa não confirma contrato de terceiro (javadoc "ex.:", seed e whitelist concordam entre si porque descendem da mesma suposição). Valem para os dois moldes: as duas primeiras cobram seções que todo runbook já tem.
Corrigido¶
- §2 ganha a metade que faltava da fronteira: "o livro não entra no runbook" — corolário simétrico do já escrito "o deploy não entra no livro". O doc que originou este feedback re-digitava três capítulos do livro (topologia-arquitetura, fluxo de eventos, contrato de entrada), contra §1.9 (um dono por fato) e §1.2 (o factual vem da fonte). Não é purismo: o contrato re-digitado longe do dono foi o que virou exemplo-que-mente. O molde novo linka o livro e escreve só o que é de operar.
Adicionado¶
-
Constituição 0.19.2→0.20.1 — o container do app vira artefato prescrito (loop §6, feedback de app: nasceu doc-only, virou código, passou em teste e e2e local e quebrou no deploy do Coolify por não ter Dockerfile). A plataforma nunca prescreveu container: todo o conhecimento vivia no registro "não pise nesta mina" (armadilhas de
.dockerignore/.git/HEAD), e o registro "isto é o que você constrói" nunca foi escrito —coolify.mddizia "Build Pack:Dockerfile" tratando o arquivo como fato da natureza. Havia contradição interna: a §5 exige health check obrigatório, mas a Estrutura padrão (§3) não listava o artefato onde oHEALTHCHECKvive. E era assimétrico:flutter.mdtinha receita,java-micronaut.mdnão citava Dockerfile em linha nenhuma — a stack que mais deploya era a sem receita. Agora: piso na §3 (app deployável levaDockerfile+.dockerignoredesde o 1º commit de código); templates rastreados e opcionaisdockerfile-java+dockerignore-java;## Deploy — o Dockerfileemjava-micronaut.md; gatedocker buildno pré-flight do/xadm-release(degradado sem daemon, declarado no relatório); trilha "repo que era só doc e passou a ter código" emapp-novo.md; decisão 0011. Motivo do gate: oci.ymlnão builda imagem e o Coolify builda o Dockerfile do repo — era o único artefato que nenhum CI tocava, e a produção era a primeira a exercitá-lo. -
Flutter §Deploy web — cache no nginx (loop §6, feedback de app). Receita nova:
main.dart.jséno-cache, não cache longo — o Flutter Web não versiona URL de filho (verificado no build 3.44:buildConfigtraz"mainJsPath":"main.dart.js"e o loader monta a URL sem query nem hash; o único?v=é doflutter_service_worker.js, e a casa builda--pwa-strategy=none). Errar isso é invisível no deploy e mata o auto-update: o banner de "nova versão" aparece, o usuário clica e o browser serve o mesmo bundle do cache por até 30 dias. Regra +mapde exemplo + os assets de nome fixo que também precisam revalidar (MaterialIcons-Regular.otf,sqlite3.wasm,powersync_db.worker.js) +curl -sI …/main.dart.jscomo verificação pós-deploy. Motivo de existir a receita: o bi-comercial corrigiu o bug em 2026-05-13 e o bi-transporte ficou dois meses com o comentário oposto emax-age=2592000em produção — o central não tinha dono do assunto.
Alterado¶
SENTRY_DSNé o env canônico do DSN em toda stack (antesGLITCHTIP_DSN). É o nome que o SDK do Sentry lê do ambiente por convenção (Java e Dart) e o GlitchTip é Sentry-compatível, então o nome canônico elimina o código que existia só para traduzir env. A constituição mandavaGLITCHTIP_DSN, mas metade dos repos Java (integrador, int-produtos-ecom, bi-transporte-xls) e todo o Flutter já usavamSENTRY_DSN— a mudança ratifica a maioria. Vale igual no Flutter (--dart-define=SENTRY_DSN) e é o nome que o/xadm-setupinjeta no Coolify (exceçãoglitchtip.dsn → SENTRY_DSNna tabelaENVS_CANONICOSdo broker). Atenção — o rename é SILENCIOSO, não barulhento: trocar o nome no Coolify sem o código já ler o novo não dá erro — o${…:-}resolve para vazio, o appender vira no-op e o app sobe normal, enquanto todo erro de produção para de ser reportado sem sinal nenhum. Ordem segura: código primeiro (com fallback${SENTRY_DSN:-${GLITCHTIP_DSN:-}}), deploy, aí o rename no Coolify, aí remove o fallback. A injeção do broker é upsert: reprovisionar criaSENTRY_DSNe deixa oGLITCHTIP_DSNórfão no recurso — apagar na mão.
Corrigido¶
-
Cache mount do Gradle no Dockerfile:
iddefault =target+ sharing default =shared(achado ao dogfoodar o template novo — buildá-lo contra um app real em vez de publicá-lo por inspeção). Os dois apps Java da casa usam--mount=type=cache,target=/root/.gradlebare; como oidde um cache mount default para o própriotarget, todos os apps Java dividem um único cache no servidor do Coolify, e o sharing defaultshared("can be used concurrently by multiple writers") é justamente o que o Gradle não tolera. Dois deploys concorrentes — ou um build morto que deixou lock stale — falham comTimeout waiting to lock journal cache (/root/.gradle/caches/journal-1). It is currently in use by another process, intermitente e com cara de bug do app. Reproduzido, não inferido. Template usaid=gradle-<slug>+sharing=locked(o que a doc do Docker prescreve para o apt, pela mesma razão); nota emjava-micronaut.md§Deploy. Migração oportunista nos apps existentes. -
.dockerignorenão é.gitignore— a diferença é silenciosa e um dos padrões quebraria o build. Verificado empiricamente (Docker 29.5), contra fonte que afirmava o contrário: o Docker casa padrões comfilepath.Matchdo Go, onde*não cruza/. Consequências que os repos atuais carregam sem saber:buildbare exclui só a raiz (sub/build/viaja pro daemon — inócuo em single-module, mina armada ao modularizar), e**/*.jarquebraria o build ao excluir ogradle/wrapper/gradle-wrapper.jar(ogradlewmorre com "Could not find or load main class org.gradle.wrapper.GradleWrapperMain"). O template usa**/builde mantém*.jarraiz-only de propósito, com o porquê escrito. A exceção!docs/app.jsondo item 79 foi verificada de fato funcionando (e só funciona depois dedocs: vence a última linha que casa, não a mais específica). -
Constituição 0.19.1→0.19.2 — kit de UI:
layout.htmldeclarava dois fragmentsnavbarhomônimos (loop §6, feedback de app). O layout tinhath:fragment="navbar"eth:fragment="navbar(menu)", mas o Thymeleaf resolve fragment por nome e não faz overload por aridade (confirmado na doc oficial) → as duas chamadas que a receita ensinava quebravam no render (500):~{layout :: navbar}casava a variante com signature ("Error resolving fragment:${menu}") e~{layout :: navbar(~{::navMenu})}casava a sem signature ("Signature navbar declares no parameters, but fragment selection did specify parameters"). Qualquer app que adotasse o kit da 0.19.0 batia nisso. A variante com menu virounavbarComMenu(menu)— nomes distintos, chamadas posicionais válidas nos dois lados, semth:ifno fragment (que cairia na armadilha de precedênciath:replace(100) <th:if(300) e contrariaria o corolário de "duas superfícies, dois fragmentos" já normatizado). Receita do Java/Micronaut §UI corrigida; apps re-derivam o kit via/xadm-docs. - Constituição 0.19.0→0.19.1 —
checa-navcasava globs comfnmatch, não com o formato.gitignore(loop §6, feedback de app). Os padrões denot_in_nav:/exclude_docs:seguem o formato .gitignore (doc do MkDocs), e o script os casava comfnmatch— semânticas diferentes, divergindo em três frentes: barra inicial (/privado/*nunca casava → falso positivo, gate vermelho num exclude legítimo — o caso do feedback), barra final (privado/não casava em nível algum — é oexclude_docs:do próprio central, inócuo só porque ainda não há.mdsobprivado/) e*cruzando/em padrão ancorado (rascunhos/*.mdabsolviarascunhos/mais/b.md→ falso negativo, órfão real passando). Trocado por um mini-matcher.gitignoreem stdlib (o check roda no gate de código do app, onde não há mkdocs nempathspec). checa-nav: menção em comentário domkdocs.ymlvalia como declaração denav:(mesmo feedback). O script varria o YAML cru com regex ([\w./*\-]+\.md), então um.mdcitado num comentário absolvia o arquivo órfão — falso negativo que escondia o falso positivo acima. Comentários agora são removidos antes do regex (preservando âncorapagina.md#secao, que não é comentário).testa-checa-nav.py(novo, central-only): regressão docheca-navem 9 casos versionados — os 4 bugs acima reprovam a versão anterior. Roda no CI (build-site.yml) e no pré-flight da/xadm-release, como otesta-valida-frontmatter.
0.24.0 - 2026-07-16¶
Adicionado¶
- Constituição 0.18.11→0.19.0 — UI unificada para apps server-render (loop §6, feedback de app).
A constituição regia doc e engenharia mas não tinha convenção de UI: cada app que renderiza
HTML no servidor (Micronaut Views/Thymeleaf) — telas de admin/ops/dev, mesmo internas e atrás
do gate de auth do Coolify — reinventava o visual (integrador padronizado × tradutor ecom com
<table border="1">cru), drift que só se pega no olho. Piso novo no §5 (UI dos apps): todo app server-render segue o layout padrão X-Adm (fragment base únicohead/navbar/scripts, Bootstrap 5, header escuro com logo, idiomas da casatable-striped/badge/alert) na paleta única doxadm.css; o app só escreve o conteúdo de cada tela. Kit rastreado opcional no manifesto (templates/views/layout.html+custom-theme.css+logo.png+favicon.ico; sincroniza pela/xadm-docs, ausente em app headless/Flutter não é defasagem) + item de conformidade §3b da/xadm-docs(tem views mas não usa o layout → não-conformidade). Detalhe operável + snippetstatic-resourcesemengenharia/java-micronaut.md§UI; re-derivado limpo na paleta canônica (o kit do integrador usava#0066CC, cor errada); decisão 0010. - Constituição 0.18.10→0.18.11 — espelho fiel ao X-Adm (nível de coluna) (loop §6, feedback da
integração PIED). A integração PIED promoveu chaves do vocabulário do parceiro (
codigo_alt,x_ped) a identidade de 1ª classe na tabela-espelho; a §Fronteiras só regia o prefixo de tabela, nada regia a identidade de coluna dentro do espelho. Piso novo no §8 (ao lado de owner-writes): a tabela-espelho modela a entidade do X-Adm, não a do parceiro — identidade em coluna nativa (conferida contra a spec de entidades do ERP), campo de origem externa entra prefixado (<origem>_) e só como rastreabilidade, nunca chave/join. Detalhe operável + tabela de coluna emdocumentacao/integracao.md§Fronteiras; eco server-side emengenharia/java-micronaut.md§Banco; decisão 0009. - Dois gotchas de stack no
java-micronaut.md(loop §6, feedback de app; engenharia-only, sem bump). (1) §Armadilhas:@Clientconfig-gated — declararmicronaut.http.services.<id>.urlcria umServiceHttpClientConfigurationeager (@EachProperty) que quebra o startup do contexto inteiro se a URL estiver vazia (Failed to inject value for parameter [url]; sintoma = N@MicronautTestem massa noNettyHttpServer.start/beforeAll). Cliente HTTP só-em-alguns- deploys:@Client("${prop.url}")na interface +BeanProvider<Client>(lazy), assim o bean nem nasce. (2) §Banco: chave-de-mudança de espelho multi-writer vive no banco (trigger), não no app —updated_atpor trigger Postgres (BEFORE INSERT OR UPDATE, bump só emIS DISTINCT FROM) cobre todos os escritores (apply, admin CRUD, PowerSync) num lugar; coluna trigger-managed não se mapeia na entidade (Micronaut Data fazUPDATEsó do mapeado → null-wipe no update). - Flyway em Postgres compartilhado — app secundário exige baseline (loop §6, feedback de app;
engenharia-only, sem bump). §Banco do
java-micronaut.md: com a história por app, o segundo app sobe com a sua tabela de histórico ainda inexistente contra um schema não-vazio (tabelas do app dono já lá) → o Flyway aborta ("Found non-empty schema(s) without schema history table"). Cura:baseline-on-migrate: true+baseline-version: 0— o primeiro deixa o Flyway marcar o schema como linha-base em vez de abortar; o segundo faz a própriaV1do app rodar (o default1pula a V1 do secundário silenciosamente). Só no app secundário; o dono do schema sobe do zero.
0.23.7 - 2026-07-14¶
Adicionado¶
- Higiene do
.claude/settings.jsonvira norma da casa (engenharia/ferramentas.md, com ponteiro noapp-novo.md): o arquivo versionado leva só o compartilhado (hooks +additionalDirectoriesrelativo + allow-list de padrões largos que absorvem os comandos comuns); grants pessoais/de-máquina vão para.claude/settings.local.jsongitignored. Sem isso o allow-list incha a cada sessão e ogit commit --amendresultante troca o hash sob a tag, soltando a release. O templategitignore-flutterpassa a ignorar osettings.local.json. - Estilo terso (caveman) ativado no repo central via hook
SessionStart(.claude/caveman.sh) — dogfood do padrão queagentes.md/app-novo.md/ferramentas.mdjá prescreviam; regra de higiene de arquivos afiada emferramentas.md("Bash executa, não lê" + blocklistsed/ls -la/grep -rn).
Corrigido¶
- Typo no
.gitignore(.claude/.settings.local.json→.claude/settings.local.json) que deixava os grants pessoais fora da proteção do repo (só o ignore global da máquina cobria).
0.23.6 - 2026-07-14¶
Adicionado¶
- Constituição 0.18.9→0.18.10 — o fechamento passa a recomendar
/compactdepois do commit (pedido do Gustavo; altitude decidida por AskUserQuestion = skill + constituição §6). O/x-documentaré o passo que persiste o durável emdocs/, então compactar o contexto ao fim do ciclo recupera tokens sem perda (linha de economia dos itens 56/57/66). A ordem importa: commit antes do/compact, senão a mensagem pronta pode ir embora no resumo e o dev ainda não salvou. Bullet do fluxo recomendado no §6 da constituição + item 4 no fechamento (§5) da skill/x-documentar(template rastreado → re-carimbo + cópia dogfood re-derivada). Bump PATCH. engenharia/java-micronaut.md§Armadilhas (loop §6, feedback de app): Thymeleaf bloqueia expressão String em atributo de evento (th:onclick/th:onload/th:on*). Por segurança (defesa contra injeção de JS via dado de usuário), o Thymeleaf 3.x só aceita nesses atributos expressão que retorna número ou boolean → String lançaTemplateProcessingException("Only variable expressions returning numbers or booleans are allowed…") no render, com o build verde. Padrão da casa p/ linha/ card clicável: dado numdata-*(th:attr="data-href=@{/rota/{id}(id=${x})}") + handler em JS no rodapé — nuncath:onclickcom a URL. Nova armadilha na família de view server-side (irmã dorecord/OGNL e doth:replace). Bônus (corolário atado ao bullet de precedênciath:replace×th:if, decisão do Gustavo — AskUserQuestion): duas superfícies de UI distintas (chrome público × navbar de operador) = dois arquivos de fragmento de layout separados, não um layout único comth:ifno menu. Engenharia-only, sem bump (java-micronaut.mdnão é rastreada).engenharia/java-micronaut.md§Armadilhas (loop §6, feedback de app):ViewModelProcessor+Map.of()do controller = HTTP 500 só nas rotas que devolvem imutável. UmViewModelProcessorinjeta dado transversal no modelo de toda view (usuário/tenant/versão no layout — padrãolayout.htmlGlobalViewModeldo molde do integrador) viamodel.put(...); se o controller devolveu umMap.of(...)(imutável, comum no ramo "não encontrado"), oputestouraUnsupportedOperationException→ 500, só naquelas rotas. Regra: view controller sempre devolve modelo mutável (LinkedHashMap), nuncaMap.of. Guarda barata: testeGETnuma rota de not-found esperando 200. Nova armadilha na família de view server-side (irmã dorecord/OGNL e doth:replace), engenharia-only, sem bump (java-micronaut.mdnão é rastreada).engenharia/java-micronaut.md§Erros e logging + §Armadilhas (loop §6, feedback de app): cliente HTTP + config vazia = 500 + registro preso. URI montada de config (baseUrl+ rota) com base vazia vira URI relativa →HttpRequest.newBuilderlançaIllegalArgumentException(não a exceção de domínio) → escapa docatch→ 500; pior, se a máquina de estados marcou o registro "em voo" (ENVIANDO) antes da chamada, ele fica preso sem transição de saída. Dividido em dois donos (decisão do Gustavo, AskUserQuestion): norma durável de resiliência em §Erros e logging (validar a montagem da chamada → exceção de domínio tratável;catchde chamada externa largo o bastante praRuntimeExceptioninesperada, garantindo transição pra estado terminal em qualquer exceção) + a armadilha concreta em §Armadilhas apontando pra norma. Engenharia-only, sem bump.- Constituição 0.18.8→0.18.9 — armadilha "Dockerfile lê
docs/app.json⇒ precisa da exceção!docs/app.jsonno.dockerignore" (loop §6, feedback de app; verificado no.dockerignoredo integrador, que excluidocs). A ponte de config da Central de Apps tem duas metades que viajam no mesmo PR: o Dockerfile lerdocs/app.json(Flutter viajq→--dart-defineem build-time; um servidor viaCOPY→leitura em runtime) e a exceção no.dockerignore— separar quebra o deploy (arquivo fora do contexto de build). Dono stack-neutro:infraestrutura/coolify.md, ao lado da gêmea "não leia.git/HEADno build", com o contraste preciso (aqui a negação!docs/app.jsonresolve — o arquivo é committado e está no contexto; lá o.gitnem chega ao contexto). Cross-link da receitajq docs/app.jsonemengenharia/flutter.md. A/xadm-setuppasso 5 (que manda "crie a ponte") passa a exigir as duas metades → bump PATCH e re-carimbo doxadm-setup-skill.md. Decisão do Gustavo (AskUserQuestion): altitude = também a skill rastreada. engenharia/powersync.md§Auth (loop §6, feedback de app PowerSync): a §Auth passa a distinguir dois modos de consumidor — app com usuário real (JWT do usuário via JWKS docentral-backend, oujwks_urido Google no Firebase) vs cliente máquina-a-máquina (o cliente Java do ERP, os reatores — sem humano), que nasce self-signed + JWK inline: o cliente assina o próprio JWT com key privada em env e o PowerSync valida contra o JWK público inline — o mais simples e sem peça nova (sem broker, sem IdP), reservando Firebase/pAbast para quem tem usuário de verdade. Casa com três normas já existentes (Kerckhoffs emseguranca.md§Segredos,inline > jwks_urida própria §Auth, reusar-antes-de-inventar). A pegadinha dojwks_uri(timeout ~3s + NAT hairpin → falha intermitente, recorrente em produção) já estava documentada; ganhou nota de que vale para os dois modos e de que o self-sign já nasce inline. Ressalva anti-confusão: este M2M ≠ o M2M do broker de setup (lá quem assina é ocentral-backend; aqui, o próprio cliente). Engenharia-only, sem bump (powersync.mdnão é rastreada). Decisões do Gustavo (AskUserQuestion): altitude engenharia-only (não subir ao §8) e só a §Auth (sem eco naseguranca.md, que já cobre Kerckhoffs).
0.23.5 - 2026-07-13¶
Adicionado¶
-
engenharia/java-micronaut.md(loop §6, feedback de app — três gotchas de stack Micronaut/Thymeleaf): (a) §Armadilhas — oERROR Failed to inject … jsonMapper (JsonMessageHandler)no fim de uma run multi-classe@MicronautTesté artefato de teardown (o Micronaut derruba o contexto compartilhado e loga a injeção de um bean interno num contexto já desmontado), não bug: se a suíte fecha verde, não caçar — mesmo balde do falso-0% do JaCoCo e do Test Resources órfão. (b) §Armadilhas — Thymeleaf:th:replace/th:insert(precedência 100) rodam antes deth:if/th:unless(300), entãoth:ifno mesmo tag de umth:replacenão segura (o elemento já foi substituído quando oth:ifseria avaliado); cura =th:ifnum elemento pai (ou<th:block>). (c) §Testes — costura para testar controller cuja ação chama serviço com HTTP externo: oHttpClientinjetado por DI (base-url vazia em teste) aponta pro servidor embutido, não pra porta dinâmica do WireMock → teste o serviço com client construído à mão na porta do WireMock e o controller só nos ramos sem rede. Placement de (a) decidido pelo Gustavo (§Armadilhas, a família "parece bug, não é"). Sem bump (java-micronaut.mdnão é rastreada). -
engenharia/java-micronaut.md§Armadilhas (loop §6, feedback de app):recordno modelo do Thymeleaf estouraProperty … not found. Micronaut Views usa o Thymeleaf standalone, cuja linguagem de expressão é o OGNL, que resolve propriedade por getter JavaBean (getFoo()); umrecordgera acessorfoo(), nãogetFoo(), então${obj.foo}falha no render (erro de template, nãonull). Saídas: passar umMapno modelo ou invocar o método (${obj.foo()}). Sem bump (java-micronaut.mdnão é rastreada). Fora (decisão do Gustavo, não normatizado): "preferir cliente REST mão-livre (JDKHttpClient) a@Clientdeclarativo" — é consistência interna de 1 app, não prática da casa (≥2 repos);@Clienté idioma Micronaut válido. -
Constituição 0.18.8 — piso no §6 (Definição de pronto): contrato de entrada entre componentes NOSSOS (skill/cliente → endpoint first-party, ex. o broker
POST /api/setup/provisionque o/xadm-setupconsome) tem os campos obrigatórios travados nos DOIS lados — o cliente declara o contrato e o provedor tem teste que rejeita payload faltante (400 legível, não 500). Sem o par, driftam em silêncio: o guia dizia(app_id, feature), o broker passou a exigir mais → 500 em produção em vários apps antes de alguém notar (loop §6, feedback de app). Distinto do que já existia ("fixture de API externa é capturada, não inventada" = shape de resposta de terceiro; este = contrato de entrada first-party não-verificado no provedor). Detalhe cross-stack emengenharia/ci-testes.md§Definição de pronto (bullet novo, ao lado da fixture capturada); contrato operável do broker emdocumentacao/central-de-apps.md(bloco de identidade doapp.json:app_id,feature,grupo,cliente_id); síntese enganosa(app_id, feature)corrigida no template rastreadoxadm-setup-skill.md(l.47) e no diagrama decentral-de-apps.md. Bump PATCH 0.18.7→0.18.8 (xadm-setup-skill.mdre-carimbado). Handoff para o appcentral-backend(§6, Gustavo colar lá): o broker precisa do teste que rejeita/provisionsem os campos obrigatórios. -
engenharia/java-micronaut.md§Erros e logging +engenharia/seguranca.md§Validação de input (loop §6, feedback de app): quando o corpo problem+json carrega umcodede máquina que um consumidor NOSSO ramifica (ex. a Central lê ocodedo broker), esse discriminador tem de ser uniforme para toda a falha da mesma classe — a armadilha é o Bean Validation na borda (@Valid @NotBlank) produzir umcodederivado do status (BAD_REQUESTgenérico) enquanto uma checagem no service produz o code próprio da exceção de domínio, deixando o consumidor com metade das faltas de campo obrigatório num code e metade noutro. Norma: conserte o contrato, não o placement — mantenha o@Validna borda (norma da seguranca) e faça oErrorResponseProcessorde ponto único carimbar umcodeestável também nas falhas de Bean Validation (com a lista de campos). Refina o lado provedor do contrato de entrada travado dos dois lados (item 75): não basta rejeitar faltante com 400 — o 400 consumido por máquina precisa de code uniforme. Sem bump (java-micronaut.md/seguranca.mdnão são rastreadas). Handoff para o appcentral-backend(§6, Gustavo colar lá): validar o campo obrigatório de contrato no mesmo lugar que os demais (ou carimbar ocodede máquina no processor também para o Bean Validation) — hoje campos validados na borda saem com code derivado do status, inconsistente com os validados no service que a Central consome. -
engenharia/java-micronaut.md§Armadilhas (loop §6, feedback de app): a armadilha do Test Resources órfão ganha um segundo gatilho além do SIGTERM — rodar o gate com--no-daemone apagarbuild/entre execuções órfã o serviço de TR (sem daemon ele não sobrevive à JVM efêmera; orm -rf build/remove os arquivos de descoberta da porta) → mesma falha "Test resource service is not available" →initializationErrorem massa, que parece bug de código mas é infra. Lado da prevenção: rodar o gate com daemon (default) e nãorm -rf build/no meio de uma sessão de testes; se travar, o sintoma é service not available, não o código. Sem bump (java-micronaut.mdnão é rastreada).
Corrigido¶
engenharia/java-micronaut.md§Observabilidade (loop §6, feedback de app Java): a receita doSentryAppendercolocava o<dsn>flat no appender, mas emio.sentry:sentry-logback8.x oSentryAppendersó expõesetOptions(SentryOptions)—dsné propriedade doSentryOptions, não do appender.<dsn>fora de<options>não casa com setter nenhum e o logback ignora em silêncio (só um WARN no status interno; build verde) → DSN nunca setado → no-op invisível, e o erro real some em produção sem ninguém notar. Corrigido para<options><dsn>${GLITCHTIP_DSN:-}</dsn></options>(o<minimumEventLevel>é setter do appender e segue flat), com a nota do porquê e do porquê a rota/test/glitchtipexiste (pega justo esse buraco). Confirmado na doc oficial do Sentry (logback). Também ajustada a prosa da §Níveis de log que dizia "DSN no próprio appender". Sem bump (java-micronaut.mdnão é rastreada).
0.23.4 - 2026-07-11¶
Adicionado¶
- Constituição 0.18.7 — regra de layout de diagrama no §5: fluxograma Mermaid é
TB(vertical) por padrão. A página tem largura fixa e rolagem vertical infinita, então emTBcada linha usa a largura toda e o texto fica legível sem zoom; emLRo diagrama cresce na horizontal e aperta as caixas. Norma pede labels curtos edirection TBtambém dentro dossubgraph;LR/RLsó para pipeline curto e linear. Enforcement: olint-mermaid.mjs(rastreado) agora falha o build numflowchart LR/RLcom mais de 6 caixas, pedindoTB— escape hatch para o pipeline curto legítimo é um comentário%% lint-mermaid: LR-okno bloco. Bump PATCH 0.18.6→0.18.7. Loop §6 (feedback de app).
0.23.3 - 2026-07-10¶
Corrigido¶
-
Constituição 0.18.6 — o gate de formatação Dart checava reescrevendo: o comando da casa era
dart format --set-exit-if-changed ., e o default dodart formaté--output=write(grava em disco). O gate saía1e consertava os arquivos de passagem — rodar de novo dava verde, o vermelho parecia transitório e a correção ficava não-commitada (ogit adddo release leva só manifesto + CHANGELOG), de modo que o CI, com checkout limpo da tag, reprovava o que "passou" na máquina. Reabria exatamente o furo que a 0.18.5 fechou, e era invisível no CI (checkout efêmero). Agora o comando canônico édart format --output=none --set-exit-if-changed .(Flutter e Dart) em todos os pontos que o enunciam:/xadm-release,/x-implementar(+ reference Flutter),/x-planejar,/xadm-meta-audit-work,ci.yml,analysis_options.yaml,engenharia/flutter.md,engenharia/ci-testes.mdedocumentacao/app-novo.md. Princípio durável novo emci-testes.md§Definição de pronto: verificador não muta a árvore de trabalho — gate é check, nunca fix (vale paraspotlessApply/ktlintFormatdo lado Java; usespotlessCheck/ktlintCheck, que o./gradlew checkjá agrega). Corolário: se o gate mudou arquivo, o gate estava errado. O bloco Flutter do/xadm-releaseganhou também a nota do prefixofvmem repo com.fvmrc(dentro do containerci-flutter:<v>o SDK está no PATH e o prefixo não se usa). Loop §6, app Flutter. Bump PATCH 0.18.5→0.18.6. -
Constituição 0.18.5 — o pré-flight do
/xadm-releaseroda o gate de CÓDIGO da stack, não só o de docs (loop §6, app Java). O template enumerava os gates de docs por nome e tratava o código por uma frase genérica ("rode todos os checks do CI") — e o checkstyle, que só dispara dentro do./gradlew checke não é coberto pela análise estática feita lendo o diff, escapava até o push da tag (CI vermelho com a tag criada, exigindo release de correção). Agora cada bloco de stack em Particularidades traz a linha Gate de código (Java./gradlew check --no-daemon; Flutterdart format --output=none --set-exit-if-changed . && flutter analyze && flutter test; Nodenpm run lint && npm test) e o pré-flight tem dois bullets nomeados — código e docs —, vermelho em qualquer um = bloqueador. Máquina sem Docker/Testcontainers roda o degradado (checkstyleMain checkstyleTest) e o relatório final declara o gate degradado. Eco da norma emengenharia/ci-testes.md§CI de código. Bump PATCH 0.18.4→0.18.5.
0.23.2 - 2026-07-08¶
Adicionado¶
- Constituição 0.18.4 — princípio "fórmula de derivação de token é segredo se não há key separada"
(loop §6 do
central-backend) emengenharia/seguranca.md§Segredos: se um token de auth é derivável de uma fórmula sem um segredo à parte, a fórmula É o segredo — não vai em javadoc/comentário em claro (quem lê o fonte cunha um token válido = bypass); o certo é Kerckhoffs — derivar por HMAC com a key em env/secret (algoritmo público, só a key secreta). engenharia/flutter.md§Testes — verificação visual headless (loop §6, bi-transporte): a receita de screenshot (kScreenshotMode+ seedInMemoryAppDatabase) dobra como loop de verificação de mudança de pixel — renderizar pro PNG e ler a imagem confirma a mudança sem app rodando.engenharia/flutter.md§Arquitetura (loop §6, bi-transporte): padrão "extrair side-effect de serviço de estado — delegação, não listener". Quando o estado rastreado (intenção do usuário) ≠ oValueListenableexposto (efetivo/derivado, com ajustes automáticos), um listener emite eventos espúrios; o serviço delegaobserver.onChange(prev, next)no ponto da mudança de intenção e move a máquina (buffer/debounce/SDK de analytics) para fora. Ref.FilterAnalyticsObserver.- Constituição 0.18.3 — disciplina "caracterização precede refactor" (loop §6): antes de virar
tarefa de plano um "unificar/deduplicar N classes de mesmo nome", ler os corpos e confirmar a
identidade (caso real: 6 variantes
_Measureque pareciam 1). Emengenharia/ci-testes.md§Definição de pronto e no/x-desenhar(§2 explorar — design-time).
Alterado¶
/xadm-setup(constituição 0.18.4, loop §6 docentral-backend): a nota de auth M2M passa a proibir derivar o token de serviço da fórmula mesmo com o fonte docentral-backendà mão (o filtro de validação à vista) — peça o token ao usuário; computar da fórmula é bypass, não conveniência. Template rastreado → bump PATCH 0.18.3→0.18.4.engenharia/java-micronaut.md(loop §6, refinamentos verificados no integrador): corrige a receita da armadilha Test Resources órfão (era 0.23.1) —./gradlew --stopsozinho não resolve (a taskinternalStartTestResourcesServicefica UP-TO-DATE); o remédio universal é apagar o estado stale (rm -rf .micronaut/test-resources .gradle/configuration-cache). Nova nota no §Guarda de fronteiras: a guarda de arquitetura é bytecode — não vê dependência via constantestatic finalinlinada (limite conhecido, não violação a caçar). Expande a nota configuration-cache-safe (sem capturarlayout/project; ler o ÚLTIMO<counter>do XML do JaCoCo).engenharia/flutter.md§Piso: reforça que os lints de segurança assíncrona (unawaited_futures/cancel_subscriptions/close_sinks) pegaram 3 bugs reais na adoção — ligar cedo (na caracterização), não no fim.
0.23.1 - 2026-07-08¶
Adicionado¶
- Princípio de engenharia "reusar a infra da casa antes de inventar mecanismo" (loop §6): ao
desenhar transporte/gatilho entre serviços, confirmar a infra que já existe (PowerSync acoplado à
fonte da verdade) antes de propor um mecanismo novo — evita decisão auto-contraditória. Em
engenharia/index.md§Princípios. - Constituição 0.18.1 (loop §6) — cobertura de código: política + JaCoCo amarrado ao Java. A
política (visível não bloqueante por norma; piso bloqueante opt-in por app) entra em
engenharia/ci-testes.md§Definição de pronto; a tech emengenharia/java-micronaut.md§Testes, com a pegadinha crítica: JaCoCo <0.8.15 não instrumenta Java 25 (class major 69) → relatório válido na aparência mas vazio (falso 0% silencioso) — amarre atoolVersionao Java (Java 25 → ≥0.8.15) + nota configuration-cache-safe. Novo item de conformidade no/xadm-docs(§3b) checa isso. - Armadilha (loop §6) em
engenharia/java-micronaut.md§Armadilhas: gate morto por SIGTERM órfã um serviço na porta (daemon do Gradle ou Micronaut Test Resources) →initializationErrorem massa na run seguinte; remédio./gradlew --stop+ matar o órfão (e a norma da casa é o container singleton, que o Ryuk reapa). - Constituição 0.18.2 — disciplina de tokens propagada aos apps. O
claude-regras.md(CLAUDE.md que os apps herdam) ganha o ponteiro de gate-discipline (gates bare /rtk err/ sem pipe-git -C-2>&1; a frugalidade-na-fonte é o portável sem rtk) — antes só cobria caveman+enxuto, e o vazamento medido (rtk discover: ~561K tokens/30d) vivia nos apps. Allow-list do central ampliada (rtk read/grep/ls/find/wc/git diff).
0.23.0 - 2026-07-07¶
Adicionado¶
- Constituição 0.18.0 —
cliente_idé a chave canônica do tenant (loop §6, decisão 0008). Desconfla ocliente(rótulo de exibição) docliente_id(chaveclientes.cliente_id, minúscula) que a plataforma vinha derivando do display — a raiz de um incidente. Campocliente_idnovo notemplates/app.json(obrigatório quandogrupo=cliente); a/xadm-setuplê a chave, valida contra o catálogo docentral-backend(fail-closed) e nunca deriva; guarda offline novalida-frontmatter(rejeitacliente_idmalformado — o incidente) com fixtures versionadas (incidente + happy-path); norma em §7 ecentral-de-apps.md. - Constituição 0.18.0 — Plataforma de Integração vira norma central (§8), no molde do §7
(decisão 0007). Nova
seção §8 (norma curta: organiza-se por shape; taxonomia hub/vertical/reator/app consumidor,
ortogonal ao
grupodo §5; owner-writes; transporte por direção; PowerSync bidirecional;bi-commons); página de detalhedocumentacao/integracao.md(4 shapes, fronteiras, transporte); techengenharia/powersync.md(connector, write-back, JWKS inline,edition:3, callback-200); invariante owner-writes no §Banco dojava-micronaut.md; 7 verbetes no glossário (shape, hub, vertical, reator, barramento, owner-writes, bi-commons). O desenho concreto/volátil (roadmap, D0–D13) migrou para aarquitetura.mdpublicada no repo do integrador — a §8/página linkam, não copiam.
Alterado¶
- Deploy do site volta ao auto-deploy do Coolify — o job
deploygated pelo CI (v0.22.0, faixa 3) foi revertido: exigia auto-deploy desligado no painel + os secretsCOOLIFY_WEBHOOK_URL/COOLIFY_API_TOKEN, que não foram configurados (o job falhava no push comsecret ... ausente). Por ora o Coolify auto-deploya no push e a falha do CI avisa no Telegram (webhook nativo da org, Forgejo ≥12); o deploy gated fica como melhoria futura (job comentado nobuild-site.yml; verinfraestrutura/coolify.md).
0.22.0 - 2026-07-06¶
Adicionado¶
-
Faixa 3 da auditoria (constituição 0.17.36): (a) deploy gated pelo CI — o
build-site.ymlganha o jobdeploy(needs: validar, só push em master) disparando o webhook do Coolify; CI vermelho = site antigo no ar (antes o auto-deploy ia pro ar com o CI vermelho; os dois modelos documentados eminfraestrutura/coolify.md; requer auto-deploy desligado no painel + secretsCOOLIFY_WEBHOOK_URL/COOLIFY_API_TOKEN). (b) Fábrica de imagens:build-push.shganha smoke test pré-push (java -version/flutter --version/toolchain da base — imagem quebrada não entra no registry) e isolamento de falha por item (upstream quebrado de uma stack não bloqueia o rebuild das outras; resumo das falhas no fim, exit 1). (c)references/docs.mdnas 5 skillsx-*com references — a stack do próprio central (concerns de entrevista/spec/refino/ plano/execução para repo MkDocs: slug/redirect, dono do fato, satélites, artefato rastreado → bump, gate docs completo); 5 novos rastreados no manifesto. -
CI do central = pré-flight nas duas direções:
build-site.ymlganhoucheca-nav.py(órfão de nav não falha o--strict— foi assim que um órfão real nasceu),valida-frontmatter.py docs/e opublica-versao.py --self-test(grátis, stdlib). sync-apps.sh: guarda de slug ([a-z0-9-]) nos nomes vindos do bucket — a key de escrita é única da org; slug malicioso/acidental com../, espaço ou aspas podia virar traversal no docroot, quebrar o loop ou corromper oindex.jsonde todos os apps./x-documentar: antes de apagar o andaime.ia/, conferegit log -- .ia/NNN-*— arquivo nunca commitado (ciclo inteiro numa sessão) não é apagado, entra no commit do ciclo (senão "o histórico fica no Git" era promessa falsa e o rastro L# se perdia).-
/xadm-release:disable-model-invocation: trueno frontmatter — só o usuário invoca a única skill autorizada a commitar/pushar (fecha o vetor de release não-comandado, REGRA Nº 2). -
Constituição 0.17.31: piso "saída do agente enxuta por default" na §6 — corta prosa conectiva e status óbvio (gate verde não se narra; nomeia-se só o que falhou), não re-descreve o que o diff/tool já mostram nem ecoa a escolha do usuário, e a prosa do fechamento §6 é terse (o literal — commit + linha §6 — não). Nunca comprime o carve-out. Vale com ou sem rtk/caveman. Detalhe e exemplos de mensagens recorrentes em
engenharia/agentes.md(nova seção "Status e fechamento — nomeie só o que falhou").
Alterado¶
- Constituição 0.17.36 — consolidação textual SEM mudança de norma (§6, §1.9, §2): o
bullet "Definição de pronto" (27 linhas, 4 níveis de parênteses) virou 3 sub-bullets
com o racional movido para
ci-testes.md(que ganhou os bullets "deferir é custo × valor" e "fixture capturada" na seção de detalhe); "No CI (código)" enxugado com a fidelidade-ao-artefato apontando pro dono; §1.9 estruturado em 4 consequências nomeadas (andaime / segredo / código×doc / um-fato-um-dono); §2 ganhou subseções navegáveis (livro, nav+resumo, conteúdo mínimo); o cabeçalho deixou de prometer "documento curto e estável" e passou a dizer o que é (o piso; detalhe nas áreas). -
engenharia/flutter.md§Específico reorganizado por tema (Build e versão do SDK / Config e Central de Apps / Observabilidade / Receitas) — era sedimento de feedbacks; bullets intactos. -
Faixa 2 da auditoria — o "miolo do código" vira norma escrita (constituição 0.17.35; destilado dos pilotos com leitura autorizada: auth, bi-transporte-xls, bi-comercial-xls, bi-transporte, bi-comercial, authui):
- Arquitetura v0.1 por stack:
engenharia/flutterganha §Arquitetura (layout feature-first, taxonomia de DI get_it/watch_it com lazy+dispose+dependsOn, estado no manager como valor observável, command_it, como nasce uma feature, variações declaradas) eengenharia/java-micronautidem (package-by-feature travado por ArchUnit, constructor injection puro, controller fino, record @Serdeable vs @MappedEntity, @ConfigurationProperties, nomes pt-BR citando decisões). - Erros e logging por stack: exceção tipada na borda + erro-como-valor + trio loading/vazio/erro + AppLog com breadcrumbs/tags (Flutter); HttpStatusException + RFC 7807 num ponto único + semântica ERROR/WARN/INFO consciente do SentryAppender + logback com tuning comentado (Micronaut). Gap de request-id declarado como pendência, não fingido como prática.
- Testes e fixtures: convenção golden-master datada com receita de captura no javadoc e anonimização obrigatória; WireMock com shape capturado (§6); Testcontainers com container compartilhado pinado à versão do deploy; unit×integração por environment (Micronaut) — e in-memory DB com DDL de produção, builders, seams nomeados, pushNewScope, teste de contrato do app.json (Flutter).
engenharia/seguranca.md(nova página): authn/authz fail-closed com tri-estado, tenancy pela claim (servidor decide), comparação em tempo constante, validação jakarta obrigatória em request novo, client-grade ≠ segredo, checklist do revisor.- Banco (Postgres compartilhado) em java-micronaut: Flyway
VN__pt_brcom história nomeada por app e destrutiva justificada; pool Hikari deliberadamente pequeno (o Postgres é compartilhado); CRUD via Micronaut Data + bulk via JDBC manual. - Gate Dart completo:
templates/analysis_options.yamlcanônico (novo rastreado, flutter_lints + regras de segurança assíncrona do piloto) edart format --set-exit-if-changed .no gate Flutter/Dart (ci.yml, páginas, skills x-planejar/ x-implementar/meta-audit — a varredura de satélites do 0.17.34 pegou os 5 pontos). -
Teste de regressão do valida-frontmatter: fixtures versionadas (
scripts/fixtures-valida-frontmatter/, 10 casos) +testa-valida-frontmatter.pyno CI e no pré-flight da release — era o script de maior blast radius sem cobertura; de quebra, frontmatter com YAML quebrado agora vira erro legível em vez de traceback. -
Constituição 0.17.34 — dois pisos de token viram norma escrita (pedido do Gustavo): (a) §6 (Regras de IA): o CLAUDE.md, além de roteador, é carregado inteiro em toda sessão — backlog/histórico concluído migra para arquivo-acervo não carregado (padrão
BACKLOG-HISTORICO.md; caso medido no central: −15k tokens/sessão); (b) §6: a saída do agente é tersa (estilo caveman) por default — telegráfica no chat, com carve-out explícito que agora inclui conteúdo de documentação (texto emdocs/sai em prosa normal pt-BR, §1.8) além da mensagem de commit, código, linha §6, tabelas e.ia/. Ecos: carve-out deengenharia/agentes.mdganha o item documentação; comentário dotemplates/claude-regras.md(rastreado, recarimbado) carrega os dois lembretes para os apps. -
Constituição 0.17.33 — §6 enxuto em tokens: os dois bullets operacionais de rtk/caveman ("saída enxuta" e "disciplina de tokens") encolhem para o piso + ponteiro; o como (envelope
rtk errnos gates, comando bare, exemplos de fechamento) já tinha dono emengenharia/agentes.mdeengenharia/ferramentas.md(um fato, um dono, §1.9). Norma inalterada. -
CLAUDE.md do central em dieta: o histórico do backlog (itens 0–52) migrou para
BACKLOG-HISTORICO.md(acervo, não carregado pelo Claude Code) — o CLAUDE.md era ~20k tokens carregados em toda sessão, ~85% histórico; fica com regras + contexto + pendências + itens recentes (~7k). Ganha também: bloco TOKENS (gates viartk err, comando bare), regra de migração contínua (>~10 itens → histórico) e a varredura de satélites no §8 (bump da constituição → conferir glossário/templates/publicar-docs/ app-novo/ia/READMEs — trata o candidato §6 da rodada anterior: o manifesto protege os artefatos copiados, as páginas do site só têm essa varredura). -
Constituição 0.17.30: o pré-flight do
/xadm-releasedeixa claro que worktree limpo com commits à frente do origin é o caso NORMAL — a IA não commita (REGRA Nº 2), então o conteúdo da sessão é commitado pelo usuário e o release apenas segue, sem auditar a autoria dos commits à frente. Evita o falso alarme de "commit que eu não fiz" no início do release. Tocado o template rastreadoxadm-release-skill.md(+ cópia instalada).
Corrigido¶
- Constituição 0.17.32 — rodada de reconciliação de drift (auditoria interna do central,
4 pareceres): (a)
templates/xadm.cssre-derivado da fonte do site — a cópia distribuída ainda carregava navy/burgundy, paleta que o logo real não tem (a vigente é azul royal#064490+ prata; comentários stale emtemplates/mkdocs.yml,mkdocs.ymletemplates.mdcorrigidos juntos); (b)templates/docs.ymlpasso versions.json usava o placeholder<app>hardcoded em vez de"$SLUG"(contradizia a fonte única do slug e quebrava o 1º release versionado do app); (c)templates/anexos-readme.mdprometia guarda extinta ("o CI bloqueia massa de dados") — agora diz a regra real (validador só bloqueia emdocs/public/; fora deprivado/publica, responsabilidade do autor); (d)xadm-meta-audit-workrealinhada ao pipeline atual — auditava a extinta/xadm-work(estágios*_phase, exigia mensagem de commit que o run concluído corretamente NÃO entrega desde a ordemimplementar → documentar → commit); (e) instrução de instalação de skills unificada (app-novo diz 10 core incluindoxadm-setup;templates.mdganhou a linha/seção da/xadm-setupe aposentou os nomes pré-renamespec/refine/plan/work;ia.md§Como adotar delega ao manifesto em vez de mandar copiar só 2); (f) exemplos que ensinavam errado:app.jsondepublicar-docs.mdsemaptabase_host(o contrato corrigido no 0.17.23),templates/app.jsoncom Flutter 3.35.0 fora da baseline (gate do release barraria),templates/riscos.mdcom nomenclaturaPROJ-legada, glossário 2 reorgs atrás (manual como nível 4,superado-por:sem aspas — formato que o validador rejeita) e estrutura legada (manual/,img/) empublicar-docs.md.
0.21.0 - 2026-07-04¶
Adicionado¶
- Constituição 0.17.29: §6 (No CI/código) ganha o piso "o gate é fiel ao artefato que
embarca" —
testroda no classpath, mas o que sobe é o fat jar/container; ordem, descoberta e configuração podem divergir (merge deMETA-INF, native-image, shading). O default é eliminar a divergência (determinismo/paridade), não "smoke em prod"; só onde a divergência é inevitável o gate exercita o artefato empacotado (sobe o jar/container e bate num endpoint). Detalhe do "como" emengenharia/ci-testes.md; regra concreta de stack (@ServerFilterhonra@Order,@Filterlegado não → ordem indefinida no fat jar) emengenharia/java-micronaut.md. Feedback §6 de app Micronaut. - Gate
toolchain ∈ baseline da fábrica: amatriz-baseline.jsonpassa a ser publicada em/toolchain/matriz-baseline.json; o pré-flight do/xadm-releasebloqueia e o/xadm-docs3b avisa cedo quando um app declara versão de stack (app.json/.fvmrc) não homologada na fábrica — pega o "manifest unknown" antes da tag, não mais só no 1º release (constituição 0.17.28, decisão 0003).
0.20.0 - 2026-07-04¶
Alterado¶
- Constituição 0.17.24: o passo analytics do
/xadm-setuppergunta o arquétipo do app (produto vs interno/admin) antes de sugerir eventos — produto mantém o baseline de product-analytics; interno/admin passa a mirar a ação sensível (quem fez), e não screen_view/retenção. Adicionado o caveat de que evento client-side é visibilidade operacional, não audit trail (auditoria de verdade pertence ao backend). Espelhado emcentral-de-apps.md. - Guia de IA (
transversais/ia.md): seção opcional de otimização de custo de tokens — ferramentas de arquivo/busca em vez de despejo de shell, comunicação enxuta onde não custa clareza, e ferramentas pessoais opcionais que o agente pode oferecer (nunca impor). - Constituição 0.17.25: higiene de saída de comando — nova seção em
engenharia/ci-testes.md(corte o verboso na fonte; reporter frugal sempre; nativas de arquivo sobre shell; proxy é complemento) + comando frugal por stack (flutter test -r failures-only,./gradlew test --quiet --console=plain, …) nas páginas de stack, e a §6 afirma que o gate roda com reporter enxuto (verboso só sob falha). - Nova página
engenharia/agentes.md(Trabalho com agentes de IA): consolida a disciplina de tokens do agente — input frugal (link p/ ci-testes) + output terso + carve-out verbatim (nunca comprimir: código, mensagem de commit, linha §6, tabelas de dado,.ia/). O estilo terso vira prática recomendada via hookSessionStart(caveman.sh, mesmo padrão do checa-constituicao; funciona sem plugin); passo no checklist de app novo.transversais/ia.mdreconciliada. Sem bump (só doc/engenharia, não rastreadas). - Nova página
engenharia/ferramentas.md(Ferramentas de token — rtk): o rtk (proxy que resume a saída de CLI antes do contexto) vira recomendação da casa — install/verify, habilitar (hookrtk hook claude), configurar como padrão (ignore_dirspor stack, tee em falha) e regras por stack (Gradle bare sem pipe +sdk default javap/ dispensar o prefixoJAVA_HOME=que fura o hook;gitbare sem--porcelain; nativas sobrecat/find; rtk não cobreflutter). Reconcilia o conflito| tailda norma frugal (flags de reporter ficam; pós-processamento com pipe cede ao bare+rtk).ci-testes.md/agentes.md/ia.mdreconciliadas. Sem bump (não rastreada). engenharia/ferramentas.mdpassa a cobrir rtk (input) + caveman (output) como ferramentas recomendadas simétricas, cada uma com install/habilitar/configurar. Install real por plataforma dos repos oficiais (rtk-ai/rtk: brew/curl/cargo/Windows +rtk init -g; juliusbrussee/caveman: plugin do Claude Code / instalador universal). A receita do hookcaveman.shmigrou deagentes.md(que ficou com o princípio + carve-out e aponta pra cá). Sem bump (não rastreada).- Constituição 0.17.26: auto-análise de uso das ferramentas de token no fechamento §6. A §6 e a
skill
/x-documentarganham o passo (condicional a usar rtk/caveman): ao fechar, o agente mede (rtk gain/rtk discover; caveman = qualitativo), auto-corrige (bare/nativas/terso+carve-out) e roteia o gap sistêmico — recipe de stack → §6 (outros apps aprendem); comando verboso sem handler nativo → envolver comrtk err/rtk summary(ajuste de uso, resolve a maioria), RTK tracker só o residual. Nova seção "Auto-análise" emengenharia/agentes.md+ hook opcional levetoken-check.sh(eficiência dortk gainno start); passo noapp-novo.
0.19.5 - 2026-07-04¶
Alterado¶
-
Client_grade de analytics inclui
aptabase_host(Aptabase self-hosted) (constituição 0.17.23; feedback §6). A keyA-SH-self-hosted é inútil no cliente sem o host — o SDK Flutter exigeInitOptions(host:)(≠ do DSN do GlitchTip, que já embute o host). Sem isso o/xadm-setuptinha que hardcodar/adivinhar o host e gravá-lo à mão noapp.json(sujeito a clobber num re-provision). Agora o client_grade =aptabase_key+aptabase_host;central-de-apps+xadm-setupatualizados. Handoffcentral-backend: oAptabaseProviderdevolveaptabase_hostno client_config. -
Metabase organiza por sub-pasta (
grupo/cliente → app_id → dashboard + cards) (feedback §6 — ocentral-backendimplementou, decisão 0014). Antes o central documentava dashboard+cards soltos numa collection (exigia sufixar os cards comapp_idpra não colidir). Agora cada app ganha uma sub-pasta (collection filha, nome =app_id) dentro da collection do grupo/cliente, contendo o dashboard + os cards com nome canônico (sem sufixo, já isolados). Espelha oapp.json.central-de-appsatualizado; andaime.ia/016-*apagado (feature pronta). Sem bump (docs page). -
§6: fixture de API externa é CAPTURADA da instância real, não inventada (constituição 0.17.22; feedback §6). Um teste (ex. WireMock) verde sobre um shape fabricado é falso atestado — o mesmo "validador stale = falso 0 erros" que o §6 já combate, aplicado a fixtures de API (quebrou 2× em prod: Aptabase e Metabase, com
app_key/id:-1inventados). Agora a §6 (norma) e ox-documentar(§3 auditoria de lacuna) exigem: fixture de API externa capturada da instância real, e provider de API externa só marca "ligada" com ≥1 resposta real capturada. -
/xadm-release: pré-flight roda o build de docs + CHANGELOG sem link relativo (constituição 0.17.21; feedback §6). (1) Referência no CHANGELOG = texto puro ou URL absoluta, nunca[texto](docs/…)relativo — oCHANGELOGé embutido emdocs/via snippet, o link viradocs/docs/…e quebra o--strict. (2) O pré-flight roda o BUILD de docs que odocs.ymlroda (mkdocs build --strictcom hooks +javadoc/dartdoc) — um gate de infra vermelho (dind) mascara os gates seguintes no CI, então rodá-los local evita descobrir em prod. -
/xadm-docs: relevância por stack (evita falso-positivo domudou_emstack-cego) (constituição 0.17.20; feedback §6). Omudou_emdo manifesto é por-arquivo-global, não por-stack — uma mudança Java-only (ex.ci.ymlTestcontainers) marcaci.ymldefasado em app Flutter. Agora o/xadm-docs, para arquivo multi-stack customizado-por-app (oci.yml, cujo diff verbatim é esperado), manda checar a entrada do CHANGELOG da versãomudou_empara ver se a mudança toca a stack do app — se não toca, não é defasagem pra ele (mantém a customização). Decisão: guidance, não campostacks:no manifesto — oci.ymlé multi-stack, e umstacks: [java]esconderia updates Flutter-relevantes (nav-check etc.).
Corrigido¶
ci.ymlTestcontainers:DOCKER_HOSTvia runner, nãodocker.sock(topologia dind isolado) (constituição 0.17.19; feedback §6 — corrige o v0.19.4). O DooD pordocker.sockque saiu no v0.19.4 não funciona no runner da org (act_runnercom dind privilegiado → o mount do socket do host vira "not a valid volume"), e todo@Testcontainersfalha no CI. Fix org-wide mantendo o isolamento: oconfig.yamldoact_runnerinjetaDOCKER_HOST=tcp://172.17.0.1:2375(gateway do dind) +TESTCONTAINERS_HOST_OVERRIDE+RYUK_DISABLEDnos jobs → otemplates/ci.ymlfica agnóstico (removido odocker.sock). Detalhe/config emforgejo.md. Pendência do Gustavo: configurar oact_runner.
0.19.4 - 2026-07-03¶
Alterado¶
-
Definição de pronto (§6): deferir teste é custo × valor, não racionalização (constituição 0.17.18; feedback §6). "Exercitado end-to-end" e "baixo risco" são justo as frases que deixam a guarda de regressão barata escapar — sobretudo em segurança/privacidade/contrato. Agora a §6 (norma) e as skills
x-implementar/x-documentarexigem, por lacuna de teste, uma classificação explícita custo × valor: default = escrever a guarda barata e proporcional agora; só deferir no quadrante caro E de baixo valor; comportamento sensível → escrever a guarda agora. Bump 0.17.17→0.17.18 (as duas skills rastreadas + §6). -
Fluxo do pipeline:
/x-implementar → /x-documentar → commit(constituição 0.17.17). O/x-implementar, ao concluir o plano, deixa de entregar a mensagem de commit — aponta pro/x-documentar, que faz a revisão geral (avalia lacuna de doc e de teste, §6), destila o durável pradocs/, apaga o andaime (.ia/NNN-*) e entrega a mensagem de commit do ciclo. Antes o commit era sugerido nox-implementar; agora o ciclo fecha nox-documentar. Bump 0.17.16→0.17.17 (as duas skills rastreadas +ia.md). -
Feedback §6 (docs de engenharia/central): paleta por grupo, receita de screenshots, gotchas, formas de provider — sem bump (engenharia/documentacao não são rastreadas). (1) A paleta do cliente passa a ser gatilhada pelo
grupo: clientedoapp.json(marca derivada do logo do cliente), não mais o "veste a marca declarado" do #31 — mais simples (engenharia/flutter). (2)screenshots-manualganha o seed com DDL do schema PowerSync + o gotcharegisterSingletonAsync. (3) gotcha doink_sparkle.fragem bump de SDK (flutter clean+pub get) emengenharia/flutter. (4)central-de-appsdestila as três formas de provider (find-before-create / forjar-token / reuso-persistido) ejava-micronautganha 2 armadilhas REST (PUTsubstitui coleção → read-modify-write; nomear por escopo em container compartilhado). -
Check de nav-drift (
checa-nav.py) no gate de código (constituição 0.17.16; feedback §6). Num repo stack+MkDocs o gate de código não roda mkdocs → um.mdnovo fora donav:só estoura nodocs.yml(que não roda a cada PR de código) e órfãos acumulam (o app relatou 3 decisões + 1 runbook órfãos por 2 fases). Novoscripts/checa-nav.py(stdlib): FALHA se um.mddedocs/não está nonav:nem excluído (isentaREADME.mdde pasta); rodado ativo noci.yml(tolerante semdocs/), publicado como os outros validadores. §6 esclarecida: "doc do estado atual" inclui aparecer no índice, não só existir. Dogfood: pegou odecisoes/0006órfão no próprio central (corrigido no nav). -
Constituição §2: provider automagico não precisa de runbook de "como usar" (constituição 0.17.15; feedback §6). O que roda sozinho (ex. provider build-time da Central de Apps — "como usar" = a skill, "porquê" = a decisão) não ganha runbook; sobra config-de-operador (envs + como obter) + diagnóstico, numa seção "Operar" da própria decisão, não num runbook que duplica o design. Os runbooks
provedor-*.md(nocentral-backend) morrem — cleanup docentral-backend(handoff). -
Auto-config do Metabase no
/xadm-setup— fatia do central (constituição 0.17.14; feature 016, desenhada via/x-desenhar→/x-definir→/x-refinar→/x-planejar→/x-implementar). Depois do Aptabase, o broker auto-configura o Metabase (API real):collection(cliente) → dashboard(app) → cards baselineque adaptam aos eventos escolhidos; idempotência por id estável; resposta honesta com sub-estadosaptabase+metabase; 6 queries ClickHouse = template canônico no broker; gravametabase_dashboard_id(pro embed futuro).central-de-apps(provider analytics) +xadm-setup-skill(passo). A implementação do broker Metabase é do repocentral-backend(handoff). -
Rota de diagnóstico
/testno baseline de observabilidade (opt-in) (constituição 0.17.13; feedback §6). Verificar pós-deploy que o erro chega no GlitchTip era improvisado por app; agoraengenharia/flutter(tela oculta login-gated com botão "enviar exceção de teste") eengenharia/java-micronaut(endpoint autenticado/test/glitchtip) trazem a receita, e o/xadm-setupoferece scaffoldar (opt-in, com o OK do dev) junto da instrumentação. Não obrigatório (backend/cron não é forçado a ter UI de diagnóstico). -
Aptabase no
/xadm-setup: provisão full-auto forjando o token (sem API) (constituição 0.17.12, mecanismo corrigido em 0.17.15; feedback §6 de um app). O Aptabase self-hosted não tem API e autentica por sessão. Descoberto empiricamente (decisão 0013 docentral-backend): o token é um JWT simétrico (HS256) assinado com o secret do próprio tool → o broker forja o token (secret no env docentral-backend) e abre sessão direto, depois cria o app →A-SH-…. (O "magic-link dos logs do Coolify" não funciona — o Coolify não expõe logs de service.) É o padrão "provider sem API → forjar o token"; guardas: fail-fast, versão fixada, secret forja sessão de qualquer usuário. Ocentral-de-appsdocumenta o fluxo + o padrão; o passo do/xadm-setuppassa a perguntar ao dev quais eventos registrar e scaffolda o helper só para os escolhidos. -
Ponte
app.json→--dart-defineno build Flutter-web +/xadm-setuphonesto (constituição 0.17.11; feedback §6 de um app Flutter-web da Central de Apps). O/xadm-setuppasso 5 assumia que "o Dockerfile/CI já lê oapp.json", mas o scaffold hardcodava ARGs e nem tinhajq.engenharia/flutterganha a receita concreta (instalarjq+flutter build … --dart-define= $(jq -r .features.<x>.<ref> docs/app.json)) e reconcilia "config no repo" (=app.json+ ponte, não hardcode; client-grade não precisa de build-arg do Coolify — só segredo). O passo 5 passa a mandar garantir/criar a ponte (aponta pra receita), não "confira que já existe". -
Identidade visual: app que veste a marca do cliente usa a marca do cliente, não a da casa (feedback §6 do bi-comercial/OnPetro). A
engenharia/fluttertratava "paleta = a da casa" como absoluto; agora a identidade da casa é o default (app da X-Adm / app de cliente sem marca própria) e um app exclusivo que veste a marca do cliente usa a paleta/logo/tipografia do cliente (declarado noapp.json). O gatilho é vestir a marca do cliente, não o merocliente:; o mecanismo da casa (tema porColorScheme, app-icon, feature-first) segue valendo em todo app. Sem bump (engenharia é regra viva, não rastreada).
0.19.3 - 2026-07-03¶
Corrigido¶
checa-dogfood.pyreconhece skills em diretório (destrava o deploy do v0.19.2). O gate de CI comparava.claude/skills/<nome>/SKILL.mdsó comtemplates/<nome>-skill.md(plano) e acusava as 6x-*(modelo diretório) como "sem template canônico" → jobvalidarvermelho → deploy travado. Agora compara o diretório inteiro (SKILL.md +references/) para asx-*e mantém o modelo plano para asxadm-*.
Alterado¶
- Pré-flight do
/xadm-releaseroda todos os checks do CI (constituição 0.17.10; feedback §6 do incidente acima). O gate local rodava sólint-mermaid+gera-manifesto --check+valida-frontmatter, mas o CI roda tambémcheca-dogfood.pyecheca-export-download.py— por isso o drift passou no pré-flight e só estourou no CI depois da tag pushada. O §1 agora manda rodar todos os checks do CI do repo (condicional "se o repo os tiver", pois são central-only).
0.19.2 - 2026-07-03¶
Adicionado¶
- Recipe GlitchTip/Sentry para Micronaut e armadilha
@Consumesem endpoint de<form>na páginaengenharia/java-micronaut.md(feedback §6 docentral-backend). Sentry viaio.sentry:sentry+sentry-logback(SentryAppendernologback.xml,minimumEventLevel=ERROR, DSN vazio → no-op, zero código no negócio; espelha o bloco Flutter).@Consumes:@Postassume JSON → 415 em formx-www-form-urlencoded; armadilha de teste (oHttpClientmanda JSON por default).
Alterado¶
-
Contrato do
/provision(Central de Apps): injetar APLICA a config (reinicia), não só grava (constituição 0.17.9; feedback §6 docentral-backend). Env upsertado não recarrega em processo vivo → o broker reinicia o recurso Coolify ao injetar, einjected:truepassa a significar "injetado E em vigor"; reinicia só quando o valor mudou (lê o atual + compara), pra não derrubar serviço sensível (central-backend) à toa em re-run. Provado nocentral-backend(decisão 0011). -
templates/ci.ymlJava dá Docker ao Testcontainers (DooD) (constituição 0.17.9; feedback §6 docentral-backend). O jobcontainer: ci-javanão montava o socket do host → todo@Testcontainers(padrão da casa p/ "banco real") falhava no CI com "could not find a valid Docker environment", deixando o gate §6 "No CI (código)" vermelho crônico. Correção:container.volumesmonta/var/run/docker.sock; pré-requisito do runner (act_runnervalid_volumes) documentado eminfraestrutura/forgejo.md.TESTCONTAINERS_RYUK_DISABLED=truejá vinha no ambiente, mas não bastava — faltava o socket. -
Skills de workflow renomeadas e realinhadas ao ACT 1.0 (constituição 0.17.8). O pipeline
spec → refine → plan → work → compoundviroux-desenhar → x-definir → x-refinar → x-planejar → x-implementar → x-documentar, adotando o split entrevista/spec do ACT 1.0 (x-desenhar conduz a entrevista e grava o prompt.ia/NNN-*-prompt.mdcom o rastro L# embutido; x-definir escreve a spec referenciando os L#), tarefa = corpo de Work Item embarcado no plano, e especialização de stack viareferences/<stack>.md(Flutter portado do ACT + Micronaut autorado) carregada pelo §0 — sem variantes-skill. Preserva os invariantes da casa (NÃO commita, §0, gate da stack, §6, ghost mode, lineage.ia/, storage.ia/sem.act/). Cada skill agora é um diretóriotemplates/<nome>/(SKILL.md +references/). Manifesto,gera-manifesto.py,xadm-docse as referências (CLAUDE.md, ia.md, constituição, app-novo, templates.md, engenharia) atualizados. Apps migram oportunisticamente via/xadm-docs. Verdocs/decisoes/0006-skills-workflow-act1.md. -
Constituição 0.17.7 — "idempotente" = procura-antes-de-cria (feedback §6: dois projetos com o mesmo nome no GlitchTip). O contrato dizia só a palavra "idempotente", que a implementação leu como cria-depois-procura — e como o GlitchTip permite nome duplicado, um re-run (ou falha no find + nova chamada) duplicou o projeto. central de apps §Provedores agora define o mecanismo: o provider checa se o recurso já existe (por nome/
app_id) e reusa, nunca cria às cegas; onde o provedor permite nome duplicado, o find é obrigatório. O guard find-before-create em si é Camada 2 (auth — o377c050corrigiu o find). Bump PATCH 0.17.6→0.17.7. - Constituição 0.17.6 —
/provisionresponde estado real +/xadm-setupinstrumenta o código (feedback §6 da implementação docentral-backend). Três furos: o/provisionrespondia 200 comenabled:truesemdsnquando o provider falhava (indistinguível de sucesso); a injeção no Coolify falhava em silêncio (best-effort, só WARN — token 401, DSN não injetado, ninguém soube); e o setup ligava a integração sem instrumentar o código. Correções em central de apps: a resposta do/provisioncarrega o estado real por provider — provisão (state/last_error) e injeção (injected+ motivo), com não-2xx se nada ligou; o/xadm-setupconfere e não gravaenabledsem o ref, avisa eminjected:false(setar o env à mão) e não re-chama provision "pra confirmar" (isso gerava projeto GlitchTip duplicado). E a skill passa a instrumentar o código (scaffold + guia, com OK do dev): instalar o SDK, envolver orunApp(SentryFlutter.init), guiar os pontos de captura — recipe emengenharia/flutter. O guard find-before-create do provider é Camada 2 (auth). Bump PATCH 0.17.5→0.17.6. - Constituição 0.17.5 — convenção: projeto GlitchTip nomeado pelo
app_id(feedback §6 da implementação docentral-backend). O slug do projeto GlitchTip é sempreslugify(nome)(oslugdo corpo é ignorado, imutável por PUT) e odsnusa o id numérico, não o slug. Convenção registrada na skillxadm-setup(passo glitchtip) e em central de apps §Provedores: o broker nomeia o projeto peloapp_id(slug estável e curto), decorrência doapp_idcanônico. A trivia da API do GlitchTip (slug ignorado no corpo, PUT, DSN por id) fica noGlitchTipProviderdocentral-backend(Camada 2), não no contrato central. Bump PATCH 0.17.4→0.17.5. - Identidade visual reconciliada com o logo + norma visual pra apps Flutter (feedback §6). O
xadm.cssafirmava paleta "extraída do logo", mas oxadm-logo.pngé azul royal + prata, sem o burgundy#8b3a3adocumentado (verificado amostrando o PNG). Decisão do Gustavo: derivar do logo real — a paleta da casa passa a ser azul royal#064490(primária) + médio#2a5b97(accent) / claro#7092bb/ escuro#042c5e+ prata#939495(neutro); sai o navy/burgundy.docs/stylesheets/xadm.cssvira a fonte-da-verdade dos tokens (docs e apps consomem), eengenharia/flutterganha a seção Identidade visual (paleta→ColorScheme, logo + receita de app-iconflutter_launcher_icons, tipografia/densidade como decisão). OThemeDatacompartilhado, o kit mínimo de componentes e a variação quadrada do logo ficam para uma spec do padrão de identidade visual de apps (barato agora, resto na spec). Sem bump da constituição (docs/assets, não artefato rastreado). - Constituição 0.17.4 — broker de setup autentica por token de serviço (M2M) (feedback §6 da
implementação do
central-backend). O contrato pressupunha login humano (JWT xadm admin/Firebase) pra chamar o/api/setup/**— mas é endpoint máquina-a-máquina (o/xadm-setup), e nenhum CLI tem como obter esse JWT (loopback-OAuth inexistente). Agrava que o/provisionrecebeproduction_urle injeta env no recurso Coolify → num endpoint aberto, a auth importa de verdade. Nova norma (§7 + central de apps seção "Auth do broker de setup" +xadm-setup-skill): token de serviço, com força casada à exposição (interno/VPN → simples; público → segredo forte ou rotativo por HMAC-de-data, nunca fórmula pública); a regra de geração vive fora de banda (servidor + admins), jamais na skill ou em doc publicada — a skill conhece só o formato e pede o valor. Registra o gap "como obter a credencial de setup". Bump PATCH 0.17.3→0.17.4; manifesto regenerado (xadm-setup-skill). - Constituição 0.17.3 — Central de Apps: injeção de config (não só secret) e web ≠ server
(feedback §6 da implementação do
central-backend). A tabela "duas naturezas" juntava web/server e dizia que o client-grade vai como build-arg — só vale pra web (bundle). Um app server lê config do env em runtime, então o client-grade dele (ex.SENTRY_DSN) também é env injetada no Coolify, não build-arg. Correções em central de apps: linguagem "injeção de secret" → "injeção de config" (client-grade + secret); tabela com colunas web / server / mobile; checkpoint de deploy garante a config (não só secret); ponteiro pro endpoint de env do Coolify. Bump PATCH 0.17.2→0.17.3. (Mantido o modelo de 3 checkpoints — não adotada a opção de dobrar a injeção no/provision.)
0.19.1 - 2026-07-01¶
Corrigido¶
- Diagrama Mermaid da Central de Apps + lint no gate local. O
sequenceDiagramdecentral-de-apps.mdtinha um;numa mensagem (separador de statement no Mermaid) que quebrava olint-mermaiddo CI e travava o deploy desde o push do build-time — sem aparecer em nenhum check local, porquemkdocs build --strictnão parseia diagramas. Corrigido o;; e o lint de Mermaid entra no gate local (constituição 0.17.2):/xadm-worke/xadm-plan(verify docs), pré-flight do/xadm-release(bloqueia antes de taggar) e o gate docs documentado em ci-testes. Bump PATCH 0.17.1→0.17.2; manifesto regenerado (skills work/plan/release).
0.19.0 - 2026-07-01¶
Adicionado¶
- Constituição 0.17.0 — Central de Apps (Camada 1, paradigma BUILD-TIME). Nova
## 7+ central de apps: a integração de um app com a infra X-Adm (erros/GlitchTip, arquivos/Garage, analytics Aptabase+Metabase, docs) é configurada em build-time, não amarrada em runtime. O app declara noapp.json(featuresvira objeto por funcionalidade); a skill nova/xadm-setup(entre codar e/xadm-release) pergunta o que ativar, provisiona via ocentral-backend-broker (que tem os tokens admin) e grava o resultado client-grade noapp.json— o build embarca, sem buscar config no boot. Runtime só o mínimo: corretores (embed Metabase/presign Garage) e feature flags (protocolo próprio — spec 014).central-backend= broker + configurador de secrets (Coolify/CI) + corretor;central.xadm.biz= dashboard (mostra o configurado + links, aprova acesso de terceiros). 3 checkpoints (setup → guard soft na/xadm-release→ guard de deploy noci.yml); 2 naturezas × plataforma (client-grade web=build-arg / mobile=--dart-define; secret web=Coolify / mobile=org-CI); fronteira de dados (auth×app;cliente_idclaim×FK). Tocados:templates/app.json(featuresobjeto),valida-frontmatter.py(rejeita chave desconhecida), skillxadm-setupnova, guard soft naxadm-release, placeholder de deploy noci.yml, ecos em publicar-docs/app-novo/flutter; Bugsink→GlitchTip reconciliado (grep=0). Bump MINOR 0.16.1→0.17.0. Substitui o desenho runtime anterior (não shipado). Camada 2 (código auth/central/apps) + feature flags (014) em specs próprias.
Alterado¶
- Constituição 0.17.1 — corretor autoriza por app + provisão por
app_id(achados do plano de implementação da Camada 2 nocentral-backend). O contrato do corretor exigia só JWT válido (autenticação); agora a §7 e central de apps exigem autorização por app — o claim do JWT casa com o app do recurso (403 cross-app): usuário logado no app A não cunha embed/presign de recurso do app B. E a §Catálogo separa provisão (porapp_id) de acesso (por (cliente, app), derivado dosoauth_access_grants) — não há tabela de pares para a provisão. Bump PATCH 0.17.0→0.17.1 (só docs de padrão; nenhum artefato rastreado mudou). - Constituição 0.16.1 — migração de conteúdo cross-repo + template×piloto (feedback §6 do
authui). A regra de migração só cobria conteúdo do próprio repo (
guias/por conteúdo); agora a §3 e a/xadm-docsdizem que doc/runbook que pertence a outro repo, achado neste, é realocado para o repo dono — não absorvido/relabelado nooperacao/local. E oapp-novo.mdesclarece template × piloto: o template (sincronizado via manifesto) é a única fonte de cópia; o app piloto/de referência é exemplo provado/destilado (Engenharia), nunca base de cópia. Bump PATCH 0.16.0→0.16.1; manifesto regenerado. - Constituição 0.15.3 —
/xadm-refine-speccom passada de contexto profunda. O refine da casa confirmava que os artefatos existiam, não que os fatos afirmados sobre eles eram verdadeiros — então não pegou o que o refine nativo do ACT pegou (ex.:app.jsonreal semslug,app_iddivergente, conflito de audiência doauthui). Correções no template e na cópia instalada: §1 vira passada profunda (ler o conteúdo de cada referência; conferir cada fato contra o artefato real; mapear tabelas/endpoints/identidade/namespaces; rastrear uma instância real ponta a ponta; e, se paralelizada, fazer isso UMA vez antes das dimensões); dimensão 2 cobra "fato conferido LENDO o artefato"; e uma lente transversal do implementador ("se eu fosse construir isto agora, onde travaria?"). Bump PATCH 0.15.2→0.15.3 (artefato rastreado mudou); manifesto regenerado. - Doc de infra — endpoint de env/secret da API do Coolify. O
coolify.mdpassa a documentar oPATCH /api/v1/applications/{uuid}/envs/bulk(upsert em lote, Bearer; "Secret" = env marcada como Secret, não um tipo à parte; flags Build/Runtime) que o guard de deploy da Central de Apps usa para injetar os secrets de build — tira a Fase 4 do broker nocentral-backenddo "spike do zero" (fato verificado na doc oficial do Coolify).
0.18.3 - 2026-06-30¶
Corrigido¶
- Constituição 0.15.2 — desambiguação versão-do-site × versão-da-constituição. Um app
em bootstrap (sem
/xadm-docsinstalada, só com o link da constituição) ficou em dúvida entre0.15.1(constituição) e0.18.2(site): o número do site aparece nu e gritante (VERSION,/versao.txt, tagvX.Y.Z, CHANGELOG, e mais ainda num clone local do repo central), enquanto o da constituição é discreto. Correções: a §6 da constituição passa a explicitar "duas versões, não confundir" (com a armadilha do clone local e do bootstrap); nova seção Duas versões: site x constituição emengenharia/versionamento.md; guard na skill/xadm-docs(a vigente vem só deconstituicao-versao.txt, nunca deVERSION/tag); nota roteadora noCLAUDE.mddo central; e correção do path staledocs/padroes/constituicao.mdna skill/xadm-release. /versao.txtauto-rotulado (site X.Y.Z). O Dockerfile passa a gravar o número do site rotulado em vez de nu, para não ser confundido com a versão da constituição.- Nota da distinção também no checklist de app novo (
app-novo.md§3): o que o app rastreia é sóconstituicao-versao.txt;VERSION//versao.txt/tag são do site. templates/mkdocs.yml— ordem dos plugins corrigida. Oprint-siteestava antes doswagger-ui-tag, contrariando o próprio comentário ("DEVE ser o último") e quebrando omkdocs build --strict(achado na adoção real). Reordenado paraprint-sitepor último.- Bootstrap sub-instalava as skills. O checklist de app novo (
app-novo.md) listava sóxadm-docsexadm-release, e omanifesto.jsonnão distinguia skill core de opcional — quem fazia bootstrap pela leitura instalava 2 de 8 (caso real). Correções: omanifesto.jsonganha o campoopcionais(hoje sóxadm-docx;gera-manifesto.pyemite e o--checkguarda); oapp-novo.mdpassa a mandar instalar todas as*-skill.mddo manifesto exceto asopcionais, listando as 8 core e apontando o manifesto como a lista mecânica completa; a/xadm-docspula asopcionaisna conferência de defasagem (opcional não-instalado não é defasagem).
0.18.2 - 2026-06-26¶
Adicionado¶
- Navegação Anterior/Próximo no rodapé das páginas (
navigation.footer). Habilitada a feature nativa do MkDocs Material no site central e notemplates/mkdocs.ymldos apps: cada página ganha links "Anterior" (esquerda) / "Próximo" (direita) derivados da ordem donav:, servindo o modo "ler como livro" (§1.7) — mais útil ainda no manual e no livro do projeto dos apps. Bump PATCH 0.15.0→0.15.1 (artefato rastreado mudou); apps re-baixam o template via/xadm-docsna migração oportunista.
Alterado¶
- Constituição 0.15.0 — screenshot do manual entra na definição de pronto (§6). A metade (2) "doc do estado atual" agora lista o screenshot do manual entre os "estados atuais que mentem": mudou UI retratada numa imagem (botão novo, rótulo, layout) → regenerar o PNG afetado é parte do pronto, e a verificação inclui abrir a imagem e confirmar o elemento novo, não só rodar o gerador (gerador que roda sem semear o estado certo "passa" mostrando a tela velha — a mesma falha da metade (1)). O runbook Screenshots do manual ganhou a seção "Revisar o diff antes de commitar" com a armadilha do diff de só-ruído (marca d'água/elemento aleatório → reverter os PNGs de só-ruído, manter o que teve mudança visual real). Loop §6 de um app de BI Flutter (vantroba-bi v1.5.0: botão de export novo, texto atualizado, .png ficou na tela velha — pego só depois).
Adicionado¶
- Engenharia/Flutter: padrão de export XLSX sem lib licenciada. Registrado na página
de Flutter: usar o
excel(justkawal, MIT) + pós-processar o OOXML comarchivepara freeze pane/autoFilter/outline (que oexcel4.0.6 não faz — verificado no fonte), em vez de trocar por Syncfusion (licenciado); com as armadilhas (Archiveimutável, download Web manual, edição idempotente + smoke real) e a nota de hierarquia × sort. Imagem/logo no XLSX ficou fora de escopo (sem receita de injeção). Loop §6 de um app de BI Flutter.
Corrigido¶
-
Menu de abas: o vão Teoria/Prática sumia na própria página Aplicações. A regra que empurra o grupo da direita (Aplicações · API Reference · Mudanças) mirava pelo href (
[href*="aplicacoes"]), mas o Material reescreve o link da aba ativa para./— que não contém "aplicacoes" — então, ao abrir Aplicações, omargin-left:autonão casava e todas as abas agrupavam à esquerda. Passou a mirar por posição (nth-last-child(3)= início do grupo da direita), imune ao estado ativo. Regra é exclusiva doxadm.cssdo portal central; apps não usam. -
Release: pré-flight pedia autorização (constituição 0.14.34): o
.claude/settings.jsoncentral não tinhagit status/git logno allow-list (o template da skill já os prescrevia — estavam dessincronizados), e o pré-flight foi rodado num bloco multi-linha comgit --no-pager …(duas coisas que o matcher recusa: comando composto não casa com um padrão, e a flag antes do subcomando quebra ogit status *). Allow-list ganhougit status/git log/git diff; a skill (template + cópia) deixa explícito: um comando por chamada Bash (sem bloco multi-linha) egitpuro sem--no-pager(4º veneno).
0.18.1 - 2026-06-26¶
Adicionado¶
slugnoapp.json— fonte única do identificador do app (constituição 0.14.32): o slug (pasta no bucketdocs-sites/, path dosite_url, nome do.docx) vivia duplicado emmkdocs.yml,docs.yml, CLAUDE.md e na skill de release, mas não noapp.json, seu lar natural. Agora o template doapp.jsontemslug(kebab-case, "para sempre"); odocs.ymlderiva dele o target do rclone e osite_url(em vez de hardcodar<app>); oexport_docxusa-o no nome do.docx(fallback p/ o diretório-raiz); e ovalida-frontmatterconfere que osite_urldomkdocs.ymlbate com oslug(igual ao cross-check decliente:). Opcional p/ app legado (migração oportunista).- Gate de CI da lista de download da
/xadm-docx(scripts/checa-export-download.py): confere que a lista hardcoded de arquivos baixados (skill + página) bate com o pipeline publicado (scripts/export-docx/+ reference/logo). Fecha o loop §6 doadmonitions.pyque defasou a lista.
Alterado¶
-
Export
.docx: nome de entrega auto-descritivo (constituição 0.14.31): sem 2º argumento, oexport_docx.pygera<slug>-<nível>[-v<versão>].docxna raiz do repo (antes:projeto.docx/pre-projeto.docxgenérico, que colidia entre apps/releases na entrega). slug = nome do diretório-raiz; nível = pre-projeto|projeto (pelo caminho); versão =git describe --tags→VERSION→ omitida; fallback genérico se não derivar. A skill/xadm-docxe a página passam a usar o default; o.gitignoredo app (e ogitignore-flutter) ganha/*.docx(root-anchored — não pega um pré-projeto.docxcommitado, §1.3). Correção junto: o download fresco da skill não baixava oadmonitions.py(passo do pipeline desde 0.14.26) — incluído na lista. -
Site central: ordem das abas, home e layout da Aplicações. Abas do topo reordenadas (Mudanças foi para o fim: Início · Documentação · Engenharia · Transversais · Aplicações · API Reference · Mudanças); a home ganhou um item explicando Transversais (IA, infraestrutura, glossário) com link; e as páginas Aplicações e API Reference (conteúdo montado via JS, sem headings no markdown → TOC vazio) receberam
hide: toc, colapsando a coluna direita vazia que deixava o layout "agrupado". Só apresentação do site central; nada de artefato rastreado.
Corrigido¶
-
App publicado só em
dev/aparecia em "Outros (sem app.json)" (constituição 0.14.33): no layout versionado (decisão 0004), o central lê aapp.jsonda raiz do bucket (docs-sites/<slug>/app.json), mas odocs.ymlsó a sincronizava em<slug>/dev/— e o passo que mexe na raiz é release-only (publica-versao.pynem toca na app.json). Logo, app que só deu push em master nunca categorizava (caía em "Outros"). Odocs.ymlagora fazrclone copyto site/app.json …/<slug>/app.json(aditivo) em todo push. Caso real:thoms-integracao-produto-ecom. -
Release sem prompt — inspeção de arquivo pela ferramenta
Read, não por shell (constituição 0.14.30): a skill/xadm-releasemandava ler oCHANGELOG.mdmas não dizia como, e uma inspeção com pipe (sed … | grep,rtk grep … | head) disparou o único prompt de um release (pipe é recusado por princípio, fora do allow-list). A skill (template + cópia deste repo) agora manda inspecionarVERSION/CHANGELOG/diff pela ferramentaRead(ougit diff), nunca porsed/grep/cat/rtk grep/| head— 3º veneno documentado. OReadnão precisa de allow-list nem confirma.
0.18.0 - 2026-06-26¶
Adicionado¶
- Gate de dogfood das skills no CI (
scripts/checa-dogfood.py): somos a vitrine, então as skills instaladas (.claude/skills/<nome>/SKILL.md) têm de bater com o template canônico (templates/<nome>-skill.md). O CI falha em drift — descoberto via loop §6: o próprio repo rodava/xadm-spec//xadm-refine-spec//xadm-plandefasados (sem NNN/segredo/lib-capability) porque as cópias nunca eram re-derivadas. As 3 cópias defasadas foram sincronizadas.xadm-releaseé adaptada por repo e fica de fora do gate.
Alterado¶
-
Spec/refine: verificar capacidade de lib de terceiro na FONTE antes de especificar (constituição 0.14.27): a
/xadm-spec(§1) passa a mandar confirmar, na fonte/changelog da versão em uso, que um recurso de lib/SDK externo do qual a feature depende existe — com o pesquisador de docs da stack (ex.act-flutter-docs-researcher) — antes de especificar; especificar API inexistente é inventar capacidade (§1.2). A dim. 2 (Premissas) da/xadm-refine-specreforça: recurso de lib assumido sem conferir no fonte = achado crítico. Origem: loop §6 do BI Transporte (feature de export xlsx — o researcher confirmou no fonte da lib 4.0.6 que não havia API de imagem/outline, evitando retrabalho). -
Export
.docx: fim do auto-anexo de.mdirmãos (constituição 0.14.28): oexport_docx.pydeixava de fora o que era embutido, mas anexava ao fim todo.mdirmão não-embutido — slurpando páginas que o livro só linka/resume (ex. etapas deprojeto/), gerando conteúdo duplicado e um anexo solto após as Decisões. Agora o.docxé o index + só o que ele embute via--8<--(igual ao site: link não entra, o link já é a relação). Pré-projeto passa a embutir seus anexos no fim doindex.md(cada#ainda ganha quebra de página). Avisado na página Exportar para .docx. -
Export
.docx: links para fora do Word viram texto (constituição 0.14.29): o livro linka legitimamente para ADRs (docs/decisoes/*.md) e outras páginas de nav — funcionam no site, mas no.docx(só o livro) virariam hyperlinks mortos (apontam para um.mdinexistente). Um filtro lua (frontmatter.lua) remove o href de links relativos (alvo fora do.docx), mantendo o texto; URL absoluta e âncora interna seguem clicáveis, e imagens (Mermaid) não são tocadas. Guidance na página: cross-ref de conteúdo só-no-site se cita por nome/número, não por link. Docx-only — no site os links funcionam.
0.17.1 - 2026-06-26¶
Adicionado¶
-
Export
.docx: admonitions viram callout (constituição 0.14.26):!!! tipo "Título"/??? tipo(que o pandoc não entende e despejava literal no Word) agora passam por um pré-processador (admonitions.py, antes do pandoc) que os converte em blockquote com título em negrito → estilo Block Text (callout) doxadm-reference.docx, com o tipo como rótulo pt-BR (Nota/Aviso/Dica/…). O autor mantém o!!!na fonte (box nativo no site); só o.docxé adaptado. Docx-only — o site já renderiza admonitions. -
Export
.docx: legenda por diagrama + estilo de código + Título 4–6 (constituição 0.14.25): (a)render_mermaidlê%% caption: <texto>no bloco e emite a legenda na figura do.docx; (b) oxadm-reference.docxe omake_reference.pyganharamSourceCode/VerbatimChar(monoespaçada, fundo e borda leves) — antes```json/códigoinline saíam na fonte do corpo — e Título 4–6 na identidade X-Adm (Verdana azul, decrescente), para o conteúdo que chega a H4+. (c) Documentada a convenção de que fragmento--8<--se autora no nível do embed (modelagem.md em###, não#) — nada rebaixa heading, no site nem no.docx(constituição §5). Só docx/reference; o site já estilizava código e todos os níveis via Material+CSS.
Corrigido¶
- Export
.docx: diagnóstico de Mermaid quando falta o Chrome (constituição 0.14.24): commmdcinstalado mas sem Chrome (de sistema ou do puppeteer — caso típico de CI/runner limpo), o erro dizia "mmdc ausente — instale o mmdc", mandando pro lado errado. Agora distingue "mmdc ausente" de "mmdc presente, Chrome falhou" e aponta as saídas offline (instalar chromium ·PUPPETEER_EXECUTABLE_PATH·npx puppeteer browsers install chrome-headless-shell), sem empurrar pro.ink. Nota de troubleshooting na página Exportar para .docx. (A detecção automática de Chrome do sistema +--no-sandboxjá existia na v0.17.0.)
0.17.0 - 2026-06-26¶
Adicionado¶
-
Pipeline de exportação para
.docxno padrão X-Adm (constituição 0.14.23): centraliza na toolchain o pipeline Markdown→Word (pandoc + reference-doc + Mermaid→PNG), derivado e saneado da PoC poc-pied. Python-only cross-platform (Linux/Windows/macOS) + o filtro nativo do pandoc (frontmatter.lua); reference client-neutral, Mermaid offline por default (mmdc;.inksó opt-in com aviso de confidencialidade, §1.9), pré-processador de--8<--(o pandoc não expande snippets). Distribuído como os validadores: scripts emscripts/export-docx/rastreados nomanifesto.jsone publicados em/toolchain/raw/scripts/export-docx/; o reference+logo publicados crus (binários, fora do manifesto). Inclui a skill/xadm-docx(baixa o pipeline fresco e gera o Word, sem commitar), a página Exportar para .docx e o §1.3 acionável — distinguindo asset de tooling (versionado: css, reference.docx, logo) de documento gerado (não commitado: o.docx, o site). -
Segredo nunca entra no Git — nem no andaime
.ia/(constituição 0.14.22, §1.9): o.ia/é versionado e o-prompt.md(cola crua do pedido) é onde uma credencial real vaza por descuido — aconteceu. Regra nova: nenhum arquivo do repo (.ia/,docs/, config) carrega segredo real; usar placeholder (<TOKEN>,${VAR}) + ponteiro pro valor real (secret da org no Forgejo, env, cofre); se vazou pro histórico, rotacionar (apagar o arquivo não basta — o Git guarda; mesma resposta dada ao vazamento das keysDOCS_S3)..ia/segue versionado (a trilha do andaime é o objetivo — não se gitignora). Eco no fluxo de IA e nas skills/xadm-spece/xadm-plan.
Corrigido¶
-
.ia/NNN-*—NNNé o id da linhagem, não sequência global de arquivos (constituição 0.14.21): as skills/xadm-spece/xadm-plannomeavam.ia/NNN-titulo-spec.md/NNN-titulo-plan.mdsem dizer o significado deNNN, e o agente tratava como sequência global de arquivos, ainda inventando um slug novo por estágio (ex. errado:001-definicao-prompt→002-integracao-...-spec), quebrando a rastreabilidade. Os dois templates passam a especificar queNNNidentifica a linhagem (a feature): prompt, spec e plan da mesma linha compartilhamNNNe slug, mudando só o sufixo de estágio; feature nova recebe o próximoNNN. Mesma definição reforçada no fluxo de IA (dono conceitual da regra). -
Bootstrap de app novo aponta para o mirror público, não o Forgejo privado: o checklist app novo e publicar docs mandavam copiar os templates de
fonte.xadm.biz/.../src/branch/master/templates/…— o Forgejo privado, que dá 404 sem auth (bootstrap de app novo, inclusive por agente de IA, falhava ou improvisava de um clone local defasado). Agora o bootstrap é manifesto-driven como a/xadm-docs: baixa do mirror públicodocs.xadm.biz/toolchain/raw/…, ancorado nomanifesto.jsoncomo fonte única, com nota de quefonte.xadm.bizé edição/hosting (privado) e consumo é sempre via mirror. Mesmo swap no link irmão de versionamento. gitignore-flutterpublicado no mirror: otemplates/gitignore-flutternão estava no manifesto, entãotoolchain/raw/templates/gitignore-flutterdava 404 — o link da engenharia Flutter já apontava pra lá (404 latente) e o de stack apontava pro Forgejo privado. O Dockerfile passa a publicá-lo cru noraw/templates/(artefato copy-once de bootstrap, fora do manifesto — cada app customiza o.gitignore, não há o que re-sincronizar), estack.mdaponta pro mirror.
0.16.10 - 2026-06-25¶
Corrigido¶
- Release sem prompt — pré-flight: o pré-flight ainda pedia autorização porque
(a)
git describe/git rev-parsenão estavam no allow-list e (b) usava substituição de comando$(git describe …), que o matcher de permissão recusa por princípio. Adicionadas as regras de leitura (git status/rev-parse/describe/log/echo) ao allow-list e ao template da skill, e documentado o veneno do$(...)(pegar o valor num passo, usar literal no próximo). Constituição v0.14.20.
0.16.9 - 2026-06-25¶
Adicionado¶
- Release sem prompt (opt-in): seção "Permissões" no template da skill
/xadm-releasedocumentando o allow-list do.claude/settings.json(git add/commit -m/tag/push origin) para o release não pedir autorização — com o trade-off explícito (remove a rede de segurança; a garantia vira a REGRA Nº 2) e a dica de rodar cadagitseparado (compound com pipe não casa o allow-list). Aplicado neste repo (.claude/settings.json+ skill local). Constituição v0.14.19.
0.16.8 - 2026-06-25¶
Alterado¶
- CI Flutter mais rápido (auditoria dos 3 apps + central): a imagem
ci-flutteragora traz clang/lld/cmake/ninja/pkg-config baked (native assets do PowerSync/sqlite3) — o app não instala mais toolchain nativa por run (~2m20 a menos noci.yml). Os templatesci.yml/docs.ymlganharam o passo Cache do pub (~/.pub-cache, key porpubspec.lock) e a nota de que a toolchain vem na imagem. Constituição v0.14.17. Migração: app Flutter re-deriva oci.yml(dropar o passo "Toolchain nativo") via/xadm-docs. - CI de docs mais rápido: o
docs.ymlganhou cache de pip + npm (~/.cache/pip+~/.npm) —mkdocsemermaidpassam a instalar do cache (sem re-download por run, ~20-35s/run). Avaliado e descartado assar numa imagemci-docs: olint-mermaid.mjsé ESM e não resolve módulo global (nem viaNODE_PATH), e imagem nova só pelo mkdocs (~15s) seria over-engineering. Constituição v0.14.18.
0.16.7 - 2026-06-25¶
Alterado¶
- Fábrica de CI (decisão 0003): a fonte das versões passou a ser a
ci-images/matriz-baseline.json— a leitura agregada dosapp.jsondo bucket foi removida. Ela criava um deadlock de bootstrap (oapp.jsonchega ao bucket viadocs.yml, que roda na imagem que a fábrica deveria ter buildado), travando o 1º app Flutter version-pinned (bi-transporte,flutter: 3.44.0). Adoção de versão nova = PR na baseline + push (a fábrica builda) antes de o app apontar ocontainer:. Baseline já comflutter: 3.44.0;ci-images.ymlsem o passo do bucket/DOCS_S3. Decisão 0003 ganhou nota datada;app-novo,engenharia/fluttere o runbook documentam a ordem. §6 do bi-transporte.
0.16.6 - 2026-06-24¶
Corrigido¶
- Fábrica de CI (
ci-images.yml): odocker builddo job falhava com "Cannot connect to the Docker daemon" — o runner da org usa DinD por TCP (sem socket unix no job). O passo de build agora derivaDOCKER_HOSTdo gateway default do job (= daemon do DinD). Runbookfabrica-imagens-ci.mdcorrigido: o socket-do-host do passo 1 só vale para runner sem DinD.
0.16.5 - 2026-06-24¶
Alterado¶
templates/docs.yml: passo Cache do Gradle no bloco de javadoc (paridade com oci.yml; o comentário já prometia mas não entregava) — todo app Java que gera Javadoc no CI de docs ganha o cache ao descomentar. Referência ao JDK deixou de citar versão fixa (era "JDK 21") e aponta a imagemci-java:<v>. Constituição v0.14.16. (§6 de app Java.)
Corrigido¶
- Imagem
ci-java:<v>da fábrica de CI estava semrclone— odocs.ymlde app Java falhava no "Publicar no Garage" (rclone: command not found), apesar de o comentário do template anunciar "toolchain pré-instalada". Adicionado aoci-images/Dockerfile.java(aci-basee aci-flutterjá tinham). Requer rebuild+push daci-java. (§6 de app Java.)
Revertido¶
- O fallback de
/commit.txtvia.git/HEAD(introduzido na v0.16.4) quebrava o build no Coolify:COPY .git/HEADfalha com"/.git/HEAD": not foundporque o Coolify não inclui o.gitno contexto de build — o deploy não subia./commit.txtvolta adev(cosmético conhecido); a receita emcoolify.mdvirou aviso de "não leia o.git/HEADno build — use Build Argument". O fix doexport TAGnorelease.yml(v0.16.4) permanece — é independente.
0.16.4 - 2026-06-24¶
Corrigido¶
templates/release.yml(bloco opt-in de Release no Forgejo): a tag saía como var de shell semexport, então opython3 -cfilho não a enxergava (KeyError: 'TAG'→ corpo do POST vazio → 422). Agoraexport TAG=…. Também documentado que a escrita no repo (permissions: contents: writeouRELEASE_TOKEN) é obrigatória — sem ela dá 403 antes do 422. Constituição v0.14.15. (§6 do maxsul-pied-simples, 1º app a habilitar o bloco.)/commit.txtcarimbavadev: o Coolify não passaSOURCE_COMMITcomo build-arg. ODockerfileagora tem fallback que lê o commit do.git/HEAD(checkout destacado do deploy = SHA);.dockerignoredeixa só oHEADentrar no contexto.
Adicionado¶
- Receita em
infraestrutura/coolify.mdpara carimbar o commit do deploy via.git/HEADquando o Coolify não passaSOURCE_COMMIT(opcional, para app que embute o commit; não é exigência da constituição).
0.16.3 - 2026-06-24¶
Corrigido¶
overrides-main.htmlechangelog.mdagora são rastreados (publicados raw + manifesto) — os dois scaffolds da doc versionada (overrides/main.htmldo banner;docs/changelog.mdda página "Mudanças") são estáticos-idênticos entre apps (como ocheckstyle.xml), mas estavam fora domanifesto.jsone doraw/do site → davam 404 e a/xadm-docsnão detectava defasagem nem conseguia re-baixá-los (só com checkout local do central). Adicionados aoRASTREADOS(auto-publicados emtoolchain/raw/templates/pelo Dockerfile) + destino documentado na skill/xadm-docs. Feedback de uso real via loop §6 (maxsul-pied-simples).
Alterado¶
release.yml: rótulo do artefato Flutter mais preciso — o comentário dizia "Flutter → bundle (apk/web)", misturando Flutter Web (que é deploy via Coolify, não artefato) com mobile/desktop (artefato distribuível → Release). Esclarecido: Web segue o caminho deploy (sem Release/asset); mobile (apk/IPA) e desktop (win/linux/mac) geram Release com asset. O como buildar cada alvo fica a definir quando surgir o 1º app (não normatizar no escuro, §1.2). Feedback de uso real via loop §6.release.yml: token da Release configurável +permissions— oGITHUB_TOKENautomático do Actions costuma ser só Actions (não cria Release → 403). O bloco opt-in agora orienta declararpermissions: contents: writeno job para elevar o auto-token e, se o instance restringir mesmo assim, usar um token dedicado da orgRELEASE_TOKEN(escopowrite:repository) — ocurlusaRELEASE_TOKENse existir, senão oGITHUB_TOKEN. Setup documentado em infraestrutura/forgejo. Feedback de uso real via loop §6 (maxsul-pied-simples).
0.16.2 - 2026-06-24¶
Alterado¶
- Dogfood: o central usa as próprias skills de workflow — instaladas
xadm-spec/refine-spec/plan/work/compound/meta-audit-workem.claude/skills/(antes só axadm-release); oCLAUDE.mdrecomenda o fluxo/xadm-*para feature não-trivial aqui. E o CI do próprio central (build-site.yml) passou a usar a imagemci-base(antes montava git+python via apt a cada run). Mudanças internas — não alteram o site publicado.
0.16.1 - 2026-06-24¶
Corrigido¶
- Build do site no Docker (deploy da v0.16.0) — o
mkdocs build --strictdoDockerfilefalhava porque o COPY seletivo do build stage não trazia da raiz ooverrides/(dotheme.custom_dir— banner de versão antiga) nem oCHANGELOG.md(embutido pela página "Mudanças" via snippet). Adicionados os dois COPY. Build local passava; o contexto enxuto do Docker não.
0.16.0 - 2026-06-24¶
Adicionado¶
- Release do Forgejo com artefato anexado (engenharia/versionamento) — no Forgejo tag ≠ Release: a tag sozinha deixava a aba Releases vazia e o jar sem proveniência. O
templates/release.ymlganhou um bloco opt-in, stack-aware que, para app que distribui artefato (jar/CLI/worker), builda o artefato no CI e publica a Release (tag + notas da seção do CHANGELOG + asset anexado), via API do Forgejo porcurl+secrets.GITHUB_TOKEN. O marco do que foi liberado passa a ser a Release (download reproduzível), não só a tag; o operador baixa o jar da aba Releases. Requer a imagemci-java:<v>(aci-basenão tem JDK) e token com escopo de escrita em release. Skill/xadm-releasee o modelo de distribuição atualizados. Feedback de uso real via loop §6 (maxsul-pied-simples). -
Documentação versionada por release (decisão 0004 / spec 011) — cada tag
vX.Y.Zpublica um snapshot imutável da doc emdocs-sites/<app>/<X.Y.Z>/, e um seletor de versão nativo do Material (extra.version.provider: mike) no topo lista as releases; a raiz abre na maior SemVer; master vira o previewdev/(fora do dropdown). Implementado:templates/mkdocs.yml(provider) +templates/docs.yml(gatilhotags,site_urlpor-versão, publish na subpasta — nuncarclone syncda raiz) + novoscripts/publica-versao.py(montaversions.json+redirect na raiz viarcat, publicado no site) +sync-apps.sh(detectaapi/na versão default). Invariantes duros:app.jsonfica na raiz (senão quebra a fábrica de imagens); forward-only (sem backfill). Spike confirmou na fonte do Material que osite_urlprecisa terminar na versão. Adoção por app é pós-release (odocs.ymlbaixa opublica-versao.pydo site). Dogfood do central e poda de storage: diferidos (spec 012 + brief). -
Página "Mudanças" (CHANGELOG) no site de cada app — o
CHANGELOG.mdda raiz do repo agora aparece no site, em entrada de nav própria no topo, embutido viapymdownx.snippets(--8<-- "CHANGELOG.md", fonte única §1.8/§1.9 — sem cópia, sem editar em dois lugares).templates/mkdocs.ymlganhou a entrada; novo scaffoldtemplates/changelog.md. Aplicado no central (dogfood) e nos pilotos. Casa com a doc versionada (spec 011): cada snapshot de versão mostra o changelog daquela release.
Alterado¶
release.ymlpassa a usar a imagem de CIci-base(decisão 0003) — faltou na migração inicial (sóci.yml/docs.ymltinham ido): rodava emnode:22-bookworm-slime instalava git+python viaapta cada release (~22s). Agora usaci-base(já traz git+python+curl) — só roda ovalida-release.py. −~22s por release.
0.15.0 - 2026-06-22¶
Adicionado¶
- Fábrica de imagens de CI por stack+versão + cache do Gradle (decisão 0003) — o runner Forgejo da org é efêmero e os templates
ci.yml/docs.ymlmontavam o ambiente do zero a cada run (apt/pip, download de JDK egit clonedo Flutter SDK — o maior custo). Agora: imagens por stack+versão servidas no registry do Forgejo (ci-base,ci-java:<v>,ci-flutter:<v>com o SDK baked), montadas por uma fábrica central (ci-images/+.forgejo/workflows/ci-images.yml) que lê o campo novotoolchaindosapp.jsonagregados do bucket (sem token de Git entre repos) + uma baseline, e builda/empurra a matriz. Os templates apontam ocontainer:da stack;actions/cache@v4cacheia o Gradle. Java 25 fixado como LTS da casa (stack). Pré-requisitos de infra (do operador): runner buildando imagem + push pelo endereço interno do registry (dribla o Traefik/499) + token de pacote da org — runbook eminfraestrutura/fabrica-imagens-ci.md(removido depois — ver 0027); o workflow já vem parametrizado (CI_REGISTRY_HOST/REGISTRY_TOKEN). Consolida o feedback de CI/deploy do piloto Flutter (loop §6):subositoquebrado, drift da versão do SDK (fonte única no.fvmrc, semFLUTTER_VERSIONno Coolify), asset gerado antes do test, config pública hardcode vs build-arg. toolchainobrigatório noapp.jsonpara app Java/Flutter (decisão 0003) — o validador agora falha se um app de stack Java/Micronaut não declarartoolchain.java(ou Flutter/Dart semtoolchain.flutter): sem a declaração a fábrica não builda a imagem-versão e ocontainer:do CI aponta para imagem inexistente, quebrando com erro de pull obscuro. Falha cedo e clara em vez de tarde e confusa. App docs-only (ci-base, sem versão) não declara. Documentado no contrato doapp.json(publicar-docs), engenharia e checklist app-novo.- Nota fixa "Build Argument × Environment Variable" no Coolify (infraestrutura/coolify + eco em engenharia/flutter) — pega-pé recorrente (mordeu 3× numa sessão:
FLUTTER_VERSION,SENTRY_DSN): valor que o build embute na imagem (--dart-define,ARG) precisa ser Build Argument (+ "Available at Buildtime"), não env de runtime — como runtime chega vazio ao bundle e o app sobe sem ele, sem erro. Mecânica bidirecional canônica em coolify.md (o sentido inverso — segredo de runtime como build arg vaza comoARG— é o caso dosDOCS_S3_*); eco específico de--dart-defineno flutter.md. Feedback de uso real via loop §6.
0.14.1 - 2026-06-22¶
Adicionado¶
- Conformidade de engenharia no loop de sincronização (constituição §6 +
/xadm-docs) — as regras de engenharia (engenharia/*) eram lidas ao vivo pelas skills mas ficavam invisíveis ao/xadm-docs, que só olhava o manifesto de templates; um app não detectava defasagem de regra de engenharia (e o raw das páginas dava 404). Agora o site publica as páginas de engenharia também cruas (.../toolchain/raw/engenharia/<stack>.md) e o/xadm-docsganhou o passo 3b: ao detectar bump, re-audita o código da stack do app contra a regra vigente (exit codes, libs, observabilidade, gate, definição de pronto) e propõe correções com aprovação — re-auditar, não re-baixar. As páginas não entram no manifesto por desenho (regra viva lida ao vivo ≠ artefato copiado que envelhece). Feedback de uso real via loop §6. - Exit code
4ocupado/lock na taxonomia de CLI/worker (engenharia/java) — a lista0ok ·1parcial ·2config/auth ·3externo não cobria "execução concorrente serializada por lock; nada feito; seguro repetir", desfecho recorrente em job reentrante (cron + gatilho sob demanda dividindo estado). Fixado também que a lista é o contrato canônico da casa (app não inventa código; desfecho novo entra por §6) e que o app documenta a saída no runbook deoperacao/. Feedback de uso real via loop §6 (auditoria maxsul-pied-simples, que já shippouexit 4na sua ADR 0003).
Corrigido¶
- Frontmatter YAML nos 6 templates de skill de workflow (
xadm-spec/refine-spec/plan/work/compound/meta-audit-work) — faltava o bloco--- name / description ---que o Claude Code usa para descobrir e rotear a skill (asxadm-docs/xadm-releasejá tinham). Sem ele, ao copiar verbatim para o app a skill aparecia com adescriptionvirando o comentário-cabeçalho e a invocação quebrava. Feedback de uso real via loop §6.descriptionno formato "o que faz + Use quando…".
0.14.0 - 2026-06-22¶
Adicionado¶
- Conjunto de skills de workflow X-Adm (constituição v0.13.0; decisão do Gustavo, 010c §13):
xadm-spec → xadm-refine-spec → xadm-plan → xadm-work → xadm-compound, modelado no ACT mas próprio da casa. Stack-aware — detectam a stack e seguem a base de conhecimento da Engenharia daquela stack.xadm-worknão commita (REGRA Nº 2);xadm-compounddestila paradocs/e refina a base de conhecimento (loop §6). Templates rastreados no manifesto (o/xadm-docs+ o hook de sessão acusam defasagem). Catálogo na IA e Claude Code. - Skills de workflow reescritas a fundo +
xadm-meta-audit-work(constituição v0.14.0; Gustavo comprou o ACT PRO vitalício e quis o workflow da casa "o mais parecido possível, inclusive os insights"): as 5 skills ganharam a profundidade do ACT adaptada à casa — contrato duro + invariantes que falham o workflow (xadm-work),map_flows/preview_outline/complexidade adaptativa (xadm-spec), regras não-negociáveis + formato de achados + review gate (xadm-refine-spec), pesquisa + style contract + checklist de qualidade (xadm-plan), extração paralela + quality bar (xadm-compound); preservados os inegociáveis X-Adm (não-commitar, destilar p/docs/, gate por stack, pt-BR, definição de pronto §6). Nova/xadm-meta-audit-workaudita um run da/xadm-work(confere não-commit, reconciliação do plano, gate da stack e a linha §6). - Princípio de engenharia "Simplicidade primeiro (sem over-engineering)" na Engenharia — busque o caminho mais simples que satisfaz a necessidade real e presente; abstração/camada/generalização só com necessidade concreta (YAGNI). As skills de workflow ecoam o princípio onde a IA decide escopo/desenho/implementação (
xadm-spec,xadm-plan,xadm-work) e axadm-refine-specganhou a 7ª dimensão de checagem (pega over-engineering já na spec). Irmão de engenharia do "enxuto por padrão" da constituição §1.5 (que vale para a doc).
0.13.0 - 2026-06-19¶
Adicionado¶
- Área Engenharia construída (
docs/engenharia/) — o lado "como construir" do padrão: Stack da casa (matriz app→stack + defaults opinativos), páginas por stack (Java/Micronaut servidor com guarda de fronteiras, Java app simples/CLI/worker, Flutter cliente), CI, gates e testes, e versionamento + health check. (Phases 1–5 do plano de Engenharia.) checkstyle.xml+suppressions.xmlcentralizados (templates/, rastreados no manifesto, publicados em/toolchain/raw/) — fonte única de estilo Java; para de copiar app-a-app.- Seção "Critérios de aceite e teste" no template de etapa (
doc-projeto.md) + marcador "regras arquiteturais guardadas por teste/lint" no livro — a ponte para quem valida.
Alterado¶
- Regras de engenharia movidas da constituição para a área Engenharia (CI por stack, SemVer/release, health check, branch
master, níveis de teste, deploy) com ponteiro curto — a constituição fica menor; o piso da definição de pronto permanece (§6). - Home (
index.md) reescrita — declara a constituição de arquitetura, engenharia e documentação, com os dois caminhos (Documentação · Engenharia); "Tudo numa página" sai do menu de abas; separador no menu entre as áreas de teoria e prática; menções a ZIM/ERP sem adjetivo.
0.12.0 - 2026-06-19¶
Adicionado¶
- Portal em duas áreas distintas: Documentação × Engenharia (constituição v0.12.0; decisão do Gustavo). O site reorganiza em abas (
navigation.tabs): Documentação (constituição, templates, publicar, app-novo, decisões), Engenharia (NOVA) e Transversais (IA, infraestrutura, glossário). As pastas no source espelham:docs/documentacao/,docs/engenharia/,docs/transversais/. A constituição passa a se chamar "Constituição da Documentação e Engenharia"; o site, "Padrões da Plataforma X-Adm". - Área Engenharia + Stack da casa (
docs/engenharia/): matriz opinativa app→stack (Micronaut servidor · +Thymeleaf view simples · Flutter cliente/web/mobile · Java puro app simples/CLI · ZIM legado) + defaults transversais (package-by-feature universal, OkHttp, Gradle KTS, JUnit5+WireMock, BigDecimal, Logback, PostgreSQL+Flyway, picocli). Default forte + exceção documentada. .gitignoreFlutter de referência (templates/gitignore-flutter; feedback via loop §6). Corrige dois bugs que o template legado da org propagava:pubspec.lockagora versionado num app (build reproduzível, sem*.lock); FVM usa.fvmrcversionado (sem o legado!.fvm/fvm_config.json). Referenciado na Stack da casa.
Alterado¶
- Toolchain de máquina migrado para
/toolchain/(constituição v0.12.1). Os artefatos que os apps baixam no CI (validadores, hooks,constituicao-versao.txt,raw/,manifesto.json) saem de/padroes/para/toolchain/— namespace neutro às duas áreas. O Dockerfile dual-publica (/padroes/segue vivo na transição → apps não quebram, re-derivam oportunisticamente). URLs de página antigas (/padroes/X) ganham redirect (mkdocs-redirects) para a área nova. - Campo
entregue: true|falsena etapa (constituição v0.11.1, §2; feedback via loop §6). Separa a entrega da feature dostatusdo doc no Resumo Executivo — permite "design aprovado, build pendente" (status: aprovado+entregue: false). Opcional e retrocompatível: sem o campo, cai no heurístico legado (status aprovado → entregue). Tocavisao-tecnica.py,valida-frontmatter.py, constituição §2 e o template de etapa. - Convenção
.ia/para andaime (constituição v0.11.1, §1.9; feedback via loop §6). Artefato de trabalho (spec, plano) vive em.ia/NNN-*.md, numerado por repo, fora dedocs/(não é conteúdo publicado).
0.11.1 - 2026-06-19¶
Adicionado¶
- Template
Guia do código(templates/guia-do-codigo.md) (constituição v0.11.0, §2/§4; feedback via loop §6 dos pilotos BI Transporte). Página Dev padrão de todo app com código: narrativa autorada da organização do código, com blocos por stack (Java package-by-feature / Flutter feature-first) e o marcador opcional de inventário. xadm.csscentralizado (templates/xadm.css) (constituição v0.11.0; feedback via loop §6). A identidade visual vira fonte única no central (antes duplicada por app) — inclui a regra.md-typeset a:not(.headerlink)(links de conteúdo com burgundy + sublinhado, antes pouco identificáveis). Apps copiam do central; mudança visual num lugar só.
Alterado¶
- Nav do template por ciclo de vida + seções Pré-projeto/Projeto (constituição v0.11.0, §2; feedback via loop §6). O
mkdocs.ymlcanônico passa a recomendar Resumo → Pré-projeto → Projeto → Operação → Dev → Público → Etapas → Decisões → Glossário, com Projeto = especificação e Etapas = execução explicitados (antes "Etapas" virava catch-all). Oresumo-executivo.mdganha o bloco "O que você encontra aqui". - Hook
visao-tecnica.pyremove<!-- GERADO: inventario -->não preenchido (feedback via loop §6). O inventário de classes é preenchido por um passo Javadoc/dartdoc nodocs.ymldo app; sem o passo, o hook compartilhado remove a linha (nunca embarca o marcador cru) — contrato documentado napublicar-docs.md. - Definição de pronto: a verificação exercita a plataforma real (constituição v0.10.15, §6; feedback via loop §6). O piso de teste ganha que a verificação tem que exercitar a plataforma/runtime real (Web, mobile, o build do app), não só
--strict/fixture — o risco recorrente em mudança de migração/toolchain é o gate "passar" sem tocar o sistema de verdade. - Branch principal padronizado em
master(constituição v0.10.17, §5; feedback via loop §6). Os templatesdocs.yml/ci.ymlvinham combranches: [main]e um comentário pedindo ajuste manual — o passo que falhava: num app emmaster, a CI nunca rodava em silêncio (só a release, por tag). Agora o template já vembranches: [master], sem ajuste por repo, e a convenção está na §5. - REGRA Nº 2: skills que commitam por padrão rodam em no-commit (constituição v0.10.16, REGRA Nº 2 / §6; feedback via loop §6). Skill de execução que commita sozinha (ex. ACT
act-workflow-work, que commita por fase) deve ser invocada sempre com a flag de não-commitar (--do-not-commit) — delegar a execução não delega a decisão de commit, que continua do dev ou da/xadm-release. /health:statusaceita liveness truthy (versionamento.md; auditoria do piloto BI Transporte). O valor exato deixou de ser normativo —"ok","UP", etc. valem; o/healthnativo do Micronaut ("UP") está conforme, sem remapear. Contrato segue: 200 + status vivo +versaolegível./healthdo Flutter web responde{status, versao}, não oversion.jsoncru (versionamento.md; auditoria do piloto BI Transporte Flutter). A receita por stack contradizia a §5 (que já exige o shape inclusive no Flutter web): o nginx monta o{status, versao}lendo a versão doversion.json; o/version.jsonsegue como endpoint separado do auto-update.- Versão em runtime nunca é literal hardcoded + receita de Java CLI (
versionamento.md; feedback via loop §6 do xadm-pied-cliente). Princípio explícito: a versão em runtime vem sempre do manifesto do release, nunca de um literal no código (literal mente e drifta, porque a/xadm-releasesó toca manifesto+CHANGELOG). Receita nova de Java CLI (picocli):--versionviaIVersionProviderlendo oversion.properties, não@Command(version="..."). - CI de docs falha cedo se faltar
DOCS_S3_*(templates/docs.yml; feedback do piloto). Guarda no passo "Publicar no Garage": secret vazio/ausente aborta com mensagem clara, em vez do crípticono such hostdo rclone após ~3min de retry.
Corrigido¶
- Banner de supersessão renderizava o número errado (
scripts/valida-frontmatter.py,frontmatter-cabecalho.py,templates/decisao.md; auditoria do piloto).superado-por: 0016sem aspas era lido como octal pelo YAML (0016 → 14), e a decisão obsoleta mostrava "superada pela 14" no site — e o validador checava a cadeia contra a decisão errada, em silêncio. Agora o validador falha sesuperado-por/supersedevier sem aspas, o template mostra"NNNN"e o hook renderiza 4 dígitos.
0.11.0 - 2026-06-18¶
Adicionado¶
projeto/mapeamento.mdopcional (constituição §2/§5): fonte embutida do cap. 4 do livro (mapeamento entrada↔dados — "origem → destino + transformação"), fragmento sem frontmatter, isento no validador (comomodelagem.md). Template novo.
Alterado¶
-
Release: versão Java também em
build.gradle.kts+ bootstrap de release mais visível (constituição v0.10.14; feedback via loop §6 — arquétipo Java CLI). Ovalida-release.pye a skill/xadm-releasepassam a ler a versão degradle.propertiesou do build script (build.gradle.kts/build.gradle, linha de topoversion = "..."), não só de gradle.properties. Oapp-novoganhou a instalação da skill/xadm-release(faltava — só listava a/xadm-docs) e a nota de que oCHANGELOG.mdnasce na 1ª release (inferido dogit log, cobre o histórico);versionamento.mdidem. Fecha o gap do PoC que adotou docs mas pulou o wiring de release. -
Modelo de dados generalizado: banco E/OU contrato de saída (constituição v0.10.13, §2/§5; feedback via loop §6 — arquétipo CLI sem banco).
modelagem.mddeixa de ser "só projeto com banco": vale também para o contrato dos artefatos de saída/integração (JSON, payload), e o cap. 3 do livro acomoda os dois. Corrige um over-fit ao piloto Java/Postgres.
Corrigido¶
- Scripts publicados também em
raw/scripts/(sha do manifesto verificável) (feedback via loop §6). Omanifesto.jsontraz o sha do arquivo cru dos scripts, mas o Dockerfile só publicava a forma carimbada (/padroes/<script>.py, versão na 1ª linha) — o sha nunca batia eraw/scripts/dava 404. Agora o Dockerfile publica também a forma crua em/padroes/raw/scripts/<script>(casa com o manifesto), mantendo a carimbada para execução. Contrato documentado em publicar-docs.
0.10.0 - 2026-06-17¶
Adicionado¶
- Definição de pronto unificada (mudou código → teste + doc no mesmo PR) (constituição v0.10.12, §6; feedback via loop §6). Fecha a assimetria: o §5 já pedia doc-no-mesmo-PR, mas faltava a metade simétrica de teste, e nenhuma era item de fechamento. O §6 passa a fixar o piso unificado — teste proporcional ao risco (mín.: nenhuma regressão sem teste de regressão) + doc do estado atual (mín.: nenhuma mudança de comportamento sem doc); pular metade é decisão explícita (REGRA Nº 3). É disciplina de fechamento + o gate do
ci.yml, não um gate que detecta "doc seguiu código" sozinho. A doutrina detalhada de teste (níveis, e2e em fronteira de segurança, paridade em refactor) fica para a área de Engenharia (futura). Item de fechamento condicionado a "mudou código" emclaude-regras.md/CLAUDE.md.
0.9.2 - 2026-06-17¶
Adicionado¶
- Ghost mode nas mensagens de commit geradas pela IA (constituição v0.10.8, §6;
feedback via loop §6). A mensagem que o agente escreve para o dev copiar sai
sem
Co-Authored-By, "Generated with" ou qualquer atribuição de IA — regra que sobrepõe o default do harness (que carimba co-autoria). Antes só a/xadm-releasetinha ghost mode; agora vale para toda mensagem gerada. Emtemplates/claude-regras.md(REGRA Nº 2), §6 e no CLAUDE.md do central. -
Oferta §6 no fechamento: obrigatória, acoplada ao commit e afirmativa (constituição v0.10.9, §6; feedback via loop §6). A regra existia em prosa mas dependia de o agente lembrar — e ele pulava. Agora a linha §6 (
§6: nada a reportarou§6: candidato a feedback — <o quê>) sai sempre junto da mensagem de commit, na mesma resposta; a ausência dela é sinal visível de passo pulado, não silêncio. Emtemplates/claude-regras.md(REGRA Nº 2), §6 e no CLAUDE.md do central. -
Guia "Como compilar e testar localmente" obrigatório no nível Dev (constituição v0.10.10, §2; pedido do Gustavo). App que executa traz em
docs/dev/um guia de como rodar localmente (modo simulação): pré-requisitos (runtime/versão, Docker p/ banco), passos e recomendações de ambiente. O detalhe fino por stack é da área de Engenharia (futura); aqui fica o requisito-de-existir.
Alterado¶
- §1.9 — destilar é obrigatório; apagar o andaime é recomendado, não obrigatório (constituição v0.10.11; refino pós-auditoria adversarial). Ao concluir uma spec/trabalho, destilar o conhecimento durável para
docs/é obrigatório (inclui o que ferramentas como/compoundgeram nos docs); apagar o artefato.ia/deixa de ser obrigatório — pode ficar versionado como andaime/histórico, desde que o durável já esteja emdocs/. Alinhado emtemplates/claude-regras.md.
Corrigido¶
<!-- HUMANO -->pendente falha o build em páginastatus: aprovado(constituição v0.10.11, §2; auditoria adversarial). Antes sóRECONCILIARfalhava — um livro (projeto/index.md) cheio deHUMANObuildava verde e publicava em branco (o comentário some no HTML). Agora o hookvisao-tecnica.pyfalha simétrico ao RECONCILIAR; em rascunho/em-revisão é WIP, preservado.
0.9.1 - 2026-06-16¶
Corrigido¶
templates/mkdocs.yml(constituição v0.10.2): o comentário do hookvisao-tecnica.pyainda descrevia o modelo antigo (montarvisao/index.mda partir decapitulo:) — contradizia a própria 0.10.x, em que o livro é autorado (projeto/index.md) ecapitulo:é legado. Comentário atualizado para "processa os marcadores do livro autorado". Feedback via loop §6.
Adicionado¶
- Lint de diagramas Mermaid no CI (constituição v0.10.4, §5; feedback via loop
§6). A constituição manda Mermaid (ER em
modelagem.md, fluxos nas etapas), mas diagrama com erro de sintaxe renderiza cru no site e passa pelo--strict— caso real:erDiagramcomukusado como TIPO renderizou cru por 6 versões. Novolint-mermaid.mjsextrai cada bloco``mermaid e rodamermaid.parse(Node + jsdom, **sem Chromium**); diagrama que não compila **falha o build**. Ligado nodocs.yml(apps) e nobuild-site.yml` (central); publicado e carimbado. tipo: livropara a Documentação Completa (constituição v0.10.5, §4; feedback via loop §6). O livro (projeto/index.md) era forçado a se declarartipo: projeto— e não é uma etapa. Agora tem tipo próprio;modulo:não se aplica (o livro é do app inteiro); ovalida-frontmatter.pyesperatipo: livronesse arquivo (e segue isentandoprojeto/modelagem.md, fragmento sem frontmatter).- Template do livro + guia de modelagem (constituição v0.10.6, §5; feedback via
loop §6).
templates/projeto-index.md(ex-visao.md) publica o esqueleto dos 8 capítulos com o embed domodelagem.mde o<!-- GERADO: changelog -->no lugar. E otemplates/modelagem.mdcravou o meio (antes improvisado caso a caso): ER Mermaid só para o overview de relacionamentos; o dicionário por tabela vai em tabela Markdown (coluna | tipo | chave | nulo? | desde | nota), porque oerDiagramnão expressaNOT NULLnem chave composta e fica ilegível em tabela larga; fluxo de dados opcional.
Alterado¶
- Ordem recomendada do nav do app (constituição v0.10.7): no
templates/mkdocs.yml, as duas portas (Resumo Executivo, Documentação Completa) primeiro; depois referência (Dev/API, Operação, Público); depois histórico (Etapas, Decisões); Glossário no fim. - Massa de dados: regra invertida para o modelo privado-por-padrão
(constituição v0.10.3, §5; feedback via loop §6). O
valida-frontmatter.pybloqueia massa de dados (.xlsx/.csv/.sql/...) só emdocs/public/(a superfície exposta), não mais "em tododocs/fora deanexos/" — a mensagem antiga ainda dizia "docs/ é público", desalinhada do §5. Em pasta privada é livre; risco declarado: até o login (spec 007), o portal servedocs/aberto excetoprivado/, então dado confidencial deve ficar emprivado/, sob responsabilidade do autor.
0.9.0 - 2026-06-16¶
Adicionado¶
- Modelo de dados obrigatório para projeto com banco:
docs/projeto/modelagem.md(ER Mermaid consolidado, fonte da verdade do schema, evoluído no mesmo PR), embutido no livro viapymdownx.snippets(constituição §5). checa-modelagem.py: aviso opt-in no CI de código quando o diff mexe numa migration sem tocar omodelagem.md(com suspeita deDROP/"legado" no SQL), tolerante e nunca fatal — publicado no site e carimbado.
Alterado¶
- Documentação Completa autorada no estado atual (constituição v0.10.x): o
livro deixa de ser montado por aspecto das etapas e passa a ser um esqueleto
autorado (
projeto/index.md) que mostra o sistema como ele é hoje, em ordem lógica que termina nos contratos públicos. O hookvisao-tecnica.pysó injeta marcadores (etapas no Resumo Executivo, changelog no livro) e falha o build em<!-- RECONCILIAR -->não resolvido. Configuração entra no livro, deploy fica emoperacao/,capitulo:vira legado. valida-frontmatter.py: supersessão de decisões verificada em cadeia (obsoleta tem que chegar a uma viva — referência quebrada/ciclo/dead-end = erro) edocs/projeto/modelagem.mdisento da validação de frontmatter.
0.8.0 - 2026-06-15¶
Alterado¶
- Constituição v0.9.0 — Documentação Completa por ASPECTO + Resumo Executivo das
etapas (feedback do Gustavo sobre a novo-4): o livro empilhava etapas inteiras
e virava copia-e-cola — porque os capítulos são aspectos (arc42) mas as folhas
são etapas que cruzam todos. Agora o hook extrai as seções
##de cada etapa e as reagrupa por capítulo (o "Modelo de dados" reúne o de todas as etapas, as "Decisões" vêm dedecisoes/), com atribuição "(Etapa NN)" — o livro arc42 que se lê por aspecto. Roteamento pelo nome da seção (canônicas do template de etapa),capitulo:vira fallback. E o Resumo Executivo (index.md) ganha o marcador<!-- etapas -->: o hook injeta "entregue/planejado" do campo novoentrega:(frase de negócio) +statusde cada etapa. Templatesdoc-projeto.mderesumo-executivo.mdatualizados; §2/§4 + app-novo.
Adicionado¶
- Constituição v0.8.3 — REGRA Nº 3: deferir é explícito, não silencioso. A
migração oportunista (§1.6) é legítima, mas vira escotilha silenciosa quando o
"deferi reescrever o conteúdo das etapas para a barra técnica nova" passa como
rodapé. Nova regra de IA (
claude-regras.md+ CLAUDE.md do central): adiar trabalho que o padrão recomenda é uma decisão — declarada em voz alta no fechamento ("deferido X porque…"); e se o repo ainda não publicou (sem slug no ar), o rename/redirect custa zero → fazer agora, não deferir como legado. Gancho na §1.6 e passo de fechamento na skill/xadm-docs. Feedback via loop §6.
Corrigido¶
- Constituição v0.8.2 — manifesto regenerado a cada bump (inclusive text-only):
um bump que mexe só no texto da constituição (como o v0.8.1) não tocava
artefato rastreado, então o
--checkpassava e o campomanifesto.constituicaoficava atrás da versão vigente (0.8.0 vs 0.8.1) — a/xadm-docsque confiasse nele subdetectaria mudanças. Prevenção: ogera-manifesto.py --checkagora falha semanifesto.constituicao != versão vigente; como o CI do central roda--check, todo bump passa a exigir--update(que re-carimba o campo) para passar — o esquecimento fica impossível. §6 atualizado. Feedback via loop §6.
0.7.0 - 2026-06-15¶
Alterado¶
-
Constituição v0.8.1 — etapa de projeto tecnicamente completa (§2): avaliação do doc fundador do piloto (PROJ-2026-001) mostrou que dava pra ler mas não dava pra implementar — faltava o modelo de dados (ER, tabelas, colunas, chaves), os contratos e a config; o template ainda dizia "não liste toda coluna". Novo critério: cada etapa é tecnicamente completa para o seu escopo (um dev/IA que nunca viu o código implementa o que ela introduz/muda), por composição sem repetir (referencia o que herda, re-enuncia só o que altera — §1.4); o livro (Documentação Completa) compõe o desenho completo do sistema. Seções de completude técnica no §2 (arquitetura, modelo de dados ER+tabelas+colunas+chaves, contratos, fluxos+estados, config, qualidade), com a navalha do §1.2 (design e contrato duráveis, não spec linha a linha). Template
doc-projeto.mdreescrito (modelo de dados com colunas/tipos/chaves; seções Contratos/Configuração/Qualidade). -
Constituição v0.8.0 — "Documentação Completa" (o livro) + Resumo Executivo + Etapas do Projeto (feedback do Gustavo sobre a novo-3): a antiga "Visão técnica" era só links e não virou livro — agora o hook
visao-tecnica.pyconcatena o CONTEÚDO das folhas por capítulo (rebaixa títulos, reescreve links/imagens, preserva Mermaid), virando a página Documentação Completa que se lê de cima a baixo. A home do app vira Resumo Executivo (porta do diretor: problema + solução sem jargão → Documentação Completa). Eprojeto/deixa de ser "PROJ-AAAA-NNN": são Etapas do Projeto —NN-titulo.md(duas casas, semPROJ-/ano), avulso emprojeto/diversos.md. §2/§3/§5 + templates (mkdocs nav, doc-projeto, visao-capitulo) + app-novo atualizados. Legado PROJ- aceito até migração oportunista. -
Constituição v0.7.3 — imagem de manual público vai em
docs/public/img/: a guarda depublic/(constituição §5) tratacomo referência, então um print emdocs/img/(fora depublic/) falhava — corretamente: imagem fora depublic/404 quando o portal trancar. Convenção alinhada: prints do manual (que é público) vivem emdocs/public/img/;docs/img/fica para imagens de páginas internas (raras — diagramas são Mermaid). §2/§3 + screenshots-manual.md - template do manual atualizados; mensagem da guarda agora cita imagem. Feedback do bi-transporte-xls via loop §6.
0.6.0 - 2026-06-15¶
Adicionado¶
-
Constituição v0.7.2 — referência de API gerada pelo CI de docs (§5): TODA doc, inclusive a ref de API, é gerada e publicada pelo pipeline de docs, nunca pelo build/deploy do app. O gerador roda antes do
mkdocs build, para dentro dedocs/→ o link é Markdown normal e o--strictvalida (acaba o anti-padrão do HTML-cru que driblava o strict e 404 no preview, e a duplicação por repo). Split de visibilidade: Javadoc/dartdoc (árvore de classes) → internodocs/dev/api/; OpenAPI (contrato) → públicodocs/public/api/embutido viamkdocs-swagger-ui-tag(spec gerado do código, ex. micronaut-openapi — não do app deployado).templates/docs.ymlganha o passo "Gerar referência de API" por stack;templates/public-api.mdnovo; pins no toolchain. Corrige também: odocs.ymlnão instalava omkdocs-print-site-plugin(bug da v0.6.0). Feedback do bi-transporte-xls via loop §6. -
Constituição v0.7.1 — modelo de acesso SIMPLIFICADO (supera o v0.7.0): o v0.7.0 (frontmatter
publico:+ build duplo + dois domínios) foi commitado mas nunca shipou — o Gustavo simplificou:docs/privado por padrão; o público vive emdocs/public/. O que é público é o que está na pastapublic/(public/index,public/manual/,public/api/swagger) — sem marcação por página, sem build separado, um site só, zero infra. O manual do usuário e a API exposta passam a morar empublic/. Guarda: o validador falha se uma página empublic/linkar para fora depublic/(senão quebraria quando o portal trancar).anexos/privado/segue excluído do build (guarda do que é sensível enquanto não há login). Toolchain do padrão (/padroes/) é pública por design (CIs dependem). Decisão do Gustavo: adiar dois domínios e login — viram specs futuras 006 (dois domínios) e 007 (Google auth); a 005 fecha aqui. Fase 3/4 de.ia/005. - Constituição v0.6.1 — validador fresco (validador stale é falso atestado):
os scripts publicados imprimem na 1ª linha a versão da constituição que
implementam (carimbada no publish pelo Dockerfile); a skill
/xadm-docs(e toda validação local) sempre baixa fresco e confere versão impressa == vigente antes de confiar no "0 erros" — nunca reusa cache de turno. Os scripts (valida-frontmatter.py,valida-release.py,frontmatter-cabecalho.py,visao-tecnica.py) entram nomanifesto.jsonnuma seçãoscriptsà parte dos templates: mudar um script obriga bump da constituição (o--checkfalha), o que torna o carimbo confiável. Origem: um validador da era 0.5.5 deu "0 erros" numa migração a 0.5.20, dando conformidade falsa (feedback §6). - Constituição v0.6.0 — Visão técnica (espinha narrativa): os seis níveis são
bons para consulta, mas uma pilha de folhas não se lê como livro. A Visão
técnica é uma página de leitura ordenada montada no build a partir do campo
capitulo:de cada folha (hookscripts/visao-tecnica.py,on_files+File.generated), na ordem de um esqueleto canônico (9 capítulos, negócio → técnico) — restaura o "ler de cima a baixo para entender" e a porta do diretor, sem desfazer a granularidade (as folhas seguem no seu nível). Pluginmkdocs-print-site-plugin(2.8) gera a página-única/PDF. Intro de capítulo opcional (docs/visao/NN-<capitulo>.md,templates/visao-capitulo.md). Origem: auditoria arc42×seis-níveis (.ia/004/005). Fase 1 da evolução (.ia/005).
Alterado¶
- §5 invertido ("docs/ privado por padrão; público em
docs/public/") + as três classes de acesso registradas. §2/§3: o manual do usuário (nível 6) passa a viver emdocs/public/manual/; estrutura ganha a pastapublic/. Validador: reconhecepublic/manual/(tipo manual) e ganha a guarda depublic/(link que sai depublic/falha). - §1.7 reescrito: de "pronto para IA" para "dois modos: consulta E compreensão" — otimizar só a consulta é a regressão típica da doc-de-folhas.
- §4: novo campo
capitulo:(opcional; enum do esqueleto; validador erra em valor inválido — um typo sumiria a folha do livro). - Hook e plugin distribuídos aos apps:
templates/mkdocs.yml(hook + print-site + nav),templates/docs.yml(curl do hook),Dockerfile(publica em/padroes/),requirements.txt(pin do print-site),app-novo.md(passo de adoção). Manifesto regenerado.
0.5.0 - 2026-06-13¶
Adicionado¶
decidido_em:no frontmatter de decisões (constituição §4) — data em que a decisão foi tomada, distinta deatualizado:(última edição). Cumpre a promessa de "retrato datado" quando o registro nasce retroativo numa migração. Opcional no validador (só confere formatoAAAA-MM-DDse presente, para não quebrar decisão legada). Feedback do bi-transporte-xls via loop §6.- Constituição v0.5.20 — 9 mudanças da auditoria arc42×seis-níveis (BI Vantroba; pauta §6, todas avaliadas e aprovadas pelo Gustavo):
- Hook
scripts/frontmatter-cabecalho.pyrenderiza Status · Responsável · Atualizado em no corpo de cada página (o frontmatter sumia no build) e abre decisão obsoleta com aviso de supersessão. Publicado no site e baixado pelodocs.ymldo app (igual ao validador); wired nos doismkdocs.yml+ Dockerfile. Constituição §6. (propostas #3 e #9 — a data vem doatualizado:, sem o plugin git-revision-date, que quebraria com o.gitfora do build.) navigation.path(breadcrumbs) nos doismkdocs.yml— "onde estou" ao chegar via busca. (#6)- Contrato do Javadoc não-órfão:
docs.ymlfalha sesite/api/existir sem nenhuma página linká-lo; receita empublicar-docs.md+ dica no template. Constituição §5. (#1) - Template opcional
templates/riscos.md(risk register transversal, consolida por link). Constituição §5. (#8) - Princípio anti-drift no §1.9 — um fato, um dono canônico; as demais páginas
linkam (ou
pymdownx.snippets), não copiam. (#7) - Mapa ADR-legado → decisão
NNNNna 1ª migração arc42→seis-níveis (app-novo.md+ skill/xadm-docs). (#10)
Validação¶
valida-frontmatter.pyganhou: aviso (não-fatal) sobre identificador de código volátil em título (*Test/Bulk*etc.) (#4); e erro quando a tabela de decisões de um doc de projeto reafirma um status que contradiz a decisão real (a decisão é a fonte única, §1.9/§5) (#5). Coluna "Status" removida do templatedoc-projeto.md.
Alterado¶
- Constituição v0.5.19 — §1.4 e §2 passam a distinguir correção factual de
superação num registro de decisão: fato incidental errado sobre o código
(número, property inexistente) corrige-se em-lugar com nota datada; só
premissa/raciocínio errado (a escolha poderia ter sido outra) exige superação
(
status: obsoleto+superado-por:). Antes, a regra "não se reescreve, supera-se" era absoluta e forçaria superar uma decisão por causa de um número errado. - §2 (Decisão) — "Alternativas consideradas" lista só as realmente
deliberadas; reconstruir alternativas nunca cogitadas (comum em registro
retroativo) é inventar deliberação (§1.2). Eco no template
templates/decisao.md.
0.4.2 - 2026-06-13¶
Adicionado¶
- Template
templates/release.yml(constituição §6) — separa a validação de contrato de release (push de tag →valida-release.py) doci.yml, que na v0.5.16 virou gate de qualidade e colidiu com oci.ymlantigo dos apps (que era o validador de release). Brecha do ho-scraper. Rastreado no manifesto; embutido emtemplates.md.
Alterado¶
- Constituição v0.5.18 (§6, manifesto): redefinição de propósito de um
template rastreado (mudar o que ele é, não só o conteúdo — ex.
ci.ymlrelease → qualidade) exige nota de migração no CHANGELOG e emapp-novo.md; um re-pull cego quebra o app. Regra que faltava quando a 0.5.16 abriu a brecha doci.yml. - Constituição v0.5.17 (§6): explicita dois workflows de código —
ci.yml(gate de qualidade: análise estática + testes, push/PR) erelease.yml(rede de segurança:valida-release.py, push de tag). Skill/xadm-releaserealinhada (aponta orelease.yml, não "o CI" genérico). Mínimo para app só-script: lint sempre (piso), testes da lógica pura onde existir, sem forçar suíte em script trivial. Migração: app comci.ymlantigo de release renomeia pararelease.ymle adota o novoci.yml.
0.4.1 - 2026-06-12¶
Alterado¶
- Constituição v0.5.16 — aviso de falha do CI agora é webhook nativo da org
(Forgejo 15; spec
.ia/001executada e apagada). O passo manual "Avisar falha no Telegram" (if: failure()) saiu dobuild-site.ymle dotemplates/docs.yml— a notificação vira config única na organização, sem YAML por workflow, cobrindo todos os repos.forgejo.mdreescrito (BotFather → getUpdates → webhook Telegram na org com evento Action Failure). Os secretsTELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDda org ficaram órfãos (o webhook guarda token/chat na própria config) — podem ser removidos.
Adicionado¶
- Template
templates/ci.ymlcanônico (constituição §6, "No CI (código)"): gate de análise estática + testes por stack (Java/Gradle./gradlew check, Flutter/Dartanalyze && test, Nodelint && test), com basenode:*para o checkout (lição do ho-scraper) e sem passo de aviso (o webhook da org cobre). Rastreado no manifesto; embutido emtemplates.md. - Constituição v0.5.15 (§2 + screenshots-manual.md): fecha a receita de
screenshots Flutter (estava "em aberto"; spec
.ia/002apagada). Provada no bi-transporte (Vantroba), 9 telas com 1 comando. Correção à direção anterior:takeScreenshotnão roda no desktop Linux → captura comRepaintBoundary.toImage(); alvo é o build Linux desktop (mesmo Skia do web, sem chromedriver); seed via seam de DI em memória; flagkScreenshotMode(equivalente Flutter da dica do.fade); veredito pela existência dos PNGs, não pelo exit code; custo de adoção (suportelinux/, toolchain nativo) documentado. Convenção nova: nome de PNG semântico quando há 1 imagem por tela (dre.png), senão<slug>-NN.
0.4.0 - 2026-06-12¶
Alterado¶
- Constituição v0.5.14 (§5 + versionamento.md): o
/healthdeixa de ser formato livre e ganha shape mínimo ({status, versao}+ opcionais), pois o monitoramento (watchdog) acionou a cláusula "shape vira contrato". Receita de versão em runtime por stack (contribuição §6): Java/Micronaut (task Gradle →version.properties→InfoSourceem/infoe/health+ log nomain; versão do@OpenAPIDefinitioné contrato HTTP à parte), Node (package.json), Flutter web (tem/health, no app ou no nginx), Flutter mobile (sem HTTP → versão in-app viapackage_info_plus), app sem HTTP (log no startup), site estático (/versao.txt). Pertence à área Engenharia (spec 003) — fica em versionamento.md até migrar.
Adicionado¶
- Página Screenshots do manual (
padroes/screenshots-manual.md, constituição v0.5.13 §2): receita reproduzível para os PNGs do manual (regenerável com 1 comando; tooling emscripts/screenshots/, fora dedocs/), separada por stack — web renderizada no servidor (Micronaut/Java) tem a receita provada com shot-scraper +seed.sh+shots.yml(inclui a dica do.fade/modal do Bootstrap); Flutter usa mecanismo próprio (integration_test/flutter drive), com a receita canônica em aberto (shot-scraper não serve em canvas). Ponteiro no templatemanual-usuario.md. Contribuição do loop §6. - Constituição v0.5.12 (§2/§3/§5): define o lar de artefatos não-prosa e
tooling, lacuna achada nos
bi-*-processador-xls. O teste é a publicabilidade (docs/é público): referência não-prosa segura de publicar (schema de contrato, exemplo de payload,.httpsem segredo) vive emdocs/no nível a que pertence (ex.docs/dev/, ao lado dos.md); ferramenta executável, insumo de build e fixture (seed, config de shot-scraper,src/.../resources) ficam fora dedocs/.scripts/reconhecido na árvore do §3 como lar de helpers de dev/ops. - Constituição v0.5.11 (REGRA Nº 2 / §6): no ponto de fechamento de um
trabalho, junto da sugestão de commit, a IA passa a oferecer (em uma
linha) avaliar se o trabalho revelou lacuna/ambiguidade/atrito no padrão —
o loop §6 deixa de depender de o dev lembrar. Sincronizado em
templates/claude-regras.mde noCLAUDE.mddo repo. - Constituição v0.5.10 (§6, "No CI (código)"): o gate de build/test do app
passa a exigir o conjunto análise estática + testes da stack, não só os
testes — lint/estilo que só roda em
check/build(checkstyle,flutter analyze) precisa falhar o PR. Comandos por stack (Java/Gradle →./gradlew check; Flutter/Dart →analyze && test; Node →lint && test) + nota emapp-novo.md. Brecha pega no bi-transporte-xls (violação de checkstyle chegou ao master). Oci.ymlcanônico por stack fica para a migração ao Forgejo 15 (nasce sem o passo manual do Telegram).
Corrigido¶
- Template
xadm-docs-skill.mdganha frontmatter (name/description): sem ele a skill/xadm-docsnão é reconhecida pelo Claude Code, e cada app vinha adicionando na mão — a divergência silenciosa que o template existe para evitar (feedback do loop §6). - Hook
checa-constituicao.shdistingue a direção da diferença de versão: base atrás (migrar), ausente (registrar) ou à frente da publicada (anomalia — central atrasado/versão registrada cedo; não migrar pra baixo). Antes, qualquer diferença mandava "migrar", o que não fazia sentido com a base à frente. Constituição v0.5.9.
0.3.2 - 2026-06-12¶
Alterado¶
- Constituição v0.5.8 (§5):
privado/vira segmento reservado — qualquer pastaprivado/emdocs/é confidencial, não sóanexos/privado/. Os guards passam a casar o padrãoprivado/em vez da string fixa (exclude_docs, guard dosite/nodocs.yml,--excludedo rclone nosync-apps.sh), fechando o risco de um caminho privado novo escapar em silêncio (feedback). A regra de identificadores de código em doc durável passa a citar caminho de arquivo explicitamente: fixar path só quando é contrato (URL, estrutura padrão, config canônica); onde um arquivo mora internamente se descreve pelo papel. - Constituição v0.5.7 (REGRA Nº 2 / §6): a IA passa a sugerir o commit de
forma proativa ao fechar um trabalho e escrever a mensagem pronta para
copiar (sem nunca commitar sozinha). Fixado o formato: assunto de uma linha
(conventional commit) + um parágrafo curto de porquê, sem lista de arquivo
por arquivo. Sincronizado em
templates/claude-regras.mde noCLAUDE.mddo repo.
Adicionado¶
- Constituição v0.5.6 (§6): manifesto de templates — o site publica
padroes/manifesto.jsoncom o hash e a versãomudou_emde cada artefato copiado para os apps (workflowdocs.yml,mkdocs.yml,app.json, hook e skills), e os arquivos crus empadroes/raw/<arquivo>. A/xadm-docspassa a listar mecanicamente quais templates do app estão defasados (mudou_em> versão-base) e re-baixá-los, em vez de inferir da leitura — fechando o modo de falha em que uma mudança de template passou despercebida (ho-scraper).scripts/gera-manifesto.py(--checkno CI,--updateao mexer num template); o--checkfalha o build se um template mudou sem o manifesto refletir, e o--updatecobra o bump da constituição.
0.3.1 - 2026-06-11¶
Adicionado¶
- Constituição v0.5.5 (feedback do bi-transporte-xls): princípio §1.9
"Duas fontes da verdade: o código e
docs/" — artefato de trabalho (spec, plano, prompt) é andaime: destila paradocs/ao concluir e é apagado ("feature pronta" inclui poder apagá-lo sem perda);CLAUDE.mdé roteador, não acervo (§6: regras + versão-base + ponteiros; conhecimento durável vive emdocs/); convenção no §5 sobre identificadores de código em doc durável (citar nome concreto só quando ele é a interface; mecânica interna se descreve pelo papel).
0.3.0 - 2026-06-11¶
Adicionado¶
valida-frontmatter.pycruza ocliente:do frontmatter com oapp.json: app de cliente exige o campo igual em todo doc (pega ausência e typo); app da X-Adm não leva o campo. Semapp.json, não checa (feedback do BI Transporte Vantroba).- Checklist de app novo:
site/no.gitignore(build local gera a pasta; site gerado nunca é commitado) e conferência daconstituicao-versao.txtantes E depois de copiar os templates (release da constituição no meio de uma adoção já passou despercebido). - Versionamento: formato do payload do
/healthdeclarado livre — o contrato é 200 quando vivo + versão legível (oversion.jsondo build Flutter satisfaz); shape só vira contrato se houver monitoramento, e mudará no padrão primeiro. - Aviso de falha do CI no Telegram: passo canônico
if: failure()no final de cada job (mensagem com repo, workflow/job, ref e link da run), com secretsTELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDherdados da organizaçãoxadm. Incluído notemplates/docs.ymle no workflow do site central; passo a passo (BotFather, chat_id, secrets) em infraestrutura/forgejo.md. Sem os secrets, o passo só loga — não falha.
0.2.3 - 2026-06-11¶
Adicionado¶
- Constituição v0.5.4 (§3 e §5): regra "docs/ é público" (nada de dado
real de cliente, credencial ou massa de dados solta — brecha apontada
pelo bi-transporte-xls) e pastas padrão
docs/anexos/(anexo público deliberado) edocs/anexos/privado/(confidencial, ex. planilha-base com dados reais — versionada no Git, nunca publicada), cada uma com README obrigatório (templates canônicos novos). - Três guardas independentes para
anexos/privado/:exclude_docsnomkdocs.ymlcanônico (e no do site central), falha no CI do app se a pasta aparecer nosite/(templates/docs.yml) e--excludeno rclone dosync-apps.sh— o portal recusa servir o caminho mesmo publicado por engano. valida-frontmatter.pybloqueia massa de dados (.xlsx,.csv,.sql,.zip...) emdocs/fora deanexos/;.docxdo pré-projeto continua liberado.
0.2.2 - 2026-06-11¶
Corrigido¶
- Constituição v0.5.3 (§3, nota de legado):
guias/migra por conteúdo, não por rename fixo — manual de usuário →manual/, runbook técnico →operacao/. A pasta legada abrigava os dois níveis; o mapa 1:1 anterior mandava runbook paramanual/errado (brecha apontada pelo ho-scraper). Mesmo ajuste notemplates/xadm-docs-skill.md.
Adicionado¶
- Padrões: seção "Ambiente local (Linux/Ubuntu)" em publicar-docs.md — MkDocs global via pipx (sem venv manual, respeitando o PEP 668 do Ubuntu 24.04+), com as versões pinadas da toolchain; link no checklist de app novo.
0.2.1 - 2026-06-11¶
Corrigido¶
- CI do site: o job de validação quebrava no checkout por falta de node no
container (
python:3.12-slim); o workflow agora usanode:22-bookworm-slimcom git e Python instalados antes do checkout — a falha impedia o próprio deploy (a v0.2.0 não tinha chegado a subir).
0.2.0 - 2026-06-11¶
Adicionado¶
- Constituição v0.4.0: modelo de seis níveis com nível Operação
(runbooks técnicos em
docs/operacao/,tipo: operacao) e nível Dev (API reference gerada +docs/dev/opcional para doc escrita à mão). - Template
runbook.md(O que é / Quando usar / Pré-requisitos / Passos / Verificação / Reversão), embutido na página de templates. - Validação do contrato de release no CI do site (
valida-release.pynobuild-site.yml; push de tagv*confere tag ×VERSION). - Constituição v0.4.1, governança: regra "IA não decide sozinha" — todo
repo que usa Claude Code abre o
CLAUDE.mdcom a regra de confirmar com o usuário antes de implementar quando houver ambiguidade ou mais de um caminho possível. - Constituição v0.5.1, governança: regra "IA não faz commit/push sozinha"
(única exceção: a skill
/xadm-release) e bloco canônicotemplates/claude-regras.mdcom as duas regras de IA, pronto para colar no CLAUDE.md de cada repo. - Constituição v0.5.0, governança: rastreio de versão-base — o app
registra em
constituicao:(docs/app.json) a versão que a doc segue; o site publica a vigente em/padroes/constituicao-versao.txte o fonte cru em/padroes/constituicao.md; skill canônica/xadm-docs(compara, avalia e migra com aprovação) e hookSessionStart(checa-constituicao.sh) que avisa ao abrir o Claude Code no repo. - Rodapé do site passa a exibir a versão vigente da constituição e a versão
do próprio site + copyright X-Adm (hook
scripts/copyright-versao.py, que lê o frontmatter da constituição e o arquivoVERSIONno build). - Página IA e Claude Code (
padroes/ia.md): o que é o Claude Code, as regras de governança de IA, o catálogo de skills da plataforma (/xadm-release,/xadm-docs), o hook de sessão e como adotar num repo; constituição v0.5.2 referencia a página no §6.
Alterado¶
- Nomenclatura definitiva das pastas de documentação:
design/→projeto/,rd/→decisoes/,guias/→manual/(tiposprojeto | decisao | dev | operacao | manual); o validador segue aceitando os nomes antigos até a migração oportunista dos repos. - Templates renomeados:
doc-projeto.md,decisao.md,manual-usuario.md. - Skill de release renomeada de
/releasepara/xadm-release(templatexadm-release-skill.mde a cópia instalada neste repo).
0.1.0 - 2026-06-11¶
Adicionado¶
- Site central de documentação (MkDocs Material, pt-BR, identidade visual X-Adm) com constituição, glossário da plataforma e docs de infraestrutura.
- Constituição da documentação v0.3.6 (níveis, frontmatter, convenções, governança com loop de feedback dos apps).
- Templates canônicos: design doc, RD, guia de usuário,
mkdocs.yml, workflowdocs.yml,app.jsone skill/release. - Pipeline descentralizado: apps publicam no bucket
docs-sitese o site sincroniza (~90s) com log de mudanças por app. - Listagem automática de aplicações e API references via
app.json+index.json. - Validadores
valida-frontmatter.pyevalida-release.py, publicados no site público para consumo pelos CIs dos apps. - Checklist de app novo e página de versionamento/release.
- Health check da imagem e carimbos de versão (
/versao.txt) e commit (/commit.txt).