Todos os erros que esta camada realmente teve foram silenciosos: uma coleção que
o Content descartou porque o nome não era um identificador JavaScript, uma
página que dava 404 porque uma ligação tinha envelhecido, uma navegação que
voltava vazia. Nenhum dos três disse nada — a compilação estava verde e o site
errado. Por isso correm verificações depois da compilação, todas a ler a mesma
coisa: a cache de análise que o Content deixa para trás.

| Verificação                                   | Gravidade |
| :-------------------------------------------- | :-------- |
| Uma coleção sem páginas                        | erro      |
| Uma pasta em conflito com um segmento de URL   | erro      |
| Um `title` ou `description` em falta           | aviso     |
| Uma ligação interna ou âncora partida          | aviso     |
| O que uma secção gerada não conseguiu ler       | aviso     |
| O que cada idioma carrega e o que ficou parado | nota      |

## Porque a gravidade não é uniforme

Um erro custa uma página que ninguém consegue alcançar e que nada no site
alguma vez explicaria — uma coleção vazia é um 404 em todas as páginas de uma
versão. Um aviso custa qualidade: a página continua a ser apresentada, e uma
fonte remota pode desatualizar-se entre um lançamento e o seguinte sem que isso
seja culpa desta compilação. Fazer a compilação falhar porque o repositório de
outra pessoa avançou tornaria uma compilação verde dependente de um terceiro.

Uma **nota** não custa nada: é um estado do site, não um defeito nele. Uma
página por traduzir não é um erro, e reportá-la como aviso ensinaria toda a
gente a passar por cima dos avisos.

## Secções geradas

Uma [secção gerada](/concepts/generated-sections) é verificada como qualquer outra
coleção, nas suas próprias palavras. O que chega a este relatório é uma de três
coisas:

- o **artefacto não estava lá** nessa ref, pelo que a secção não foi construída;
- o tipo **não leu nada dele**, pelo que a secção reclama URL e não serve nenhum;
- o tipo **passou por cima de alguma coisa** — um `$ref` que não resolve num
  documento OpenAPI, um título de changelog que parece uma release mas não é
  analisado como tal e por isso passou a fazer parte da release acima dele.

As três são avisos, e isso não é um compromisso. Tudo o que um artefacto
**local** pode ter de errado — um caminho que não existe, um ficheiro que o tipo
não consegue sequer ler, uma opção que desconhece — já faz a compilação falhar
enquanto a configuração é carregada, muito antes de este relatório existir: a
configuração do próprio site é um erro que a compilação não pode carregar. O que
sobra aqui é ou um artefacto **remoto**, que pode legitimamente ter
desatualizado entre lançamentos, ou uma página que foi desenhada com algo em
falta.

::callout{type="tip" title="Antes eram impressos e perdidos"}
Um artefacto é analisado enquanto a configuração é carregada, por isso um aviso
sobre um `$ref` descartado era impresso antes de o servidor de desenvolvimento
ter acabado de arrancar e já tinha desaparecido do ecrã quando alguém olhava —
enquanto todos os outros achados desta camada esperavam num único relatório.
Agora vêm pelo relatório, e com ele pelo painel
[Checks](/reference/devtools/checks).
::

## O relatório de traduções

Uma linha por idioma, em cada compilação:

```
[duxt] translations
[duxt]   de: 118/121 pages, 3 behind the original
[duxt]     "docs/guides/deploying.md" has no de translation.
[duxt]     "docs/de/concepts/sources.md" has not moved since the original changed.
```

O segundo número precisa de `history: true` na fonte — as datas vêm do git, e
uma fonte que não pediu o seu histórico não tem nenhuma. Sem ele o relatório
conta a cobertura e nada diz sobre o desfasamento, em vez de o adivinhar a
partir da ausência.

Existe por causa de uma falha concreta. O OpenCode traduziu a sua documentação
para dezassete idiomas com um agente em CI, desligou o fluxo de trabalho, e em
lado nenhum ficava dito que as traduções tinham deixado de se mexer. A camada já
sabe, por página, que idiomas a carregam e quando cada ficheiro mudou pela
última vez; um relatório é o que transforma isso em algo que alguém vê.

## O mesmo relatório, como comando

Os achados vivem em dois sítios difíceis de entregar a alguém: um registo de
compilação que já desapareceu do ecrã, e painéis de devtools que só existem
dentro de um servidor de desenvolvimento a correr. O `duxt report` é o terceiro
— os mesmos dados em Markdown no stdout. Vem com o pacote, por isso estender a
camada é tudo o que é preciso para o ter.

```bash
pnpm exec duxt report            # Markdown
pnpm exec duxt report --json     # os mesmos dados, sem desenhar
pnpm exec duxt report --help     # o que aceita e com que código sai
```

Lê o `app.config.ts` do site, os artefactos ao lado dele e as páginas a partir
da cache de análise do Content — **sem servidor**, por isso corre em CI e num
pipe. Sai com `1` num erro e com `0` em avisos, que é a mesma regra de gravidade
que a compilação segue, e com `2` quando não percebeu a linha de comandos — uma
afirmação sobre a tua invocação, não sobre a tua documentação.

Três painéis não têm equivalente aqui, e de propósito: o índice de pesquisa, o
depurador de caminhos e a listagem da cache são perguntas sobre um processo em
execução, e respondê-las a partir de fora seria adivinhar.

::callout{type="warning" title="Reporta a última análise, não este segundo"}
As páginas vêm da cache do Content, por isso uma página escrita desde a última
compilação ou execução de desenvolvimento não está lá. Corre uma das duas
primeiro — o relatório di-lo claramente quando não encontra cache nenhuma, em
vez de reportar todas as coleções como vazias.
::

## O que as verificações sabem e tu não

- **Uma colisão de pastas** só é um problema enquanto um prefixo está ativo. Uma
  pasta de documentação chamada `v2` está bem num site sem versões e é
  inalcançável num versionado, porque o prefixo ganha. A compilação sabe qual é
  o caso, e a mensagem nomeia o ficheiro e oferece a solução.
- **Uma ligação é resolvida como o tema a resolve.** Um caminho absoluto escrito
  numa página é relativo à fonte dessa mesma página, por isso é tentado primeiro
  sob o prefixo da fonte e depois nu. Um verificador que fizesse apenas a forma
  nua reportaria todas as ligações corretas de um site multirrepositório.
- **Uma ligação é resolvida no seu próprio idioma.** Todos os idiomas de uma
  fonte servem caminhos de conteúdo idênticos — a locale fica à frente do URL,
  não na árvore de conteúdo — e as âncoras derivam do texto dos títulos, pelo
  que um título alemão tem uma âncora alemã. Uma verificação que resolvesse só
  pelo caminho confrontaria uma ligação alemã com títulos ingleses e reportaria
  cada âncora correta de um site traduzido. Uma ligação para uma página que um
  idioma não carrega recorre ao original, que é o que o site faz.
- **URL externos não são verificados.** Verificar um poria a rede dentro da
  compilação, e um servidor alheio inacessível não é razão para a tua
  documentação não ser publicada.

## Escreve as ligações como caminhos de documentação absolutos

`/concepts/sources`, não `../2.concepts/2.sources.md`. O prefixo sob o qual uma
página é servida é decidido na compilação, por isso escrever caminhos sem ele é
o que permite que o mesmo Markdown sirva `/guides/deploying` num site e
`/acme/v2.0.0/guides/deploying` noutro. É também a única forma que o verificador
de ligações vê.