Pular para conteúdo

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

  1. 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.
  2. 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.
  3. 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 .docx quando a diretoria exigir Word. Não confundir com asset de tooling (o xadm.css, o xadm-reference.docx de 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.
  4. 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.
  5. 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.
  6. 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).
  7. 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.
  8. Português do Brasil em toda a documentação.
  9. 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 de docs/. Ao concluir, destilar para docs/ é 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: o docs.yml baixa 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, no nav: e com frontmatter, que o embute (o padrão do projeto/index.md embutindo o modelagem.md). Fato publicado só no raw/ é consumível por máquina e invisível para gente; fato deixado em not_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 , 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.

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:

  1. 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.
  2. Contexto e problema — o que o sistema resolve; escopo e fora-de-escopo.
  3. Dados de entrada — o que entra (planilha, fila, payload) e suas regras.
  4. 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.
  5. Mapeamento entrada↔dados — como o que entra vira o que se guarda; pode embutir projeto/mapeamento.md (§5).
  6. Fluxos e processamento — recepção, máquina de estados; a arquitetura interna (camadas/blocos/dependências) entra aqui.
  7. Conceitos transversais e configuração — idempotência, auth, prefixos, e as variáveis do projeto (acesso a banco, GlitchTip, chaves) que o sistema usa.
  8. Contratos públicos / API — a conclusão: endpoints, auth, OpenAPI gerado (§5). Tudo antes existe para justificar estas assinaturas.
  9. 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.

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 a dev/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 em decisoes/) · riscos · qualidade/observabilidade. Schema grande que evolui muito: dicionário de dados vivo em docs/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 de atualizado:, que é a última edição) — é o que cumpre a promessa de "retrato datado" quando o arquivo nasce retroativo. Status: decisão nova nasce rascunho e vira aprovado pelo responsavel: na revisão do PR; registro de decisão já em vigor nasce aprovado.
  • 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 de docs/, §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 em exclude_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 — o docs.yml importa 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 — o templates/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 em scripts/screenshots/, PNGs em docs/public/img/ — o manual é público, §5; imagem fora de public/ 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.

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/ e guias/ (com tipo: design | rd | guia). design/projeto/ e rd/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 no operacao/ (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: obsoleto e acrescentar superado-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 a tipo: decisao: registra a data em que a decisão foi tomada, distinta de atualizado: (ú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 formato AAAA-MM-DD se 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 usa integracao.
  • tipo: livro é a Documentação Completa (projeto/index.md, §2) — não é uma etapa. Leva titulo/status/responsavel/atualizado (e cliente: em app de cliente); modulo: não se aplica (o livro é do app inteiro). O validador espera tipo: livro nesse arquivo e isenta projeto/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ão capitulo: não tem mais função — é aceito por compatibilidade (o validador confere o enum se presente, mas não exige). manual/ e pré-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. Arquivo NN-titulo-curto.md (duas casas, ordinal por repo, sem PROJ-/ano — a data vive em decidido_em:/Git). A seção no site chama-se "Etapas do Projeto" (extra/nav). Etapa avulsa que não merece número entra em projeto/diversos.md (rótulo "Diversos"). Legado: repos pré-v0.8.0 usam PROJ-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-ui ao vivo como explorador de runtime, mas a fonte publicada é o portal). O CI de docs roda o gerador da stack antes do mkdocs build e escreve para dentro de docs/ — assim o link é Markdown normal e o --strict valida (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ágina dev/; OpenAPI (contrato, público) → docs/public/api/openapi.yaml, embutido por docs/public/api/index.md via mkdocs-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.md transversal consolida por link a seção "Riscos" de cada doc de projeto (não a substitui; o dono do risco é o projeto, §1.9). Template templates/riscos.md.
  • docs/ é privado por padrão; o público vive em docs/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 dedicada docs/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á em public/ — sem marcação por página, sem build separado: é uma pasta. Guarda: uma página em public/ não pode linkar para fora de public/ (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 de docs/ (em scripts/, 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ó em docs/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 serve docs/ aberto, exceto privado/ — segmento reservado sempre excluído do build (exclude_docs, guard do site/, --exclude do rclone). Logo, dado real de cliente / confidencial DEVE ficar em privado/ (ex. anexos/privado/); fora de privado/, 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 o modelagem.md no mesmo PR (§1.1): a etapa descreve o delta, o modelagem.md carrega 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 o erDiagram não expressa NOT NULL nem 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.py avisa (não falha) se um diff mexe numa migration sem tocar o modelagem.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 do modelagem.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 o pymdownx.snippets (site) nem o export_docx.py (docx) rebaixam headings — o nível que você escreve é o nível final. Como modelagem.md/mapeamento.md entram 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 em TB (vertical) por padrão: a página tem largura fixa e rolagem vertical infinita — em TB cada linha usa a largura toda, as caixas ficam largas e o texto fica legível sem zoom; em LR o diagrama cresce na horizontal, aperta as caixas e força zoom/scroll lateral. Use labels curtos e direction TB também dentro dos subgraph. LR/RL só para pipeline curto e linear. O CI de docs linta os blocos mermaid (lint-mermaid.mjs: mermaid.parse headless, sem Chromium): diagrama com erro de sintaxe renderiza cru e passa pelo --strict, então falha o build aqui, como link quebrado (§6); e flowchart LR/RL com mais de 6 caixas falha pedindo TB — 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, é o extra.homepage apontando o logo/título para o portal (já vem no mkdocs.yml canô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 do xadm.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): o custom-theme.css é a identidade da casa — rastreado, verbatim, não se edita por app; o app.css (public/css/app.css, sempre linkado pelo head) é dos componentes do app — o que só existe ali e o kit não cobre nem deveria. Todo app que usa o kit tem o app.css, nem que vazio. Falta algo da casa? O caminho é o §6, não editar o kit: fork some do radar do /xadm-docs e 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 (/health com shape {status, versao}) e health check obrigatório em versionamento, release e health; gate de CI por stack, branch master e 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.md o 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 a aprovado. 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ão obsoleto com 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 o checa-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.
  • 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 de public/ (página em docs/public/ não linka para fora de public/, §5) + cobertura de nav (checa-nav.py: todo .md de docs/ está no nav: ou excluído — o --strict só 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 no mkdocs.yml, que o YAML resolve apagando a primeira em silêncio — foi assim que um segundo exclude_docs: apagou a exclusão de privado/, 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 do check, que é quando o spec existe) e release.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.json de cada app registra, no campo constituicao:, a versão desta constituição que a doc segue (o CLAUDE.md do repo repete a informação para a IA; o app.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 em constituicao-versao.txt), desacoplada da versão do site/portal que a hospeda (VERSION na raiz do repo central → /versao.txt, tag vX.Y.Z e 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-docs instalada (bootstrap pelo link da constituição), pegue a versão de constituicao-versao.txt ou do frontmatter versao:; e num clone local do repo central a versão da constituição é esse versao:não o arquivo VERSION (esse é do site). Detalhe operacional: versionamento. A skill /xadm-docs compara, mostra o que mudou e oferece a migração; o hook SessionStart do 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.
  • 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 workflow docs.yml, o mkdocs.yml, o app.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-docs usa 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 --update re-carimba o campo constituicao para a versão vigente. O --check falha se manifesto.constituicao ficar atrás da versão vigente (senão o campo mente sobre a "vigente" e a /xadm-docs subdetecta 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. o ci.yml que era validador de release virou gate de qualidade — leva nota de migração no CHANGELOG e em app-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: o settings.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.json gitignored. Grant hiper-específico no versionado (comando com hash, path de scratchpad) força git commit --amend repetido 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, um mkdocs/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 (ou dev) = abortar, não atestar conformidade. Os scripts entram no manifesto.json numa seção scripts à parte dos templates (são baixados frescos, não copiados para o app): mudar um script obriga bump da constituição (o --check falha), 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.md com 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. ACT act-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: sem Co-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-release já segue isso). O CLAUDE.md é roteador, não acervo (§1.9): regras de agente + versão-base + ponteiros para docs/ e README. Conhecimento durável migra para docs/; 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.md na 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 o plan.md vivo: 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 em docs/ (é o que o /x-documentar faz), 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 reportar ou §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 (envelope rtk err nos 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-setup provisiona (via o auth, que tem os tokens admin dos provedores) e grava o resultado client-grade de volta no app.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_id canônico (kebab), um só namespace com os oauth_access_grants; grupo xadm|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 do auth; dados de domínio e estado de flags = DB do app. cliente_id = chave canônica no auth, coluna de escopo do claim no app (sem FK cross-DB). cliente_id (chave, minúscula) ≠ cliente (rótulo de exibição): o app.json declara 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/, no app.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 topologiaortogonal ao grupo do §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.