Aller au contenu principal

Retour aux investigations

4 min · 697 mots · 200 mots/minute · mesuré le 2026-09-05

Une fausse attribution a été en ligne sur ce site

La question

Un dépôt qui promet la traçabilité et qui la manque doit-il le publier ou le corriger en silence ? La question s'est posée en vrai, et la réponse de ce dépôt est cette page.

L'incident

Le site publie des citations qui viennent de deux fichiers différents : le contenu converti (site/content/**/*.json, qui porte l'identité de la citation) et l'index des citations (corpus/index/citations.json, qui porte le texte source et sa localisation). Il faut donc les joindre. La première version de resoudreCitation les joignait sur la clef id#ref.

ref n'est pas une clef commune. C'est un ordinal local au document qui déclare la citation — la première citation d'une note vaut c1, la première citation d'une autre note vaut c1 aussi, et les deux ne désignent pas le même passage. La jointure rendait donc un verbatim juste sous une localisation fausse : le bon texte, la bonne référence bibliographique, et le mauvais bloc. Pour un dépôt dont toute la valeur tient au renvoi localisé, c'est l'erreur maximale.

La mesure, refaite le 2026-09-05

.venv/bin/python -c "
import json, pathlib, collections
par_ref = {(c['id'], c['ref']): c
           for c in json.load(open('corpus/index/citations.json'))['citations']}
faux, tot = collections.Counter(), 0
for p in sorted(pathlib.Path('site/content').rglob('*.json')):
    if p.name == '_generated.json': continue
    for c in json.loads(p.read_text(encoding='utf-8')).get('citations', []):
        cible = par_ref.get((c['id'], c['ref']))
        if not cible: continue
        tot += 1
        idx = cible['ancre'] if cible.get('ancre') is not None else cible.get('bloc')
        if str(c.get('ancre_ou_bloc')) != str(idx): faux[p.parent.name] += 1
print(tot, sum(faux.values()), dict(faux))"

Sortie : 94 74 {'lecture': 1, 'outils': 33, 'synthese': 39, 'transposition': 1}.

Soixante-quatorze citations sur quatre-vingt-quatorze jointes auraient désigné un autre passage que le leur : trente-trois sur les fiches d'outils, trente-neuf sur la synthèse, une sur la lecture, une sur la transposition. Le chiffre relevé au moment de l'incident était le même, avec la même ventilation — ce qui est le point de cette page : la mesure de l'incident est encore rejouable aujourd'hui, parce qu'elle porte sur la clef fautive et non sur un état disparu du rendu.

La correction

resoudreCitationLocalisee(id, ancreOuBloc) joint sur la localisation, seule clef qui désigne la même chose dans les deux fichiers, et jette dans les deux cas où la jointure ne peut pas être sûre : localisation absente, localisation ambiguë. Une exception au build est ici strictement préférable à un rendu : une page qui ne se construit pas se remarque, une attribution fausse qui s'affiche ne se remarque pas.

La part /synthese est inchangée — trente-neuf sous id#ref —, et sans effet : /synthese ne résout aucune citation à l'affichage.

Ce que ça change

La forme de la promesse. Ce dépôt n'a jamais promis de ne pas se tromper ; il promet que ses erreurs sont mesurables et écrites. Une erreur de traçabilité tue la promesse si elle est tue, pas si elle est publiée avec sa mesure et son correctif.

La règle de jointure, générale. Un identifiant local ne devient jamais une clef parce qu'il en a l'air. Deux fichiers ne se joignent que sur ce qu'ils désignent tous les deux : ici, la localisation dans le document source, jamais l'ordinal de déclaration.

Un contrôle plutôt qu'une intention. L'incident a été possible parce que le contrôle des citations vérifiait alors un modèle — la citation existe, elle porte ses champs — et non un affichage. La passe du rendu, qui vérifie que la page servie porte bien l'URL canonique et l'instantané de sa source, existe depuis.

Ce que ça n'établit pas

Qu'aucune autre attribution fausse n'a été servie. La mesure ci-dessus porte sur une classe de faute, celle de la clef de jointure. Une citation dont l'index lui-même porterait une localisation erronée passerait ce contrôle sans bruit — le contrôle compare deux fichiers entre eux, il ne retourne pas à la page d'origine.

Que le rendu fautif ait été vu par quelqu'un. Le site n'a jamais été déployé. L'erreur a été en ligne dans le rendu local, entre deux plans de construction, et non sur une origine publique. Cela diminue le dommage ; cela ne change rien à la faute, et ce n'est pas une raison de ne pas l'écrire.

Rejouer la mesure

Les commandes ci-dessus sont dans le texte, pas ici.

Elles se lancent depuis un clone du dépôt.

Aucune ne fait de requête réseau.

La source de cette page, dans le dépôt :

deliverables/investigations/02-une-fausse-attribution-a-ete-en-ligne.md