Vérification du wiki#
En bref#
Quatre contrôles automatisables garantissent qu'aucune fiche ne ment sur sa complétude : les liens internes résolvent, chaque fiche porte ses sources et ses limites, aucune fiche n'est orpheline depuis l'accueil, et rien n'est en anglais. À lancer après chaque lot de rédaction, depuis la racine.
Le contrôle#
Enregistrer sous /tmp/verifier.py puis lancer python3 /tmp/verifier.py depuis la
racine du dépôt. Le script ne modifie rien.
import pathlib, re, sys
RACINE = pathlib.Path('.')
# Dossiers hors périmètre : dépôt git, dossier de travail de la publication,
# site HTML publié, configuration de l'outillage.
HORS_PERIMETRE = {'.git', '.publication', 'dist', '.claude'}
fiches = sorted(f for f in RACINE.rglob('*.md')
if not HORS_PERIMETRE & set(f.parts)
and f.name not in ('CLAUDE.md', 'PROJET.md'))
anomalies = []
# Les blocs de code contiennent des gabarits, pas de vrais liens : on les retire.
def sans_blocs_de_code(texte):
return re.sub(r'[`]{3}.*?[`]{3}', '', texte, flags=re.S)
liens_internes = {}
for f in fiches:
corps = sans_blocs_de_code(f.read_text(encoding='utf-8'))
# 1. Liens internes
cibles = set()
for cible in re.findall(r'\]\(([^)\s]+\.md)(?:#[^)]*)?\)', corps):
if cible.startswith('http'):
continue
cibles.add(cible)
if not (f.parent / cible).resolve().exists():
anomalies.append(f'LIEN CASSÉ {f} → {cible}')
liens_internes[f] = cibles
# 2. Sections obligatoires (les gabarits sont des modeles, pas des fiches)
if 'gabarits' in f.parts:
continue
for section in ('## Sources', '## Limites'):
if not re.search(rf'^{re.escape(section)}', corps, re.M):
anomalies.append(f'SECTION MANQUANTE {f} → {section}')
# 3. Une piste de suite déclarée
if 'Prochaine piste' not in corps:
anomalies.append(f'PISTE MANQUANTE {f}')
# 4. Fiches orphelines : atteignables depuis l'accueil ?
atteintes, pile = {RACINE / 'index.md'}, [RACINE / 'index.md']
while pile:
courante = pile.pop()
for cible in liens_internes.get(courante, ()):
suivante = (courante.parent / cible)
if suivante.exists():
suivante = pathlib.Path(suivante.resolve()).relative_to(RACINE.resolve())
if suivante not in atteintes:
atteintes.add(suivante)
pile.append(suivante)
for f in fiches:
if f not in atteintes and 'gabarits' not in f.parts:
anomalies.append(f'ORPHELINE {f}')
print('\n'.join(anomalies) if anomalies else 'Aucune anomalie.')
print(f'\n{len(fiches)} fiches contrôlées.')
sys.exit(1 if anomalies else 0)
Les contrôles non automatisables#
À faire à l'œil, à la fin de chaque lot :
- Français intégral — aucun titre, nom de fichier, section ou terme technique en anglais. Les noms propres et les citations font exception.
- Traçabilité — toute affirmation non triviale porte une source cotée. Toute source de cote D figure au journal des arbitrages.
- Symétrie de traitement — la méthode impose que ce qui est examiné chez l'un le soit chez les autres. Se relire en se demandant : un lecteur du camp adverse trouverait-il ce texte injuste, et sur quoi ?
- Datation — aucune date relative. Tout chiffre porte sa date de mesure.
Sources#
- Méthode — les règles que ces contrôles font respecter
- PROJET.md — l'exigence de niveau 3 dont découle le contrôle des « Limites »
Limites#
- Les gabarits sont exclus du contrôle d'orphelinat : ils sont des modèles, pas des fiches de contenu.
- Les dossiers
.publication/(travail de publication) etdist/(site HTML publié) sont hors périmètre : ils contiennent des copies et des fichiers tiers. - Le contrôle des liens ne vérifie ni les ancres (
#section) ni les liens externes, qui peuvent pointer vers une page disparue. - La détection de l'anglais n'est pas automatisée.
- Prochaine piste : ajouter un contrôle des ancres internes, et un contrôle de
fraîcheur signalant toute fiche dont
derniere_majremonte à plus de trois mois.