O que wiki-lint faz?
Antes da primeira execução, leia
docs/GOTCHAS.mdno kit (falsos positivos conhecidos, notas operacionais).
Uma verificação de saúde somente leitura do wiki (wiki_root em wiki.config.json) mais três ferramentas de reparo. Tudo é biblioteca padrão do Python e funciona da mesma forma no Windows, macOS e Linux. Cada regra específica do wiki (pastas, taxonomia de status, mapeamento de campos, isenções, lista de verbos, padrões de placeholder, limites) reside na configuração, portanto a lógica não muda quando o wiki ganha um novo campo, pasta ou verificação. Projetado para ser rápido mesmo com mais de 3.000 notas (uma execução completa leva segundos, exceto a Verificação 13 que leva cerca de meio minuto nesse tamanho).
Habilidades relacionadas
wiki-ingest,wiki-ingest-book: escrevem as notas que esta habilidade verifica (mesma configuração para decisões de status e roteamento de índice).wiki-synthesize: a Verificação 15 produz sua lista de trabalho; a Verificação 32 audita seus selos.vocabulary-update: o remédio para a Verificação 30.
Layout
skills/wiki-lint/
SKILL.md
scripts/
lint.py # executa as verificações (--only, --out-dir, --self-test)
fix.py # ferramentas de reparo: proposta de índice, backlinks da Verif. 12, promover rascunhos
references/
check-origins.md # POR QUE cada verificação existe (autoridade única)
cli-reference.md # flags, chaves de configuração, arquivos de saída, formato do relatório
wikikit/lint/ # a implementação: context.py, checks_*.py, fixes.py, defaults.json
tests/fixtures/lint/ # mini-wiki sintético exercitando cada verificação + expected_counters.txt
Onde cada tipo de fato reside (nunca duplicar entre eles): references/check-origins.md = por que uma verificação existe; este arquivo = o que ela faz; wikikit/lint/checks_*.py = como é implementada; tests/fixtures/lint/expected_counters.txt = por que o valor da fixture é o que é. Ao final de uma execução ao vivo, a ferramenta imprime o ponteiro para check-origins.md de cada verificação que disparou, para que a motivação chegue quando você estiver decidindo se um acerto é real ou ruído.
Invariantes de engenharia (não quebrar)
- Toda E/S de arquivo passa por
wikikit.textio(UTF-8 em nível de byte, sem BOM, terminações de linha nunca traduzidas). Nunca useopen()em modo texto com tratamento de nova linha padrão: ler e reescrever um arquivo CRLF dessa forma o converte silenciosamente. - Nunca use uma regex multilinha com
$sobre texto que possa conter CRLF:$fica entre o CR e o LF e divide o par. Edite por linhas (frontmatter.set_key,fixes._insert_backlinks). - A configuração é a única fonte da verdade. Quando o wiki ganhar um campo, pasta, status ou isenção, edite
wiki.config.json(seçãolint), não o código. Os padrões residem emwikikit/lint/defaults.json. - Isenção de stub é uniforme.
lint.stub_exempt_checkslista os números das verificações que pulam notas comstatus: stub; leia a lista da configuração, nunca a reafirme em prosa. As Verificações 10 e 11 excluem stubs inline (contagens do sistema de arquivos). - A detecção de placeholder é bloqueada por resolução. Um alvo de wikilink que resolve no índice do vault NUNCA é sinalizado como placeholder, mesmo que seu nome corresponda a um padrão; notas com abreviações legítimas em CAIXA ALTA não devem sinalizar.
- Teste automático antes de enviar mudanças de lógica. Qualquer edição em uma verificação (nova verificação, limite alterado, heurística refatorada) deve executar verde:
python skills/wiki-lint/scripts/lint.py --self-test. Atualizetests/fixtures/lint/eexpected_counters.txtjuntos quando a mudança for intencional. Prove que a fixture discrimina: reverta a mudança de lógica, execute novamente e confirme que o contador se moveu. Uma fixture que passa antes e depois não protege nada. - Um zero é uma medida do detector, não do wiki. Antes de confiar no zero de uma nova verificação, injete um caso positivo e veja se dispara.
- A propriedade do marcador de aviso é posicional, nunca encadeada por regex (Verificação 22). O wiki pode usar tanto
[[Paper]]⚠quanto⚠[[Paper]]. O proprietário de um glifo é o link imediatamente à sua ESQUERDA se o texto alinhado à esquerda terminar com]], caso contrário o link à sua DIREITA; um glifo adjacente a nenhum link é uma observação em prosa. Não resolva isso com duas regexes encadeadas (a segunda engole o marcador do link vizinho e desmarca silenciosamente uma fonte não verificada). Qualquer edição em massa de marcadores precisa de um censo por citekey antes e depois.
Como as verificações funcionam
| # | Nome | O que sinaliza | Isenção |
|---|---|---|---|
| 1 | Conceitos órfãos | Nenhum backlink de qualquer artigo/livro (link inline ou concepts_touched) E nenhum de um conceito irmão (inline ou related_concepts); a perna irmã evita que hubs de síntese sinalizem falsamente | stub |
| 2 | Métodos órfãos | Nenhum backlink de um artigo/livro E nenhum link de saída para qualquer artigo/livro | stub |
| 3 | concepts_touched vazio | Uma nota de artigo com o campo nulo, vazio ou [] (também o canário para corrupção de frontmatter em ingestões em lote) | stub |
| 4 | Alegações sem fonte | Notas de conceito com pelo menos unsourced_threshold linhas carregando um verbo de alegação (verb_list) e nenhum wikilink dentro de +/-unsourced_radius linhas nem sob um cabeçalho com wikilink | stub |
| 5 | Wikilinks quebrados / placeholder | Alvo não está no índice do vault inteiro (broken), ou corresponde a placeholder_wikilink_patterns quando não resolve (placeholder), ou vazio ([[]], empty-target) | link_target_ignore_patterns (embeds de Bases) |
| 6 | Rascunhos | promote: um rascunho citando pelo menos min_distinct_sources_for_final fontes DISTINTAS (um livro e seus capítulos contam como uma obra). stale: um rascunho mais antigo que stale_days abaixo disso, ou sem data | nenhuma |
| 7 | Cobertura do índice | Uma nota cujo nome base não é um wikilink em index.md | stub; pulado quando index_mode é bases |
| 7b | Seção do índice incorreta | Um - [[X]] sob a seção ## errada (index_sections) | pulado no modo bases |
| 8 | Sincronia chapters_ingested do livro | Conjunto declarado difere dos arquivos {book}_ChNN.md no disco | stub de capítulos |
| 9 | Capítulo sem book: | Uma nota *_ChNN cujo link book: está ausente ou não resolve | nenhuma |
| 10 | Deriva de Estatísticas Rápidas | Uma contagem declarada em ## Quick Stats difere da contagem ao vivo de arquivos da pasta mapeada (quick_stats_labels) | nenhuma |
| 11 | Deriva de Cobertura por Campo | Colunas de Conceitos/Métodos de ## Coverage by Field diferem das contagens ao vivo de não-stubs (linhas de campo por field:, linhas de subcampo -> por subfield: chaveadas pelo link [[MOC_*]]; um subcampo com notas e sem linha é MISSING-ROW) | stubs; notas sem campo são INFO |
| 12 | concepts_touched bidirecional | article -> concepts_touched[token] cujo alvo não tem o backlink [[article]] (asymmetry, auto-corrigível) ou resolve para nenhuma nota (missing_target, triagem do token) | nenhuma |
| 13 | Alias sem link | Um alias documentado (acrônimo distinto ou frase de várias palavras) usado na prosa de outro conceito/método sem um link para sua nota canônica. INFO | stub; alias_link_stoplist; blockquotes não são pesquisados |
| 14 | Radar de idioma | Um cabeçalho (H2-H6) em um idioma diferente do wiki, ou pelo menos prose_threshold acertos de stopword na prosa. Apenas relatório; desabilitado a menos que language_radar.enabled | stub |
| 15 | Radar de síntese | Em conceitos/métodos final: missing = qualifica para um bloco Estado-do-Conhecimento e não tem nenhum; stale = bloco mais antigo que o last_verified mais recente de suas obras citadas | não-final |
| 16 | Vocabulário de status | status não está em status_by_type[type], status ou type ausentes, ou um vazamento de template (draft | final) | nenhuma |
| 17 | Sem source_verification | Um artigo sem selo; um valor começando com abstract NÃO é um selo. INFO backlog | stub |
| 18 | Verificado mas incompleto | Um artigo selado sem volume ou pages e sem um check18_exempt: "YYYY-MM-DD, reason" datado; artigos de conferência e teses são isentos por veículo e forma | stub |
| 19 | evidence_level fora do vocabulário | Valor não está em evidence_level_vocab (apenas artigos) | nenhuma |
| 20 | Úteis Citações ]] | Um blockquote sob ## Useful Quotes com um ]] vazado | nenhuma |
| 21 | Coorte pré-texto completo | Um artigo datado antes de synthesis.prefulltext_cutoff sem selo de texto completo. INFO | stub |
| 22 | Marcador pré-texto completo desatualizado | Um ⚠ cujo wikilink proprietário é um artigo que já tem um selo de texto completo, após três isenções de cauda (observação em prosa, anotação itálica datada, confiança (N src). Tem falsos positivos reais | nenhuma |
| 23 | Placeholder de alegação não preenchido | Uma linha correspondendo a unfilled_claim_patterns (uma fonte nomeada mas nenhuma alegação escrita) | nenhuma |
| 24 | Chave de frontmatter obsoleta | Uma chave em deprecated_frontmatter_keys (padrão journal para venue); correspondência exata, portanto sem falsos positivos | nenhuma |
| 25 | Figura não citada | Uma figura estatística (numeric_claim_patterns) sem wikilink dentro do raio, classificada cited / block / methods / domain. INFO. Nada é isento | stub; blockquotes pulados |
| 26 | Colunas de tabela inconsistentes | Uma tabela cujas linhas têm um número diferente de células que a linha delimitadora (a única verificação estrutural) | nenhuma |
| 27 | Mojibake | Uma linha que percorre encode('cp1252').decode('utf-8') para uma string diferente | nenhuma |
| 28 | Selo sem last_verified | Um artigo/livro com source_verification e sem last_verified (invisível para a Verificação 15 stale). INFO | nenhuma |
| 29 | Link slug fora do vault | Um wikilink para um slug em memory_slug_prefixes, no vault inteiro. Desabilitado a menos que a lista não esteja vazia | memory_slug_exempt_files |
| 30 | Sincronia de vocabulário | _VOCABULARY.md faltando um conceito/método vivo (missing) ou mantendo uma linha para uma nota que não existe mais (ghost). INFO; remédio /vocabulary-update | nenhuma |
| 31 | Selo sem texto | Um selo de no máximo 60 caracteres sem texto de verificação descritivo em qualquer canal. INFO | nenhuma |
| 32 | Par sem selo | Um par (nota, obra citada em seu bloco Estado-do-Conhecimento) cujo selo da nota fonte não nomeia a nota. Bloqueante; dívida declarada em seal_debt_file é relatada mas não contada | dívida declarada |
| 33 | Sem título ou veículo | Um artigo com title ou venue vazios | stub |
Invocação
A partir do vault (o wiki.config.json mais próximo acima é a configuração):
python skills/wiki-lint/scripts/lint.py # todas as verificações; listas vão para _temp/
python skills/wiki-lint/scripts/lint.py --only 5,12 # verificações selecionadas
python skills/wiki-lint/scripts/lint.py --self-test # fixtures vs expected_counters.txt
python skills/wiki-lint/scripts/fix.py check12 --dry-run
python skills/wiki-lint/scripts/fix.py promote _temp/promote_batch.txt
python skills/wiki-lint/scripts/fix.py index # precisa de index_mode "manual" e uma execução lint anterior
Flags, chaves de configuração e os arquivos que cada verificação escreve: references/cli-reference.md.
Quando editar o quê
- Nova pasta do wiki (por exemplo
datasets/): adicione-a afoldersemwiki.config.json. Ela deve ser registrada ANTES de as notas serem criadas, ou cada link para ela será lido como quebrado. - Novo valor de status: adicione-o a
lint.status_by_type.{type}. Novo tipo de nota: adicione o tipo com seus status permitidos. - Novo campo para conceitos/métodos: adicione o slug a
field_titlecase_map. - Refinar Verificação 4:
lint.verb_list. Afrouxar/apertar sua tolerância:lint.defaults.unsourced_radius. - Novo padrão de placeholder:
lint.placeholder_wikilink_patterns(lembre-se que só se aplica a alvos que não resolvem). - Uma verificação deve pular stubs: adicione seu número a
lint.stub_exempt_checks. - Ativar o radar de idioma: copie
examples/languages/es.json(ou escreva suas próprias listas) emlint.language_radar. - Adicionar uma nova verificação: implemente-a em
wikikit/lint/checks_*.py, adicione seus contadores aexpected_counters.txt, adicione uma fixture que a exercite, escreva sua entrada emreferences/check-origins.md. Uma verificação cuja motivação não está escrita é desabilitada na primeira vez que parecer ruidosa. Se a motivação não puder ser encontrada, escreva "não documentado"; nunca invente uma.
Regras de segurança
- O lint é somente leitura contra o wiki; ele só escreve as listas por verificação em
--out-dir(padrão_temp/). fix.pyaltera notas apenas onde relata (backlinks, status), mantém as terminações de linha de cada arquivo e registra cada alteração para rollback.- Quando não tiver certeza se uma mudança de heurística está correta, escreva a fixture primeiro, veja o auto-teste falhar, depois faça a mudança.
- Diretórios de cache nunca são versionados (
_temp/e afins permanecem em.gitignore). Uma medição versionada é lida meses depois como se fosse de hoje.