Constituição da Documentação e Engenharia¶
Status: Rascunho · Atualizado em: 2026-08-04
Define o que documentamos, para quem e qual o mínimo — o piso; o detalhe operacional vive nas áreas de Documentação e Engenharia do site. Templates, toolchain e automações derivam daqui. Se algo conflita com este documento, este vence.
1. Princípios¶
- Documentação perto do código. A doc mora no
docs/do repositório do próprio projeto e muda no mesmo PR que muda o comportamento. Doc longe do código morre. - Documentar interfaces, decisões e uso — não o código. O código já diz o quê. A doc serve para por quê (decisões), como integrar (contratos) e como usar (quem não escreveu). O que dá pra inferir do código não vai pra doc — vira mentira.
- Docs as code. Markdown versionado no Git é a fonte da verdade. Documento gerado
— o site (HTML) e os
.docx/PDF de saída — é gerado, nunca editado à mão nem commitado; a exceção única é o pré-projeto (nível 1), que pode ir em.docxquando a diretoria exigir Word. Não confundir com asset de tooling (oxadm.css, oxadm-reference.docxde estilo, o logo): esse é versionado e rastreável — é insumo do gerador, a fonte canônica do visual; pode ser regerado (make_reference.py), mas é commitado como qualquer template. A receita de geração do Word está em Exportar para .docx. - Decisão é registro; referência é viva. Uma decisão ou doc de projeto é um retrato datado:
não se reescreve, supera-se com outro. Já manuais, runbooks e API reference têm um
responsavel:e precisam refletir a realidade — referência desatualizada é bug. O que é intocável é a decisão e o porquê, não os fatos que ela cita sobre o código. Um fato incidental errado (um número, o nome de uma property inexistente — a decisão continuaria de pé se o fato estivesse certo) é bug de referência: corrige-se em-lugar, com nota datada no corpo (> **Correção AAAA-MM-DD:** ...). Só quando o fato errado é uma premissa que sustentava a decisão (a escolha poderia ter sido outra) é que se supera — aí o registro original não mente sobre a decisão, ele documenta uma decisão tomada sob premissa falsa, e isso é história a preservar. - Enxuto por padrão. Docs de projeto curtos; decisões de ~1 página. Seção vazia não existe — ou tem conteúdo útil, ou não está no documento.
- Migração oportunista. Não se migra doc legada por migrar; só o que está sendo tocado. Projeto novo já nasce no padrão. Deferir é explícito, não silencioso: quem adia trabalho que o padrão recomenda declara no fechamento ("deferido X porque…"); e se nada foi publicado ainda (sem slug no ar), faz agora — rename/redirect custa zero antes de publicar (regras de IA, nº 3).
- Dois modos: consulta E compreensão. A doc serve quem busca um fato
(consulta — slug estável como ID público, não renomear sem redirect; cada
##responde uma pergunta; vocabulário controlado; é o que deixa a doc pronta para IA/RAG) e quem quer entender o sistema lendo (compreensão — uma leitura ordenada de cima a baixo, a Documentação Completa, §2). Folha pequena e endereçável ganha na consulta, mas sozinha não ensina o todo; a espinha narrativa devolve o "ler como livro" sem desfazer a granularidade. Otimizar só a consulta é a regressão típica da doc-de-folhas. - Português do Brasil em toda a documentação.
- Duas fontes da verdade: o código e
docs/. Tudo o mais é transitório ou roteador. Quatro consequências:- Andaime, não acervo. Artefato de trabalho (spec, plano, prompt — de qualquer
ferramenta) vive em
.ia/NNN-*.md, fora dedocs/. Ao concluir, destilar paradocs/é OBRIGATÓRIO (decisão →decisoes/, desenho →projeto/, procedimento →operacao/, uso →manual/; o inferível do código se descarta). Apagar o andaime é recomendado, não obrigatório — "feature pronta" = ele poderia ser apagado sem perda. - Segredo nunca entra no Git — nem no andaime. O
-prompt.md(cola crua do pedido) é onde credencial vaza por descuido. Em qualquer arquivo do repo: placeholder (<TOKEN>,${VAR}) + onde o valor vive (secret da org, env, cofre). Vazou para o histórico → o valor está comprometido, rotacione (apagar o arquivo não basta; o Git guarda). - Código e doc não se duplicam, se complementam: código = o quê; doc = porquê, como operar, como usar, como integrar.
- Anti-drift — um fato, um dono. Dentro de
docs/, um fato delicado (contrato, valor, procedimento) tem um dono canônico — o documento do nível mais técnico onde ele vive. As demais páginas linkam ou embutem (pymdownx.snippets), não copiam: cada cópia é um lugar para divergir no próximo refactor. - Fato de outro repo: link, importação no build, ou nada — nunca cópia. O
--8<--não atravessa repositório, mas o build atravessa: odocs.ymlbaixa o arquivo-fato publicado pelo dono para um diretório efêmero (gitignored) e o embute. O fato chega fresco do dono a cada build — a mesma disciplina do validador fresco (§6), e o oposto da cópia, que nasce certa e envelhece calada. Onde um link basta, linka; onde o leitor precisa do fato na mão, importa. Receita em Publicar docs. - Publicar o fato é obrigação do DONO, não do consumidor. Fato que mais de um
repo precisa mostrar (contrato de ingestão da Plataforma de Integração, formato
de um artefato compartilhado) o dono expõe como artefato consumível: o OpenAPI
gerado e/ou um
arquivo-fato em
raw/. Consumidor que copia é sintoma de dono que não publicou — e a regra pune o sintoma se parar no consumidor. Caso real: a Plataforma de Integração gerava OpenAPI e não o publicava; seu contrato vivia espalhado em dez arquivos de prosa; o app específico que o consome re-digitou a tabela de campos, o exemplo e a lista de origens — e virou exemplo-que-mente. O consumidor não tinha o que linkar. Publicar inclui hospedar: o arquivo-fato é fragmento, não página — ele sai do build (exclude_docs:) e quem o mostra é uma página hospedeira do dono, nonav:e com frontmatter, que o embute (o padrão doprojeto/index.mdembutindo omodelagem.md). Fato publicado só noraw/é consumível por máquina e invisível para gente; fato deixado emnot_in_nav:vira página órfã — H1 inventado do nome do arquivo, URL própria e entrada na busca competindo com a página de verdade. - Quem rege a evolução de um repo não vira dono dos fatos dele. Um projeto pode dirigir a evolução de outro (o app específico que puxa as features da Plataforma de Integração) — isso diz quem escreve o PR, não quem possui o fato. O dono continua sendo o repo onde o fato é verdade (o código, o schema, o spec gerado): o projeto-mãe cria o arquivo-fato lá, e o importa aqui. Do contrário um serviço compartilhado passa a ter um dono por consumidor, que é o caos que esta regra existe para evitar.
- Andaime, não acervo. Artefato de trabalho (spec, plano, prompt — de qualquer
ferramenta) vive em
2. Os seis níveis¶
| Nível | Conteúdo | Audiência | Onde vive | Formato |
|---|---|---|---|---|
| 1. Pré-projeto | estudo de caso / viabilidade e regras de negócio: go / no-go | diretoria | no Git, no repo do app | Word (.docx) ou Markdown + Mermaid |
| 2. Projeto | o desenho do projeto, antes de programar | quem implementa hoje e quem mantém em 5 anos | docs/projeto/ do repo do app |
Markdown + Mermaid + frontmatter |
| 3. Decisões | o que foi decidido em cada projeto, e por quê | quem implementa hoje e quem mantém em 5 anos | docs/decisoes/ do repo do app |
Markdown + frontmatter |
| 4. Dev | doc técnica para devs: API reference e afins | dev de manutenção | ref gerada pelo CI de docs (Javadoc/dartdoc → docs/dev/api/; OpenAPI → docs/public/api/); opcional docs/dev/ à mão |
gerada do código no pipeline de docs; Markdown + frontmatter |
| 5. Operação | runbooks técnicos: deploy, recuperação, operações destrutivas, credenciais | dev/ops da X-Adm | docs/operacao/ do repo do app |
Markdown + frontmatter |
| 6. Manual | manual do usuário: passo a passo com prints | usuário final e suporte | docs/public/manual/ (público, §5) |
Markdown, imagens em docs/public/img/ |
A diferença entre os níveis 5 e 6 é operar vs. usar: o manual ensina a usar o app (mesmo quando o usuário é o suporte interno); o runbook ensina a rodar e manter o app em produção — pressupõe acesso a repo/infra e tolera jargão.
A Documentação Completa — o livro (estado atual, uma página)¶
Os seis níveis são ótimos para consulta, mas uma pilha de folhas não se lê como
livro (§1.7). A Documentação Completa é um esqueleto autorado em
docs/projeto/index.md que mostra o sistema como ele é hoje — não a soma das
etapas — numa ordem lógica de raciocínio que termina nos contratos públicos:
- Capa — o sistema numa tela. O livro abre com uma figura de topologia + fluxos principais (quem fala com quem, o que entra e o que sai), com legenda — o leitor vê a forma do sistema antes de ler a primeira frase. Mermaid por padrão; figura de capa pode ser SVG autoral (§5) — o exemplo vivo é a topologia da integração. Para cada fluxo principal, a capa (ou a seção que ela aponta) diz como acompanhar — o mecanismo pelo qual um humano vê se o fluxo rodou ou falhou (tela de status, push, email de erro). Obrigatória em projeto novo; app existente adota por migração oportunista (§1.6): quando tocar o livro.
- Contexto e problema — o que o sistema resolve; escopo e fora-de-escopo.
- Dados de entrada — o que entra (planilha, fila, payload) e suas regras.
- Modelo de dados — o estado atual do schema do banco e/ou do contrato dos
artefatos de saída/integração; embute
projeto/modelagem.md(§5), não o re-digita. - Mapeamento entrada↔dados — como o que entra vira o que se guarda; pode embutir
projeto/mapeamento.md(§5). - Fluxos e processamento — recepção, máquina de estados; a arquitetura interna (camadas/blocos/dependências) entra aqui.
- Conceitos transversais e configuração — idempotência, auth, prefixos, e as variáveis do projeto (acesso a banco, GlitchTip, chaves) que o sistema usa.
- Contratos públicos / API — a conclusão: endpoints, auth, OpenAPI gerado (§5). Tudo antes existe para justificar estas assinaturas.
- Apêndice — Histórico (changelog) — as etapas e decisões, "como chegamos aqui".
O leitor que integra entra direto no cap. 7; na doc interna ele é o fim da
cadeia, não a abertura. O deploy não entra no livro — é procedimento, vai em
operacao/ (runbook).
Factual de fonte; narrativa humana marcada. O livro não re-digita o factual:
o modelo de dados vem do modelagem.md (embed --8<--), a API do OpenAPI gerado, o
changelog do frontmatter (o hook injeta no marcador <!-- GERADO: changelog -->). O
humano escreve o fio narrativo e o "porquê". A LLM redige a prosa que consegue
ancorar nas fontes; onde não consegue, marca para o humano — é ambos, não um ou
outro; o proibido é inventar fato (§1.2), não redigir. Marcadores (sozinhos na
linha; processados por visao-tecnica.py):
- <!-- HUMANO: pergunta concreta --> — ponto onde a LLM defere ao humano; preservado.
- <!-- RECONCILIAR: o quê --> — transição não resolvida (um rename, um legado em
transição); falha o build até reconciliar (dívida silenciosa vira build vermelho).
- <!-- GERADO: changelog --> — o build injeta a trilha de etapas + decisões.
Regra de corte: derivável do código/fonte ⇒ vem da fonte, não se re-digita à mão.
As etapas (projeto/NN-*.md) seguem no nav como histórico detalhado (cada uma
descreve o seu delta); o livro é o estado consolidado. pré-projeto/ e
public/manual/ ficam fora do livro.
Nav recomendado e Resumo Executivo¶
O nav recomendado segue o ciclo de vida (o template mkdocs.yml já vem
nessa ordem): Resumo → Pré-projeto → Projeto → Operação → Dev → Público →
Etapas → Decisões → Glossário. A seção Projeto é a especificação (o livro
+ a spec); Etapas é a execução (o delta de cada etapa ao longo do tempo) — não
confundir as duas. Todo app com código traz uma página Guia do código em dev/
(narrativa autorada da organização do código).
A porta de entrada complementar é o Resumo Executivo (a home/index.md do
app): o problema e como se resolve, em 1–2 parágrafos sem jargão, terminando
em "→ Documentação Completa". É a porta do diretor; o livro é a leitura completa.
Pode trazer o resumo das etapas (o que já foi entregue / o que está planejado):
ponha o marcador <!-- etapas --> e o hook injeta as duas listas a partir do campo
entrega: (frase de negócio) e da entrega de cada etapa. A entrega vem do campo
opcional entregue: true|false — a entrega da FEATURE, distinta do status do
DOC: permite "design aprovado, build pendente" (status: aprovado + entregue:
false). Sem o campo, cai no heurístico legado (status aprovado → entregue;
rascunho/em-revisão → planejado). Etapa nova aparece sozinha, sem lista à mão.
Conteúdo mínimo por nível¶
- Pré-projeto: problema do cliente em 1 frase; regras de negócio envolvidas; nº de clientes impactados; esforço em faixa (S/M/L/XL, não em horas); riscos; alternativas consideradas; recomendação. 3 a 6 páginas — não é TCC.
- Etapa do projeto (doc de projeto): tecnicamente completa para o seu
escopo — teste: um dev (ou IA) que nunca viu o código implementa o que esta
etapa introduz/muda a partir deste doc + das etapas que ele referencia.
Completude por composição, sem repetir: o que já foi desenhado numa etapa
anterior se referencia (link), não se copia; só se re-enuncia o que ESTA
etapa altera (e aí vale §1.4 — corrige fato em-lugar, supera decisão/premissa).
Lido na Documentação Completa (§2), o conjunto das etapas dá o desenho
completo do sistema; a primeira etapa naturalmente é a mais cheia (define a base).
Cubra as seções que seu escopo toca: contexto/escopo (com o que está fora) ·
arquitetura (blocos/camadas/dependências) · modelo de dados — o delta que esta
etapa faz no schema (tabela nova/alterada: colunas, tipos, chaves, índices); o
estado consolidado vive em
projeto/modelagem.md(§5), que esta etapa atualiza no mesmo PR · contratos (API: request/response/status/auth — ou link adev/api-rest; formatos de arquivo/integração: ex. mapeamento de colunas) · fluxos + máquina de estados · configuração (envs/flags que mudam comportamento) · decisões (corpo emdecisoes/) · riscos · qualidade/observabilidade. Schema grande que evolui muito: dicionário de dados vivo emdocs/dev/(snapshot atual) complementa. A navalha (§1.2): completo no nível de design e contrato (duráveis), não spec linha a linha — o que muda toda semana e a mecânica interna são código. - Decisão (registro de decisão): Contexto · Decisão · Consequências ·
Alternativas consideradas. ~1 página: "decidimos X porque Y, considerando Z".
Uma decisão aceita não se reescreve — é superada por outra. Exceção: fato
incidental errado sobre o código se corrige em-lugar com nota datada; só
premissa/raciocínio errado exige superação (§1.4). Alternativas consideradas:
lista só as que foram realmente deliberadas — reconstruir alternativas que
nunca se cogitou (comum em registro retroativo de migração) é inventar
deliberação (§1.2); se não houve, a seção fica curta ou se omite.
decidido_em:registra quando a decisão foi tomada (distinto deatualizado:, que é a última edição) — é o que cumpre a promessa de "retrato datado" quando o arquivo nasce retroativo. Status: decisão nova nascerascunhoe viraaprovadopeloresponsavel:na revisão do PR; registro de decisão já em vigor nasceaprovado. - Dev: documentar interfaces (o que expõe, recebe, mexe) e
chamadas cruzadas (chama X, é chamado por Y). Não colocar comentário que repete
o nome da função. Referência de API gerada — gerada pelo CI de docs, nunca
pelo build/deploy do app (§5): o pipeline de docs roda o gerador da stack a
partir do código e costura o resultado no portal. Split de visibilidade:
Javadoc/dartdoc (árvore de classes, ref interna) →
docs/dev/api/; OpenAPI/Swagger (contrato, público) →docs/public/api/. Onde não há gerador adotado, o doc de projeto cobre as interfaces.docs/dev/é opcional, para doc escrita à mão que só interessa a devs (ex. mapa do código) — não duplicar o README. Também acolhe referência não-prosa publicável (schema, exemplo de requisição) ao lado dos.md(tooling executável e insumo de build ficam fora dedocs/, §5). Rodar localmente — OBRIGATÓRIO para app que executa:docs/dev/traz um guia "Como compilar e testar localmente" (modo simulação na máquina do dev): pré-requisitos (runtime e versão, Docker quando o banco/serviços sobem em container, etc.), passos para subir e testar, e recomendações de ambiente (editor, IA, issues). É o que faz um dev novo rodar o projeto sem garimpar. O detalhe fino por stack é da área de Engenharia — aqui fica o requisito-de-existir; sem build/execução (ex. repo só de conteúdo), não se aplica. -
Runbook: O que é · Quando usar · Pré-requisitos · Passos · Verificação · Reversão. Teste de qualidade: um dev que nunca operou o app consegue executar só seguindo o documento. Operação destrutiva começa com aviso explícito. Separar do doc de projeto: o porquê/como funciona vai no doc de projeto; o runbook é só o procedimento. O livro não é RE-DIGITADO no runbook — é o corolário simétrico de "o deploy não entra no livro" (§2). O que o runbook escreve é o que é de operar: topologia de operação (o que sobe, onde, com que auth — com diagrama, é o que o operador lê primeiro), superfície, variáveis por servidor com os pares que têm de casar, e passos · verificação · reversão. O porquê daquela topologia — arquitetura, fluxo de processamento, modelo de dados, contratos — tem dono fora do runbook.
- Re-digitar é proibido; transcluir não é (§1.9: as páginas linkam ou
embutem). Onde o operador precisa do fato na mesma página, o runbook
transclui do arquivo-dono** (
--8<--) em vez de linkar — o fato não é copiado, é incluído no build, e muda junto com o dono sem ninguém lembrar de nada. Onde um link basta, linka. Exigir link para evitar cópia cobra do leitor um custo que o--8<--não cobra — e produziu um dev revertendo uma migração conforme. - Transclui-se do arquivo-dono, não do livro. O livro é narrativa: ele
próprio embute os fatos (
modelagem.md,mapeamento.md) em vez de possuí-los. Fato que dois documentos precisam mostrar ganha arquivo próprio — o padrão dos snippets do livro (sem frontmatter, headings em###, declarado emexclude_docs:, que o tira do build:not_in_nav:só cala o aviso e deixa o fragmento virar página órfã, com H1 inventado e entrada própria na busca) — e livro e runbook embutem o mesmo dono. Transcluir "a seção do livro" inverte o padrão e entrega ao operador um diagrama autorado para outro leitor: caso real — o livro de um app tem um Mermaid de arquitetura ponta-a-ponta e o runbook, outro de topologia de rede. São documentos diferentes porque são diagramas diferentes, não porque a regra os separou. - Cross-repo: link ou importação no build — nunca cópia. O
--8<--não atravessa repositório, mas o build atravessa (§1.9): onde o operador precisa do fato de fora na mão — e é o caso típico do runbook de implantação, que fica entre dois apps — odocs.ymlimporta o arquivo-fato publicado pelo dono e o embute. Se o dono ainda não publica, é link ou<PLACEHOLDER>— e a dívida é dele, não do consumidor: cobre a publicação em vez de copiar. Nunca cópia, nunca transclusão fingida. Fato re-digitado longe do dono envelhece sozinho; fato transcluído, não. Três regras, cada uma de um defeito real que passou pela revisão humana: - Reversão enumera TODAS as vias, e o que cada desligar isolado NÃO para. Sistema com mais de uma via de descoberta (webhook + varredura, fila + retry) 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.
- Todo passo de Verificação declara o resultado esperado, conferido no código — não no que parece razoável. Mesmo payload ≠ mesma versão: onde a chave-versão é bumpada por escrita (timestamp, sem dirty-check), idempotência prova-se com uma escrita e vários ciclos do consumidor, nunca com duas escritas — o passo invertido dá falso alarme no dia do deploy.
- Valor de contrato externo sem fonte do outro lado é
<PLACEHOLDER>, não exemplo. Fixture nossa não confirma contrato de terceiro: javadoc "ex.:", seed de teste e whitelist concordam entre si porque descendem da mesma suposição. Sem fonte de lá:<PLACEHOLDER>+ bloco "A confirmar" com o efeito real de errar. Exemplo de contrato é lido como contrato por quem implementa.
Dois moldes, mesmo nível: procedimento (operação pontual — o
templates/runbook.md) e implantação (como o sistema fica de pé e como se desliga — otemplates/implantacao.md, que acrescenta topologia de operação, superfície, e variáveis por servidor com os pares que têm de casar). Automagico ≠ procedimento manual: o que roda sozinho (ex. um provider build-time da Central de Apps, cujo "como usar" é a skill e o "porquê" é a decisão) não ganha runbook de "como usar" — sobra só config-de-operador (as envs a setar + como obtê-las) e diagnóstico, que vivem numa seção "Operar" da própria decisão, não num runbook que duplica o design. - Manual de usuário: passo a passo, screenshots, "quando der esse erro, faça isso". Sem jargão técnico, sem nome de tabela. Screenshot é regenerável (tooling emscripts/screenshots/, PNGs emdocs/public/img/— o manual é público, §5; imagem fora depublic/quebra quando o portal trancar), não print manual — receita por stack (web e Flutter) em screenshots do manual; nome do PNG semântico quando há 1 imagem por tela (dre.png), senão<slug>-NN. - Re-digitar é proibido; transcluir não é (§1.9: as páginas linkam ou
embutem). Onde o operador precisa do fato na mesma página, o runbook
transclui do arquivo-dono** (
Transversal (vive no repo central documentacao): esta constituição, templates,
glossário de negócio, documentação da infraestrutura Coolify/Forgejo/Traefik e o
site MkDocs que publica tudo.
3. Estrutura padrão de cada repo de aplicação¶
meu-app/
├── README.md # o que é, como rodar, como buildar
├── Dockerfile # se o app deploya no Coolify — o Coolify builda
├── .dockerignore # ESTE arquivo, do repo; não há buildpack
├── docs/ # PRIVADO por padrão (gating é spec futura)
│ ├── index.md # visão geral do app
│ ├── pre-projeto/ # nível 1: estudo de caso/viabilidade (.md ou .docx)
│ ├── projeto/ # index.md = Documentação Completa (livro, §2);
│ │ # modelagem.md = modelo de dados (§5, projeto com
│ │ # banco); etapas NN-titulo.md (+ diversos.md)
│ ├── decisoes/ # 0001-titulo-da-decisao.md
│ ├── dev/ # opcional: doc à mão só para devs (mapa do código)
│ ├── operacao/ # runbooks técnicos (deploy, recuperação...)
│ ├── public/ # PÚBLICO (§5): o que está aqui é o rosto externo
│ │ ├── index.md # porta de entrada do público
│ │ ├── manual/ # manual do usuário final (passo a passo, prints)
│ │ ├── img/ # imagens do manual público (prints) — dentro de
│ │ │ # public/ para sobreviver ao gating futuro
│ │ └── api/ # API exposta ao público / swagger-ui
│ ├── anexos/ # apoio interno — README.md obrigatório
│ │ └── privado/ # sensível de verdade (dado de cliente): excluído do build
│ └── img/ # imagens de páginas INTERNAS (raras; diagramas são
│ # Mermaid inline). Print de manual vai em public/img/
├── src/ # Javadoc/dartdoc nos comentários
├── scripts/ # helpers de dev/ops (seed, geração de doc/screenshots) — fora de docs/
└── .forgejo/workflows/docs.yml
App deployável leva Dockerfile e .dockerignore no repo, desde o primeiro
commit de código. O Coolify builda o Dockerfile do repo (não há buildpack), e
o ci.yml não builda imagem — então, sem esses dois arquivos, o primeiro a
descobrir que faltam é o deploy em produção. Os dois são uma decisão só e viajam
no mesmo PR que o código que empacotam; é onde vive o health check obrigatório
(§5 → versionamento).
Derive-os dos templates (dockerfile-java, dockerignore-java; app Java/Gradle leva
junto o .gitattributes de gitattributes-java — checkout Windows sem ele deixa o
gradlew com CRLF e o build morre no Alpine) — não copie do repo
irmão, que é como um app fica dois meses com o comentário errado do outro (§1.9). O
detalhe operável, por stack, na Engenharia: Java/Micronaut
e Flutter. Repo que não
deploya (biblioteca, CLI distribuído como artefato) não tem os dois — ausência aqui
não é defasagem.
Nomenclatura legada: repos criados antes da v0.4.0 usam
design/,rd/eguias/(comtipo: design | rd | guia).design/→projeto/erd/→decisoes/são renames 1:1;guias/migra por conteúdo, não por rename fixo — a pasta abrigava dois níveis de hoje: manual de usuário →manual/(tipo: manual) e runbook técnico →operacao/(tipo: operacao). O validador aceita as duas formas; a troca acontece na migração oportunista (§1.6), lembrando que renomear arquivo publicado exige redirect (slug estável, §5). Conteúdo cross-repo: doc/runbook que pertence a outro repo, achado neste durante a migração, é realocado para o repo dono — não absorvido nem relabelado nooperacao/(ou qualquer pasta) deste.
4. Frontmatter obrigatório¶
Todo .md em projeto/, decisoes/, dev/, operacao/ e public/manual/
(ou manual/ legado):
---
titulo: "..."
tipo: projeto | decisao | dev | operacao | manual | livro
status: rascunho | em-revisao | aprovado | obsoleto
responsavel: "Nome <email>" # dono do documento — campo OBRIGATÓRIO
modulo: "..." # ex. combustiveis, transporte, integracao
cliente: "..." # só em app de cliente específico, ex. OnPetro, Vantroba
atualizado: AAAA-MM-DD
decidido_em: AAAA-MM-DD # só em decisão: quando foi TOMADA (≠ atualizado:)
capitulo: building-blocks # LEGADO/opcional: sem função no livro autorado (§2)
entrega: "..." # só em etapa (projeto/): frase de negócio p/ o Resumo Executivo
tags: [...]
---
- Em decisões superadas, marcar
status: obsoletoe acrescentarsuperado-por: "NNNN". Ids de ADR (superado-por:,supersede:) vão entre aspas — sem elas o YAML lê o zero à esquerda como octal (0016 → 14) e o número renderiza/valida errado; o validador falha se vier sem aspas. decidido_em:aplica-se atipo: decisao: registra a data em que a decisão foi tomada, distinta deatualizado:(última edição do arquivo). Decisão nova deve trazê-lo; em registro retroativo (migração), preencher com a melhor data conhecida. Opcional no validador (que só confere o formatoAAAA-MM-DDse presente), para não quebrar decisão legada — preenche-se na migração oportunista (§1.6).moduloé enum fechado:combustiveis | transporte | fiscal | financeiro | estoque | compras | vendas | integracao | comum(novo módulo = PR neste repo). App de cliente que não pertence a nenhum módulo de negócio usaintegracao.tipo: livroé a Documentação Completa (projeto/index.md, §2) — não é uma etapa. Levatitulo/status/responsavel/atualizado(ecliente:em app de cliente);modulo:não se aplica (o livro é do app inteiro). O validador esperatipo: livronesse arquivo e isentaprojeto/modelagem.md(fragmento sem frontmatter, embutido no livro — §5).capitulo:(legado, opcional): roteava a folha num capítulo da Documentação Completa quando ela era montada das etapas. No livro autorado (§2) a ordem vem do esqueleto, entãocapitulo:não tem mais função — é aceito por compatibilidade (o validador confere o enum se presente, mas não exige).manual/epré-projeto/nunca levaram.
5. Convenções¶
- Abrangência do app: todo app é da X-Adm (parte do produto — todos os
clientes do módulo correspondente usam, ex. CIOT no Transporte) ou de
cliente (sob medida, ex. BI Transporte da Vantroba, BI Comercial da
OnPetro). App de cliente leva
cliente:no frontmatter da sua doc; app da X-Adm não leva. A seção Aplicações do site lista os dois grupos separados. - Etapas do Projeto: os docs de
projeto/são as etapas (specs/features implementadas ao longo do tempo) do app — não "projetos" soltos. ArquivoNN-titulo-curto.md(duas casas, ordinal por repo, semPROJ-/ano — a data vive emdecidido_em:/Git). A seção no site chama-se "Etapas do Projeto" (extra/nav). Etapa avulsa que não merece número entra emprojeto/diversos.md(rótulo "Diversos"). Legado: repos pré-v0.8.0 usamPROJ-AAAA-NNN; o validador aceita as duas formas até a migração oportunista — renomear arquivo publicado exige redirect (§1.7). - Decisões:
NNNN-titulo-curto.md, numeração sequencial por repo. - Slugs estáveis: o nome do arquivo é o ID público da página — não renomear sem redirect (quebra busca, links e o futuro RAG).
- Identificadores de código em doc durável: citar nome concreto (classe,
tabela, endpoint ou caminho de arquivo) só quando ele é a interface —
endpoint público, tabela compartilhada, variável de ambiente, caminho que é
contrato (URL publicada, estrutura padrão de pastas, nome canônico de
config). Mecânica interna se descreve pelo papel ("o serviço de reset da
replicação"), não pelo nome — e onde um arquivo mora internamente também
é mecânica: descreva o papel, não fixe o path. Nome ou caminho interno
acopla a doc a renames. Citou e renomeou? A doc muda no mesmo PR (§6).
Em título o acoplamento é pior (envelhece a página inteira): o validador
avisa sobre identificador de classe em título (
*Test,Bulk*etc.). E a tabela de decisões de um doc de projeto não reafirma o status da decisão referenciada — a decisão é a fonte única (§1.9); o validador falha se a tabela contradisser o status real. - Referência de API: gerada pelo CI de docs, dentro de
docs/, antes do build. TODA doc — inclusive a ref de API — é gerada e publicada pelo pipeline de docs, nunca embutida no build/deploy do app (o app pode servir/swagger-uiao vivo como explorador de runtime, mas a fonte publicada é o portal). O CI de docs roda o gerador da stack antes domkdocs builde escreve para dentro dedocs/— assim o link é Markdown normal e o--strictvalida (link quebrado = falha; sem HTML cru driblando o strict, sem cópia pós-build, sem guard de grep). Onde: Javadoc/dartdoc (árvore de classes, interna) →docs/dev/api/, linkada de uma páginadev/; OpenAPI (contrato, público) →docs/public/api/openapi.yaml, embutido pordocs/public/api/index.mdviamkdocs-swagger-ui-tag. O OpenAPI é gerado do código (ex. Java/Micronaut:./gradlew classes+ micronaut-openapi), não do app deployado. Os artefatos gerados são.gitignore. Receita por stack: publicar docs. - Riscos consolidados (opcional): quando a diretoria/ops quer todos os riscos
numa página, um
docs/riscos.mdtransversal consolida por link a seção "Riscos" de cada doc de projeto (não a substitui; o dono do risco é o projeto, §1.9). Templatetemplates/riscos.md. docs/é privado por padrão; o público vive emdocs/public/. O modelo de acesso tem três classes: (1) toolchain do padrão (constituição, validadores, hooks, manifesto, templates raw, em/padroes/…) é pública por design — os CIs dos apps a baixam por curl; (2) docs internas de app (pré-projeto, projeto, decisões, dev, operação, anexos) são privadas; (3) o público de cada app vive numa pasta dedicadadocs/public/—public/index(porta de entrada),public/manual/(manuais do usuário final),public/api/(API exposta / swagger). O que é público é o que está empublic/— sem marcação por página, sem build separado: é uma pasta. Guarda: uma página empublic/não pode linkar para fora depublic/(o validador falha) — quando o portal trancar tudo menos/public/, esses links quebrariam. O que não é doc (ferramenta, insumo de build, fixture) continua fora dedocs/(emscripts/, no fonte, resources). Login (gating de tudo-menos-/public/) e dois domínios são specs futuras (006 dois domínios, 007 Google auth) — por ora há um site só, servido como hoje; nada de infra muda.- Massa de dados e anexos: o validador bloqueia massa de dados (planilha, dump,
banco —
.xlsx/.csv/.sql/...) só emdocs/public/(a superfície exposta); em pastas privadas é livre, pela organização do app (docs/anexos/,projeto/anexos/, etc.; README por pasta recomendado). Risco declarado (pré-login): enquanto não há gating (spec 007), o portal servedocs/aberto, excetoprivado/— segmento reservado sempre excluído do build (exclude_docs, guard dosite/,--excludedo rclone). Logo, dado real de cliente / confidencial DEVE ficar emprivado/(ex.anexos/privado/); fora deprivado/, um arquivo de dados vaza no publish, e isso é responsabilidade do autor — o validador não barra mais (alinhado ao "privado por padrão"; quando o login entrar, vale na prática). - Modelo de dados (projeto com banco OU contrato de dados): projeto que tem
banco ou um contrato de dados de saída/integração (artefatos JSON, payload,
formato de arquivo) mantém
docs/projeto/modelagem.md— o modelo consolidado do estado atual, a fonte da verdade: o schema do banco (ER Mermaid: tabelas, colunas, tipos, chaves) e/ou o contrato dos artefatos (campos, tipos, obrigatoriedade), do qual se gera a implementação (design-first). Toda etapa que evolui o modelo evolui omodelagem.mdno mesmo PR (§1.1): a etapa descreve o delta, omodelagem.mdcarrega o todo. É um arquivo (não um por etapa), embutido na Documentação Completa (§2) por--8<--. O meio (use cada notação para o que faz bem): ER Mermaid só para o overview de relacionamentos (PK/UK, não a lista de colunas); o dicionário por tabela (toda coluna —Coluna | Tipo | Chave | Nulo? | Desde | Nota— com chave composta, índices e CHECK em bullets) vai em tabela Markdown, porque oerDiagramnão expressaNOT NULLnem chave composta e fica ilegível em tabela larga; fluxo de dados (flowchart) é opcional. Relacionamento sem FK física se anota como nota autoral. Reforço opcional no CI de código:checa-modelagem.pyavisa (não falha) se um diff mexe numa migration sem tocar omodelagem.md(publicar docs). - Mapeamento entrada↔dados (opcional): o cap. 4 do livro (§2) pode ter como fonte
um
docs/projeto/mapeamento.md— tabela de "campo de origem → campo de destino + transformação" (ex. resposta da API → artefato de saída; planilha → tabela). Mesma mecânica domodelagem.md: fragmento sem frontmatter, embutido no livro por--8<--, evoluído no mesmo PR que muda o mapeamento. - Fragmento embutido por
--8<--se autora no NÍVEL DO EMBED, não como doc autônomo. Nem opymdownx.snippets(site) nem oexport_docx.py(docx) rebaixam headings — o nível que você escreve é o nível final. Comomodelagem.md/mapeamento.mdentram sob uma seção##do livro, seus títulos começam em###(não#); um#viraria Título 1 no meio do livro e quebraria o aninhamento/sumário (no site e no.docx). - Diagramas: Mermaid inline; PlantUML (
.puml) só quando o Mermaid não dá conta. Comitar a fonte do diagrama, nunca o SVG/PNG renderizado. Fluxogramas emTB(vertical) por padrão: a página tem largura fixa e rolagem vertical infinita — emTBcada linha usa a largura toda, as caixas ficam largas e o texto fica legível sem zoom; emLRo diagrama cresce na horizontal, aperta as caixas e força zoom/scroll lateral. Use labels curtos edirection TBtambém dentro dossubgraph.LR/RLsó para pipeline curto e linear. O CI de docs linta os blocosmermaid(lint-mermaid.mjs:mermaid.parseheadless, sem Chromium): diagrama com erro de sintaxe renderiza cru e passa pelo--strict, então falha o build aqui, como link quebrado (§6); e flowchartLR/RLcom mais de 6 caixas falha pedindoTB— se for mesmo um pipeline curto que cabe, marque o bloco com um comentário%% lint-mermaid: LR-ok. - Figura autoral (SVG) — a exceção de capa: quando a figura é de capa/poster
(ex. a topologia da plataforma numa tela, para a diretoria) e o layout automático do
Mermaid não entrega a composição (grid, rowspan, ilustração), permite-se SVG
autorado à mão — aí o SVG é a fonte (a regra "nunca comitar o render" não se
aplica: não há render, há autoria). Condições: Mermaid-base junto (mesmos nós e
arestas, em admonition colapsado abaixo da figura — fonte semântica, continua no
lint; mudou a topologia, atualiza o base PRIMEIRO e espelha no SVG), paleta e
tipografia da casa (tokens do
xadm.css, Inter) e legenda embutida. Ponto cego declarado: nenhum gate valida o SVG em si — mais uma razão para o base lintado. Receita completa (com as armadilhas de embed): templates §Figura autoral em SVG. - Volta ao portal: o site de cada app tem, no header, link de volta para
o início da documentação principal (
https://docs.xadm.biz/) — no MkDocs Material, é oextra.homepageapontando o logo/título para o portal (já vem nomkdocs.ymlcanônico do repo central). - UI dos apps: todo app que renderiza HTML no servidor (Micronaut Views /
Thymeleaf), mesmo telas só de admin/ops/dev e atrás do gate de auth do
Coolify, segue o layout padrão X-Adm — fragment base único (
head/navbar/scripts), Bootstrap 5, header escuro com logo + nome do cliente e os idiomas visuais da casa (table-striped,badge bg-*,alert-*), tudo na paleta única doxadm.css. Tela interna não é desculpa para visual ad-hoc — consistência de marca e de UX do operador vale para todos. O esqueleto (layout.html,custom-theme.css, logo, favicon) vem do template rastreado — o app só escreve o conteúdo de cada tela. Dois CSS, dois donos (§1.9): ocustom-theme.cssé a identidade da casa — rastreado, verbatim, não se edita por app; oapp.css(public/css/app.css, sempre linkado pelohead) é dos componentes do app — o que só existe ali e o kit não cobre nem deveria. Todo app que usa o kit tem oapp.css, nem que vazio. Falta algo da casa? O caminho é o §6, não editar o kit: fork some do radar do/xadm-docse congela o app numa versão velha — foi o que a lacuna do slot de CSS produziu, em dois apps, com dois contornos diferentes (0.24.2). Detalhe operável e receita em Java/Micronaut. (App sem view server-render — Flutter, serviço headless — não é alcançado; Flutter tem identidade própria em Flutter.) - Engenharia — construção, CI, versionamento, deploy: as convenções de como
construir vivem na área Engenharia, não aqui:
SemVer + release (tag
vX.Y.Z+CHANGELOG, via/xadm-release), versão em runtime (/healthcom shape{status, versao}) e health check obrigatório em versionamento, release e health; gate de CI por stack, branchmastere níveis de teste em CI, gates e testes. A constituição fica com os princípios (definição de pronto, §6); o detalhe operável é na Engenharia. - Glossário: cada repositório define em
docs/glossario.mdo vocabulário do seu domínio (o repo da plataforma define os termos da plataforma; um app de transporte, os termos de transporte que usa). Não duplicar termo de outro glossário — linkar, com URL absoluta (ex.https://docs.xadm.biz/glossario/): os sites MkDocs são separados e não se enxergam por link relativo.
6. Governança¶
- Por documento: o
responsavel:do frontmatter responde pelo conteúdo. Doc sem responsável não chega aaprovado. O frontmatter aparece no corpo: um hook do MkDocs (scripts/frontmatter-cabecalho.py, baixado do site no build, igual ao validador) renderiza Status · Responsável · Atualizado em no topo de cada página e abre decisãoobsoletocom aviso de supersessão — governança que o leitor não vê não governa. - No PR: mudou contrato, schema ou fluxo público → a doc correspondente muda no mesmo PR. Revisão de doc é parte do code review.
- Definição de pronto (mudou código): no mesmo PR, as duas metades da mesma régua:
- (1) Teste proporcional ao risco. Nenhuma regressão sem teste de regressão; a
verificação exercita a plataforma/runtime real (Web, mobile, o build), não só
--strict/fixture; fixture de API externa é CAPTURADA da instância real, não inventada (shape fabricado = falso atestado; provider só marca "ligado" com ≥1 resposta real); o teste não reimplementa a lógica que atesta — teste que refaz no corpo a conta da produção passa por construção e sobrevive a qualquer bug do código real (é a fixture inventada por outro caminho: o oráculo saiu da mesma cabeça que o código, então nada é conferido); o oráculo vem de fora — valor esperado escrito à mão, saída capturada, ou invariante independente; contrato de entrada entre componentes NOSSOS (skill/cliente → endpoint first-party, ex. o broker de setup) tem os campos obrigatórios travados nos DOIS lados — o cliente declara o contrato e o provedor tem teste que rejeita faltante — senão driftam em silêncio (guia dizia(app_id, feature), broker exigia mais → 500 em produção em vários apps antes de alguém notar); deferir é decisão de custo × valor, não racionalização — só no quadrante caro E de baixo valor; sensível → a guarda se escreve agora. - (2) Doc do estado atual. Nenhuma mudança de comportamento sem doc — corrigir os "estados atuais" que mentem: README, runbook, contrato de API, livro e o screenshot do manual (mudou a UI retratada → regenerar o PNG e abrir a imagem para confirmar o elemento novo, não só rodar o gerador). Delta relevante vira etapa nova; o porquê arquitetural vira decisão.
- Pular qualquer metade é decisão explícita (REGRA Nº 3), declarada no fechamento
— nunca silêncio. É disciplina de fechamento + o gate do
ci.yml: detector automático de "doc seguiu código" existe só onde a afirmação da prosa tem um artefato gerado para conferir contra — hoje ocheca-rotas.py(rota REST citada × OpenAPI gerado do controller). Fora dessa classe, quem cobra é a revisão de PR; "verde" não atesta doc atual. O detalhe (níveis de teste, e2e em fronteira, paridade em refactor, o quadrante custo × valor, a armadilha do screenshot) vive em CI, gates e testes e Screenshots do manual; aqui fica o piso.
- (1) Teste proporcional ao risco. Nenhuma regressão sem teste de regressão; a
verificação exercita a plataforma/runtime real (Web, mobile, o build), não só
- No projeto: o checklist de conclusão cobra "documentação atualizada".
- No CI (doc): validação de frontmatter (campos obrigatórios e enums de
tipo,status,modulo,capitulo) + a guarda depublic/(página emdocs/public/não linka para fora depublic/, §5) + cobertura de nav (checa-nav.py: todo.mddedocs/está nonav:ou excluído — o--strictsó INFO-a órfão, então "doc do estado atual" inclui aparecer no índice, não só existir; o mesmo check reprova chave YAML repetida nomkdocs.yml, que o YAML resolve apagando a primeira em silêncio — foi assim que um segundoexclude_docs:apagou a exclusão deprivado/, com todos os gates verdes). Um site só (mkdocs build --strict); login e dois domínios são specs futuras. - No CI (código): o gate por stack —
ci.yml(análise estática + testes, +checa-nav.py, que pega nav-drift no mesmo PR de código, +checa-views.py, shape dos fragments Thymeleaf de quem tem view: o sha do manifesto atesta cópia idêntica do kit de UI, não render que funciona, +checa-rotas.py, contrato-drift de quem gera OpenAPI: rota REST citada na prosa que o spec — derivado dos@Controller, logo a verdade — desmente; roda depois docheck, que é quando o spec existe) erelease.yml(valida-release.py: SemVer ↔ CHANGELOG ↔ tag). Dois pisos do gate: roda com reporter enxuto (verde ≈ 1 linha; verboso só sob falha) e é fiel ao artefato que embarca (teste roda no classpath, o deploy sobe o fat jar/container — o default é eliminar a divergência entre os dois; só onde ela é inevitável o gate exercita o artefato empacotado, não "smoke em prod"). O detalhe — comandos por stack, higiene de saída, fidelidade ao pacote — vive em CI, gates e testes. Templates rastreados:ci.yml,release.yml. - Versão-base da constituição: o
docs/app.jsonde cada app registra, no campoconstituicao:, a versão desta constituição que a doc segue (oCLAUDE.mddo repo repete a informação para a IA; oapp.jsoné a fonte — atualizar sempre os dois juntos). O site publica a versão vigente em texto puro (https://docs.xadm.biz/toolchain/constituicao-versao.txt) e o fonte em.../documentacao/constituicao.md.- Duas versões, não confundir. Esta constituição tem versão própria
(frontmatter
versao:; no rodapé do site como “Constituição vX.Y.Z”; publicada emconstituicao-versao.txt), desacoplada da versão do site/portal que a hospeda (VERSIONna raiz do repo central →/versao.txt, tagvX.Y.Ze CHANGELOG; no rodapé como “Site vX.Y.Z”). A versão-base de um app é sempre a da constituição, nunca a do site. Num app sem a/xadm-docsinstalada (bootstrap pelo link da constituição), pegue a versão deconstituicao-versao.txtou do frontmatterversao:; e num clone local do repo central a versão da constituição é esseversao:— não o arquivoVERSION(esse é do site). Detalhe operacional: versionamento. A skill/xadm-docscompara, mostra o que mudou e oferece a migração; o hookSessionStartdo Claude Code (.claude/checa-constituicao.sh) avisa ao abrir o repo se a doc está defasada. Migrou para uma versão nova → atualiza campo e CLAUDE.md no mesmo PR.
- Duas versões, não confundir. Esta constituição tem versão própria
(frontmatter
- Sincronização de templates (manifesto): a versão-base diz que há
defasagem, mas não quais artefatos mudaram — e ler só o texto da
constituição já deixou passar uma mudança de template (caso real). Por
isso o site publica um manifesto
(
https://docs.xadm.biz/toolchain/manifesto.json) que registra, por artefato copiado para o app (o workflowdocs.yml, omkdocs.yml, oapp.json, o hook e as skills), o hash do conteúdo e a versão da constituição em que ele mudou pela última vez (mudou_em). A/xadm-docsusa o manifesto para listar exatamente quais templates do app estão defasados e re-baixá-los do site (.../toolchain/raw/<arquivo>), sem inferir da leitura. Regra: toda alteração de um artefato rastreado bumpa esta constituição (patch) e regenera o manifesto (scripts/gera-manifesto.py --update); o CI do central valida a integridade (--check) e falha se um template mudou sem o manifesto refletir — o esquecimento fica impossível. Inclusive bump text-only: todo bump da constituição regenera o manifesto, mesmo quando mexe só no texto (sem tocar artefato rastreado) — o--updatere-carimba o campoconstituicaopara a versão vigente. O--checkfalha semanifesto.constituicaoficar atrás da versão vigente (senão o campo mente sobre a "vigente" e a/xadm-docssubdetecta mudanças). O manifesto sinaliza que o arquivo mudou, não distingue um ajuste de uma redefinição de propósito: mudança que muda o que o template é (não só o conteúdo) — ex. oci.ymlque era validador de release virou gate de qualidade — leva nota de migração no CHANGELOG e emapp-novo.md, porque um re-pull cego quebra o app. Renomear/substituir é migração, não atualização. - Conformidade de engenharia (regra viva, não copiada): as regras de como
construir vivem nas páginas de Engenharia e são lidas
ao vivo pelas skills de workflow — não copiadas para o app, então fora do
manifesto por desenho (não há cópia que envelhece; "copiado que envelhece" é a
semântica do manifesto). O risco é o outro: a regra muda e o código do app deixa
de conformar, invisível ao re-pull de templates. Por isso o site publica as páginas de
engenharia também cruas (
.../toolchain/raw/engenharia/<stack>.md) e a/xadm-docs, ao detectar bump, re-audita o código da stack do app contra a regra vigente (exit codes, libs/padrões, observabilidade, gate de CI, definição de pronto) e propõe as correções com aprovação — re-auditar, não re-baixar. A saída é mudança de código/config do app, não substituição de arquivo. - Config do agente no repo (
.claude/settings.json) — versionado enxuto, pessoal fora: osettings.jsoné versionado (todo mundo que abre o repo carrega), com só o compartilhado — hooks (SessionStart),additionalDirectories(caminho relativo) e allow-list de padrões LARGOS que absorvem os comandos comuns (Bash(git *),Bash(mkdocs *),Bash(python3 scripts/*),Bash(node scripts/*),Bash(curl * https://docs.xadm.biz/*)). O pessoal — grants de máquina, caminhos absolutos, experimentos — vai em.claude/settings.local.jsongitignored. Grant hiper-específico no versionado (comando com hash, path de scratchpad) forçagit commit --amendrepetido que troca o hash sob a tag e solta a tag da release — recorrente → alargue o padrão, não cole a variante exata. Regra viva (de engenharia, não copiada): ferramentas - bootstrap app-novo. (Está aqui só como ponteiro — a regra completa é lá.)
- Validador fresco (validador stale é falso atestado): os scripts publicados
(
valida-frontmatter.py,valida-release.py, hooks) imprimem na 1ª linha a versão da constituição que implementam (carimbada no publish). Quem valida localmente — a skill/xadm-docs, ummkdocs/validação manual — sempre baixa fresco (arquivo efêmero, nunca cache de turno anterior) e confere a versão impressa == a vigente ANTES de confiar no "0 erros"; divergência (oudev) = abortar, não atestar conformidade. Os scripts entram nomanifesto.jsonnuma seçãoscriptsà parte dos templates (são baixados frescos, não copiados para o app): mudar um script obriga bump da constituição (o--checkfalha), o que torna o carimbo confiável. Motivo: um validador da era 0.5.5 deu "0 erros" numa migração a 0.5.20, escondendo checks novos — conformidade falsa. - Regras de IA no CLAUDE.md: todo repo que usa Claude Code (ou outra IA)
abre o seu
CLAUDE.mdcom o bloco canônico de regras (templates/claude-regras.md): nº 1 — IA não decide sozinha (ambiguidade ou mais de um caminho → apresentar opções com recomendação e confirmar antes de implementar; decisão às cegas é a forma mais rápida de divergir do padrão) e nº 2 — IA não faz commit nem push sozinha, nunca (única exceção: a skill/xadm-release, que é comando explícito de commit+tag+push; fora isso a IA, ao fechar um trabalho, sugere o commit e escreve a mensagem — assunto de uma linha + um parágrafo curto de porquê, sem lista de arquivos —, mas quem executa é o usuário). Vale inclusive para skills que commitam por padrão (ex. ACTact-workflow-work, que commita por fase): invoque-as sempre com a flag de não-commitar — delegar a execução não delega a decisão de commit. Essa mensagem sai em ghost mode: semCo-Authored-By, "Generated with" ou qualquer atribuição de IA — regra que sobrepõe o default do harness (o autor do commit é o dev; a/xadm-releasejá segue isso). OCLAUDE.mdé roteador, não acervo (§1.9): regras de agente + versão-base + ponteiros paradocs/e README. Conhecimento durável migra paradocs/; conteúdo que vale para mais de um repo sobe para o repo central. E ele é carregado inteiro em TODA sessão — mantê-lo enxuto é economia direta de tokens: backlog/histórico concluído migra para um arquivo-acervo não carregado (ex.BACKLOG-HISTORICO.mdna raiz, linkado do CLAUDE.md, consultável via grep sob demanda); no CLAUDE.md ficam pendências abertas + itens recentes (~10). Caso medido no central: 998→376 linhas ≈ −15k tokens por sessão. O detalhe operacional — o que é o Claude Code, as skills disponibilizadas e como instalar tudo num repo — está em IA e Claude Code. - Fluxo recomendado de dev auxiliado por IA: feature não-trivial segue
/x-desenhar (.ia/NNN-*-prompt.md) → /x-definir → /x-refinar → /x-planejar → /x-implementar fase/tarefa a tarefa → /x-documentar, com revisão humana entre os passos (corrigir o prompt é mais barato que remendar a spec). Ao executar, a IA mantém oplan.mdvivo: marca o que rodou, quando, o que faltou e por quê, e o que é decisão/operação humana (release, deploy, validação). Fechado o ciclo — depois do commit do dev — o fechamento vale sugerir/compact: o durável da sessão já foi destilado emdocs/(é o que o/x-documentarfaz), então compactar o contexto recupera tokens sem perda; a ordem importa — commit antes, senão a mensagem pronta pode ir embora no resumo. Passo a passo em IA e Claude Code. - Brechas no padrão: quem aplica este padrão num app e encontra
ambiguidade, lacuna ou conflito não contorna localmente — decisão
local vira divergência silenciosa. O caminho: peça ao Claude do app um
texto descrevendo o problema (o que travou, que decisão foi tomada às
cegas, sugestão de correção) e cole esse texto numa sessão do Claude
Code no repo central
documentacao, que entende o contexto e corrige a constituição/templates para todos. Esse loop já produziu várias regras desta página — funciona. Para não depender de o dev — nem do agente — lembrar, a reflexão §6 é passo obrigatório e verificável do fechamento, acoplado à mensagem de commit (REGRA Nº 2) na mesma resposta e declarado de forma afirmativa:§6: nada a reportarou§6: candidato a feedback — <o quê>. A ausência dessa linha é sinal visível de passo pulado, não silêncio. - Saída do agente TERSA (estilo caveman) por default: o chat do agente é telegráfico —
corta artigo/filler/gentileza/hedging, prosa conectiva e status óbvio (gate verde não se
narra; nomeia-se só o que falhou, com a saída), não re-descreve o que o diff/tool já
mostram. Nunca comprime o carve-out: conteúdo de documentação (o que se escreve em
docs//site sai em prosa normal pt-BR, §1.8), a mensagem de commit pronta, código, nomes de API/CLI, strings de erro, a linha §6, tabelas de dado e specs/planos.ia/. Terso é o CHAT; o deliverable, nunca. Vale com ou sem ferramenta de token. Detalhe e exemplos: Agentes: estilo terso. - Disciplina de tokens (se o repo usa rtk/caveman): no fechamento §6 o agente mede
(
rtk gain/rtk discover), auto-corrige o próprio uso e roteia o gap sistêmico (recipe de stack → §6; comando sem handler → issue tracker do RTK, não esta constituição). O como (envelopertk errnos gates, comando bare, install/config) vive em Ferramentas de token e Agentes — um fato, um dono (§1.9). Não força onde não há as ferramentas. - Evolução futura (não implementar agora): bot de frescor, dashboard de saúde da doc, evals de qualidade do RAG.
7. Central de Apps¶
O auth.xadm.biz é o Backend de Central de Apps e Autenticação (plano de controle em
build-time); a central.xadm.biz (Flutter web/android, evolução do login.xadm.biz) é o
dashboard de governança. Norma:
- Setup em build-time. A integração com a infra (erros, arquivos, analytics, docs) é
configurada antes do deploy, não amarrada em runtime. O app declara o que usa no
app.json; a skill/xadm-setupprovisiona (via oauth, que tem os tokens admin dos provedores) e grava o resultado client-grade de volta noapp.json, que o build embarca. Sem buscar config no boot. - Broker de setup é M2M. O
/api/setup/**é máquina-a-máquina (o/xadm-setup, ferramenta de dev/CI) — autenticado por token de serviço, não por login humano interativo. A exposição do endpoint é declarada e a força do token casa com ela: fora da rota pública (rede interna/VPN) → token simples basta; público → segredo forte (aleatório) ou rotativo por HMAC-de-data-com-chave, nunca fórmula pública (forjável). A regra de geração do token vive fora de banda (código do servidor + admins), jamais na skill (distribuída a todo repo) ou em doc publicada — a skill conhece só o formato e pede o valor. - Catálogo + identidade. Todo app é entidade de plataforma com
app_idcanônico (kebab), um só namespace com osoauth_access_grants;grupoxadm|cliente. O catálogo é populado pelo setup. - Observabilidade é baseline. Todo app com release tem rastreamento de erros (GlitchTip).
- Runtime só o mínimo: corretores (URLs assinadas/expiráveis — embed Metabase, presign
Garage) no
auth, que autorizam por app (o claim do JWT casa com o app do recurso — 403 cross-app), não só autenticam; feature flags têm protocolo próprio (estado no DB do app,auth= painel — spec 014). - Fronteira de dados. Autenticação/autorização (
clientes, pAbast, acessos de terceiros,auth_methods) = DB doauth; dados de domínio e estado de flags = DB do app.cliente_id= chave canônica noauth, coluna de escopo do claim no app (sem FK cross-DB).cliente_id(chave, minúscula) ≠cliente(rótulo de exibição): oapp.jsondeclara os dois; a plataforma escopa pela chave declarada (validada contra o catálogo, fail-closed) e nunca a deriva do display — conflá-los foi a raiz de um incidente (decisão 0008). - Segredo jamais em
docs/, noapp.json(público — só client-grade) ou no bundle. - O detalhe operável — ciclo de setup, provedores, corretores, dashboard, tokens — vive em central de apps.
8. Plataforma de Integração¶
Toda integração do ecossistema X-Adm — trazer dados do ERP para a nuvem, devolvê-los ao ERP, ou enviar dados a terceiros — segue uma arquitetura da casa. A unidade de organização é o tipo de fluxo de dados, não o cliente: o que é genérico (a cópia do ERP, o transporte) é único e estável; o que diverge (parse, mapeamento) isola-se por peça, com deploy independente. Norma:
- Organiza-se por tipo de fluxo, não por cliente. Colocar a lógica divergente no motor compartilhado ("para atualizar um cliente, redeploya todo mundo") e duplicar o motor por cliente são os dois erros a evitar; separar genérico de divergente é o que os resolve.
- Taxonomia por papel na topologia — ortogonal ao
grupodo §5 (que diz para QUEM o app é): Plataforma de Integração = a cópia fiel do banco do X-Adm + o connector do PowerSync, uma por cliente, e não é porta de ingress de mais nada; app específico = recebe de fora pela porta própria (relatório, webhook/poll) OU da Plataforma de Integração via REST, transforma e é dono das suas tabelas, um por cliente (enviar para fora — API de terceiro — é um caso do app específico, tipo ④ da plataforma); app compartilhado = app específico multi-tenant (1 instância, N clientes) que nasce quando o parceiro é global — não escreve no barramento, conversa com a Plataforma de Integração, que grava; app consumidor = lê para o usuário final (Web ou Mobile). Cada peça é um app da plataforma (§5/§7): repo próprio,app.json,/health, skills. - Owner-writes. No Postgres por cliente — o barramento — cada tabela tem um dono (roda o Flyway e escreve); os demais leem via PowerSync. Sem "mão única" num serviço central: o ponto de integração é o banco, não um dispatcher. (Server-side em Java/Micronaut §Banco.)
- Espelho fiel ao X-Adm. A tabela-espelho (réplica da Plataforma de Integração, estado-desejado do app específico) modela a
entidade do X-Adm, não a do parceiro: a identidade (chave, join) vive em coluna nativa,
conferida contra a spec de entidades do ERP; campo de origem externa entra prefixado
(
<origem>_) e só como rastreabilidade — nunca chave nem critério de join. Deixar a chave do parceiro virar identidade de 1ª classe acopla o espelho ao vocabulário de um terceiro. (Detalhe em Plataforma de Integração §Fronteiras.) - Transporte X-Adm↔nuvem padronizado por direção — muda o processamento e o destino, nunca o transporte: saída em tempo real = push HTTP à Plataforma de Integração; saída em lote = outbox + agente que roteia por tipo; entrada no ERP = tabelas-espelho com máquina de status + cliente PowerSync que importa e devolve o status.
- PowerSync bidirecional é o transporte (connector na Plataforma de Integração): serve os apps consumidor (Web e Mobile, saída) e o cliente Java do lado do ERP (entrada). Entre a Plataforma de Integração e apps específicos que enviam para fora, o transporte é REST — o servidor Java não consome PowerSync. O praticado — connector, write-back, config do PowerSync — em PowerSync.
Fronteira com o §7: o §7 diz como um app liga na infra (broker, app.json, observabilidade);
o §8 diz como as integrações são arquitetadas entre si (tipo de fluxo, Plataforma de Integração/app específico/app compartilhado, transporte).
Sem sobreposição de norma (§1.9).
O contrato preciso de cada papel (o que a Plataforma de Integração é e não-é, os 4 tipos de fluxo, o transporte detalhado) e o desenho concreto/roadmap vivem em Plataforma de Integração.