0071 — Méta-gouvernance documentaire exécutable et cartographie de la couverture E2E
Contexte
Section intitulée « Contexte »Atlas tient un appareil de pilotage documentaire dense et déjà largement outillé :
une série d’ADR numérotée (docs/decisions/), un registre de drifts indexé
(ADR 0056), des reconnaissances
consignées (ADR 0060),
une cadence d’audit transverse (ADR 0039),
et une documentation vérifiable (ADR 0028)
qui empêche déjà la doc rédigée de mentir sur le code via pnpm audit:docs
(scripts/audit/documentation.mjs). Cet appareil garde pourtant trois angles
morts que le volume rend visibles.
1. Le registre de drifts est une capacité à moitié câblée. Le registre
(ADR 0056) consigne un écart révélé à
l’exécution avec un statut (corrige, caduc, ouvert). Un drift ouvert
est un écart non encore résolu — mais rien ne pilote sa résolution : le
schéma Zod (docs/src/content.config.ts) accepte statut: "ouvert" sans exiger
de pointeur vers le travail qui le fermera. Un écart connu peut donc rester
ouvert indéfiniment sans qu’aucune issue ne le suive, ce qui contredit l’esprit
« un changement se reflète dans la même trace » de l’ADR 0033.
2. Les conventions de gouvernance ne s’auto-vérifient pas.
scripts/audit/documentation.mjs contrôle la doc rédigée (README présent,
liens internes valides, ADR référencé existant, page non orpheline, compteur
« N ADR » exact — fonction staleAdrCounts, charte
ADR 0052, règle R8).
Mais personne ne vérifie la cohérence de l’index ADR lui-même : que
decisions/index.md liste tous les fichiers NNNN-*.md, sans trou ni doublon
de numéro, et que le statut affiché dans l’index concorde avec le statut réel
du fichier (un ADR superseded doit l’être des deux côtés). Une incohérence de
ce genre ne se voit qu’à la prochaine revue humaine, épisodique — une
gouvernance qui ne s’auto-vérifie pas périme en silence, le travers même que
l’ADR 0039 combat pour les
audits.
3. La couverture des tests de bout en bout n’est cartographiée nulle part.
La pyramide de tests d’Atlas est décrite (quality/tests)
: cinq niveaux, dont trois self-skipping (qui se désactivent quand leur
environnement n’est pas démarré) — contrats REDCap, authentification Appwrite,
smoke end-to-end. Le self-skip est un choix de conception assumé
(ADR 0057) : un
contributeur sans Docker lance pnpm test sans erreur. Mais aucune page ne
croise les chaînes du dépôt (les applications, les contrats externes, le
pipeline DataOps) avec leurs niveaux de test et leur état réel d’exécution
(« a-t-il déjà tourné en vrai ? »). La mémoire des écarts E2E est réactive
(le registre de drifts, post-mortem) mais non cartographiée : on ne sait pas,
d’un coup d’œil, où sont les trous connus — par exemple un smoke qui se
saute silencieusement si REDCap (la plateforme de saisie de formulaires
structurés qu’Atlas pilote) est absent.
Le dépôt voisin cluster a outillé ces deux besoins : un audit régulier des
conventions de gouvernance (ADR cluster 0060) et une matrice de couverture
de ses scénarios (pages matrice-catalogue.md / plan-de-tests.md). On s’en
inspire, sans transposer son contexte : cluster cartographie une
infrastructure (matériel, topologie, banc) ; Atlas, lui, déploie sur ce
cluster (ADR 0043) — il n’a ni
matériel ni topologie à cartographier. On garde la forme (un audit qui
signale, une matrice qui nomme les trous), pas les axes infra.
Décision
Section intitulée « Décision »La gouvernance documentaire d’Atlas devient elle-même exécutable, en trois volets concrets : (a) le registre de drifts est durci AU BUILD — un drift
ouvertdoit porter une issue ; (b) l’audit documentaire croise l’index ADR avec les fichiers par statut ; (c) une page de matrice cartographie la couverture de bout en bout, trous nommés compris.
Volet (a) — Registre de drifts vivant : ouvert ⇒ issue, durci au build
Section intitulée « Volet (a) — Registre de drifts vivant : ouvert ⇒ issue, durci au build »On ajoute au schéma Zod de la collection drifts (docs/src/content.config.ts,
ADR 0056) :
- un champ
issueoptionnel (URL ou numéro de l’issue de suivi) ; - un
superRefinequi exige uneissuenon vide quandstatut === "ouvert"(les statuts terminauxcorrigeetcaducn’en demandent pas — l’écart est clos ou caduc, plus rien à suivre).
Le contrôle s’arme là où le registre se valide déjà : au docs:build. Un
drift déclaré ouvert sans issue fait échouer le build (et donc la CI),
exactement comme une entrée malformée aujourd’hui. Pourquoi durcir au build, et
non par convention humaine ? Côté cluster, lier une issue à un drift ouvert est
une discipline de rédaction ; Atlas peut faire mieux parce que son
registre est déjà une content collection validée par un schéma — on transforme
la convention en invariant mécanique quasi gratuitement (≈ six lignes, aucune
dépendance ni job nouveau, le contrôle monte sur le gate docs:build existant).
Contre l’alternative « rappel en revue » : elle laisse passer l’oubli jusqu’à
la prochaine paire d’yeux ; le build, lui, ne pardonne pas. Objection « zéro
drift ouvert aujourd’hui » : c’est précisément le bon moment — l’invariant est
posé avant le premier drift ouvert, pas rétro-ajusté sur une dette ;
ouvert est un statut de première classe et mérite son garde-fou. Cette symétrie
prolonge l’ADR 0033 (« un
changement se reflète dans la même trace ») : un drift Atlas ouvert lie une
issue Atlas.
Volet (b) — check_index : croiser l’index ADR avec les fichiers, par statut
Section intitulée « Volet (b) — check_index : croiser l’index ADR avec les fichiers, par statut »On étend scripts/audit/documentation.mjs d’une fonction pure check_index,
à côté de staleAdrCounts, testée dans documentation.test.mjs (le parsing
du tableau Markdown de l’index et des frontmatter, plus la logique de
concordance, sont non triviaux — ils méritent des tests, au modèle des fonctions
existantes du fichier). Elle croise decisions/index.md ↔ les fichiers
NNNN-*.md et signale :
- un trou de numéro — un
NNNNlisté dans l’index sans fichier, ou un fichier sans ligne d’index ; - un doublon de numéro — deux fichiers ou deux lignes pour le même
NNNN; - un statut discordant — le statut affiché dans l’index (colonne « Statut »)
ne concorde pas avec le premier mot du
## Statutdu fichier (Accepted,Superseded,Deprecated). Un ADR superseded doit l’être des deux côtés.
Ce contrôle vit dans l’audit documentaire existant — bloquant en pre-push et
en CI, comme le reste de documentation.mjs : un index ADR incohérent est un
défaut factuel et dérivable du code, donc du ressort de l’anti-dérive de
l’ADR 0028, pas une question de
goût rédactionnel. Pourquoi étendre documentation.mjs plutôt qu’un nouveau
script ? La logique (lecture des fichiers ADR, parsing du frontmatter et des
liens) y est déjà ; ajouter une fonction testée à côté de staleAdrCounts
réutilise l’infrastructure au lieu de la dupliquer. Contre l’alternative
« script shell jetable » : parser le tableau de 68 lignes d’index et croiser les
statuts est une logique de cohérence qui mérite des tests, pas du shell
fragile.
Volet (c) — Page quality/matrice-e2e.md : cartographier la couverture, trous compris
Section intitulée « Volet (c) — Page quality/matrice-e2e.md : cartographier la couverture, trous compris »On ajoute une page docs/src/content/docs/quality/matrice-e2e.md : un tableau
croisant les chaînes propres à Atlas × leurs niveaux de test
(unitaire / intégration / E2E) × « a-t-il déjà tourné en vrai ? », complété
d’une liste explicite des trous nommés. Les chaînes cartographiées sont celles
du dépôt, pas une infrastructure :
- applications SvelteKit — smoke end-to-end Playwright (le pilote de
navigateur) sur
amarreetsillage, niveaux unitaire et intégration en vitest ; - contrats externes — tests de contrat REDCap (
cli/crf-openapi,apps/crf-dashboard) et flux d’authentification Appwrite (le backend-as-a- service qui gère l’authentification,packages/baas,apps/sillage), tous deux self-skipping ; - chaîne DataOps de bout en bout — ingestion → orchestration Dagster → transformations dbt → traçabilité de lignage → suivi de dérive (ADR 0062, ADR 0068).
Les trous connus sont nommés, pas tus — au premier rang, le smoke skippable si REDCap est absent : un test qui se saute silencieusement n’est pas un échec, mais un état qui doit être visible. Pourquoi nommer le trou plutôt qu’armer une porte bloquante ? Forcer ce smoke à échouer quand REDCap manque rouvrirait le piège que l’ADR 0034 écarte (une CI rouge pour une dépendance absente, sans rapport avec le code poussé) et contredirait le self-skip assumé par l’ADR 0057 ; la matrice rend l’état lisible sans gate qui frictionnerait au mauvais moment.
La cohérence de la page est vérifiée : un miroir testé doc ↔ specs
contrôle que les chaînes listées dans la matrice correspondent aux fichiers de
test réels (*.spec.ts côté Node, test_*.py côté DataOps) — la matrice ne peut
donc pas dériver des tests sans que le contrôle bronche, dans l’esprit de
l’ADR 0028.
Ce que cette décision EXCLUT explicitement
Section intitulée « Ce que cette décision EXCLUT explicitement »On n’ajoute pas de bloc « le dépôt en chiffres » régénéré dans le README (ADR par statut, plans, drifts, duplication). Pourquoi l’exclure ? Ces compteurs sont une donnée volatile, non reproductible octet par octet depuis l’arbre Git — exactement le cas que l’ADR 0032 réserve à une série append-only auditée pour sa cohérence structurelle, et jamais à un fichier diff-checké. Y glisser un bloc régénéré dans le README rouvrirait le faux positif que 0032 ferme. La volatilité de gouvernance, si on la veut un jour, ira dans la série append-only existante, pas ici.
Accepted (2026-06-24). Amende l’ADR 0056
(le registre devient vivant : un drift ouvert lie une issue, durci au build) et
étend l’ADR 0028 (la
vérifiabilité couvre désormais la cohérence de l’index ADR et le miroir
doc ↔ specs de la matrice), dans l’esprit de l’ADR 0039
(une gouvernance qui ne s’auto-vérifie pas périme en silence). S’appuie sur
l’ADR 0052
(compteurs exacts, R8), l’ADR 0057
(self-skip assumé) et l’ADR 0034
(pas de gate sur dépendance absente). N’invalide aucun ADR. Inspiré de la pratique
du dépôt cluster (ADR cluster 0060, audit régulier des conventions ; pages
matrice-catalogue.md / plan-de-tests.md, cartographie de couverture), sur le
modèle de crédit de l’ADR 0056 — le
contenu reste propre à Atlas (aucun axe matériel, topologie ou banc : Atlas
déploie sur le cluster, ADR 0043).
Conséquences
Section intitulée « Conséquences »Bénéfices.
- Le registre devient pilotable : un drift
ouvertne peut plus stagner sans issue de suivi — ledocs:buildle refuse. La capacité de l’ADR 0056 passe de « moitié câblée » à invariant mécanique, quasi gratuitement. - L’index ADR s’auto-vérifie : trou, doublon ou statut discordant sont signalés en CI, plus seulement à la revue humaine épisodique — le travers « périmer en silence » est neutralisé pour la table d’index.
- La couverture E2E devient lisible : on voit d’un coup d’œil quelles chaînes ont tourné en vrai et où sont les trous, le smoke skippable nommé compris. La mémoire E2E cesse d’être seulement réactive (le registre, post-mortem) pour devenir cartographiée.
Prix à payer.
- Un peu de cérémonie : ouvrir une issue dès qu’un drift est déclaré
ouvert, tenir la matrice à jour quand une chaîne de test apparaît ou change. Le miroir doc ↔ specs signale une dérive, mais la matrice reste une page rédigée à maintenir. - Trois petites surfaces à porter : ≈ six lignes de Zod, une fonction testée dans
documentation.mjs, une page de doc avec son test de miroir. Léger, mais non nul.
Garde-fous.
- En revue de PR, un drift
ouvertsans issue ne passe pas (échecdocs:build) ; un index ADR incohérent ne passe pas (pnpm audit:docs, bloquant). Les deux sont opposables et tracés. - Les niveaux self-skipping restent non bloquants quand leur environnement est absent (ADR 0057, ADR 0034) : la matrice rend l’état visible, elle n’arme aucune porte qui rouvrirait ce piège.
- Aucun compteur volatil n’est diff-checké : toute statistique de gouvernance qu’on voudrait un jour afficher relève de la série append-only de l’ADR 0032, jamais d’un bloc régénéré dans le README.
- Le périmètre reste le dépôt de code : on vérifie des conventions internes (cohérence d’index, registre, miroir de tests), jamais une décision propre à un organisme déployeur (ADR 0035).
Alternatives écartées
Section intitulée « Alternatives écartées »- Laisser le registre de drifts en convention humaine (lier l’issue « quand on
y pense »). Écarté : Atlas peut faire mieux que
clusterici, parce que son registre est déjà un schéma Zod validé au build — durcir l’invariant coûte quelques lignes et supprime l’oubli à la source. - Porter tel quel l’audit de gouvernance de
cluster(un script dédié + cron + bloc de statistiques). Écarté : trois familles de ce script n’ont pas de support Atlas, son bloc de chiffres heurte l’ADR 0032, et la valeur anti-doc-rot des compteurs « N ADR » est déjà faite (staleAdrCounts). On en garde le noyau utile — le croisement index ↔ fichiers par statut — greffé surdocumentation.mjs, pas un script de plus. - Tout en shell. Écarté : parser le tableau d’index, croiser 68 statuts et
valider le miroir doc ↔ specs est une logique de cohérence non triviale → des
fonctions pures testées au modèle de
documentation.mjs, pas du shell jetable. - Cartographier l’infrastructure (matériel, topologie, banc), à la manière de la
matrice
cluster. Écarté : Atlas déploie sur le cluster (ADR 0043), il ne monte pas de bancs ; sa matrice cartographie ses chaînes de test (applications, contrats externes, pipeline DataOps), pas une infrastructure qu’il ne possède pas.