Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
115 KiB
Activity Log · 2026-07-31 · Claude Code DTP
Session 20260731_150110 · Buffer S8 · Domaine Mobile (2e surface) : la ligne récap du README app Expo énumérait À LA MAIN ses 5 onglets ((Ventes/Construction/Achat/Compta/Direction)) sans gate d'IDENTITÉ — le bloc mobile existant ne recompute que le COMPTE (« 5 onglets ») et son patron matche la parenthèse par \([^)]*\) en ignorant son contenu, aveugle à un échange / retrait / réordre de nom.
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Le bloc mobile gaté
précédemment recompute le quadruplet de la ligne récap (onglets · rôles
couverts · langues · identifiants a_confirmer) depuis MANIFEST.counts. Restait,
dans la même ligne du même README, une surface data-derived distincte : la
parenthèse qui énumère les 5 onglets — matchée par le patron du compte mais
dont le contenu est ignoré.
Dérive silencieuse fermée :
05_deliverables_mvp/mobile/app_config/README.md:66— « 5 onglets (Ventes/Construction/Achat/Compta/Direction) · … ». La parenthèse est une liste ordonnée data-derived ; le patron du compte (\*\*(\d+) onglets\*\* \([^)]*\)) la matche mais IGNORE son contenu.- Source faisant autorité :
mobile/app_config/out/role_navigation.json(byte-gaté parcheck_artifacts) — un onglet par portail métier du contrat RBAC (bijection 1 onglet ⇔ 1 portail ·plateformeexclu), chaque entrée portantportail(id)label« OTO ». Nom court énuméré = lelabelsans le préfixe « OTO ».
- Le bloc mobile ne gate QUE le compte (« 5 onglets »). Un échange
(
Ventes → Marketing), un retrait (Comptaabsent — l'énumération tombe à 4 tandis que « 5 onglets » reste juste, aveugle au compte) ou un réordre laisse le compte à 5 pendant que l'artefact dit autre chose. « Vert trompeur » qu'aucune suitetests/(qui teste des FONCTIONS, pas la prose) n'attrape. - État courant : aucun onglet périmé — la liste des 5 recoupe l'artefact exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Nouveau bloc dans ci/check_readme_claims.sh : recompute la liste ordonnée
des noms courts depuis role_navigation.json (zéro duplication de la navigation du
générateur · #6) et exige que la parenthèse l'énumère exactement : contrôle par
ENSEMBLE (absent ET en trop mordus via set-diff), puis ordre. Même patron que
l'énumération des confirmations DevOps, la carte de renormalisation par archétype 4Big
ou la table « Verdict courant » de l'audit 5D. Cohérence croisée en bonus :
len(role_navigation) == MANIFEST.counts.onglets == MANIFEST.counts.portails (la
bijection du générateur) — mord un compteur du manifeste désynchronisé de sa navigation.
Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
échange Ventes → Marketing (compte reste 5 · gate-compte aveugle) |
✗ énumère [Marketing,…] MAIS role_navigation [Ventes,…] · absents=[Ventes] · en trop=[Marketing] · exit 1 |
retrait Compta (énumération=4 · « 5 onglets » reste juste) |
✗ énumère 4 MAIS role_navigation 5 · absents=[Compta] · exit 1 |
réordre Construction/Ventes/… (même ensemble) |
✗ même ensemble, ordre ≠ · exit 1 |
| énumération supprimée (claim disparu) | ✗ énumération des onglets INTROUVABLE · exit 1 |
| restauré | ✓ Ventes/Construction/Achat/Compta/Direction == role_navigation · bijection 5==onglets==portails · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (2ᵉ surface du même README mobile).
Suite CI complète re-verte (7 gates) : guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact).
Auto-score 4Big : 96/100.
Session 20260731_143104 · Buffer S8 · Domaine QA/Audit 5D (2e surface) : la table « Verdict courant » de l'audit de conformité énumérait À LA MAIN ses 4 open items (control → dimension → propriétaire) sans gate d'IDENTITÉ — le bloc existant ne recompute que le COMPTE (« 4 à confirmer »), aveugle à un échange d'open item.
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Le bloc audit_5d gaté
précédemment couvre « 17 contrôles / 5 dimensions » (×2 docs) et la ventilation
du verdict « 13 PASS · 0 FAIL · 4 à confirmer » (recomputée d'audit_report.totals).
Restait, dans le même README, une surface data-derived distincte : la table
« Verdict courant » qui énumère les 4 contrôles ouverts avec leur dimension et leur
propriétaire.
Dérive silencieuse fermée :
05_deliverables_mvp/qa/audit_5d/README.md:51-56énumère 4 lignes| D1.1 · … | D1 | Direction |… — chacune porte trois faits data-derived : l'iddu contrôle, sa dimension, son propriétaire.- Source faisant autorité :
qa/audit_5d/out/audit_report.json.open_items[](byte-gaté parcheck_artifacts, chaque item ={control, dimension, owner, detail}recalculé en rejouant les contrôles sur les hand-off amont). - Le bloc de ventilation ne gate QUE le compte (« 4 à confirmer »). Un
échange d'open item (ex.
D2.3 → D3.1) laisse le compte à 4 — le compteur reste AVEUGLE — pendant que l'artefact dit autre chose ; idem une dimension mal étiquetée (D1.3rangé sousD2) ou un propriétaire réattribué. « Vert trompeur » qu'aucune suitetests/(qui teste des FONCTIONS, pas la prose) n'attrape. - État courant : aucun open item périmé — les 4 lignes recoupent l'artefact exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Nouveau bloc dans ci/check_readme_claims.sh : recompute l'ensemble
{control:(dimension, propriétaire)} depuis audit_report.open_items (zéro
duplication du modèle de l'auditeur · #6) et exige que la table README l'énumère
exactement : contrôle par ENSEMBLE (absent ET en trop mordus, pas seulement
présence), puis (dimension, propriétaire) par LIGNE. Même patron que
l'énumération des confirmations DevOps ou la carte de renormalisation par archétype
4Big. Cohérence croisée en bonus : l'ensemble des control == MANIFEST.open_items
(le manifeste qui résume le rapport) — mord un manifeste désynchronisé de son rapport.
Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
échange D2.3 → D3.1 (compte reste 4 · gate-compte aveugle) |
✗ énumère […,D3.1] MAIS référentiel […,D2.3] · exit 1 |
dimension D1.3 étiquetée D2 (compte + ids intacts) |
✗ open item D1.3 dit dimension=D2 MAIS artefact D1 · exit 1 |
propriétaire D1.1 Direction → Compta (compte + ids + dims intacts) |
✗ D1.1 dit propriétaire=Compta MAIS artefact Direction · exit 1 |
ligne D1.2 supprimée (sous-ensemble) |
✗ énumère [D1.1,D1.3,D2.3] MAIS référentiel [D1.1,D1.2,D1.3,D2.3] · exit 1 |
| restauré | ✓ [D1.1,D1.2,D1.3,D2.3] (control→dimension→propriétaire) == audit_report.open_items · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (2e surface du même README audit 5D).
Suite CI complète re-verte (7 gates) : guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact).
Auto-score 4Big : 96/100.
Session 20260731_140101 · Buffer S8 · Domaine QA/Audit 4Big (2e surface) : la carte de renormalisation PAR ARCHÉTYPE de l'auditeur (« data_room (sans HANDOFF) · parser (sans CONTRAT … ni HANDOFF) · contract (sans CLI ni HANDOFF) ») était transcrite À LA MAIN dans le README du module — la session précédente a gaté le barème (critères + poids + generator (N critères)) mais PAS les exclusions des trois autres archétypes.
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). La session 133057 a gaté le
barème de l'auditeur (nombre de critères + poids par critère + generator (5 critères)). Restait, dans le même README, une surface data-derived distincte : la
carte de renormalisation par archétype, qui dit quels critères CHAQUE archétype
exclut du dénominateur.
Dérive silencieuse fermée :
05_deliverables_mvp/qa/audit_4big/README.md:32-34énumère «generator(5 critères) ·data_room(sans HANDOFF) ·parser(sans CONTRAT local ni HANDOFF) ·contract(sans CLI ni HANDOFF) ».- Cette carte est DATA-DERIVED : pour un archétype, l'ensemble des critères
applicables =
{check.criterion}de ses modules dansout/quality_report.json(byte-gaté parcheck_artifacts) — les critères non-applicables sont absents deschecks. Donc exclus = ordre du barème − applicables. - Le bloc barème de la session précédente ne recompute QUE
generator(« 5 critères », zéro exclusion). Les exclusions dedata_room/parser/contractn'étaient recomputées par AUCUN gate : changer la règle de renormalisation d'un archétype (ex.data_roomexclut aussiCLI) ferait dériver la prose en silence pendant quequality_reportdit autre chose — « vert trompeur » qu'aucune suitetests/(qui teste des FONCTIONS, pas la prose) n'attrape. - État courant : aucune exclusion périmée — les 3 cartes recoupent l'artefact exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated.
Nouveau bloc (6) dans ci/check_readme_claims.sh (section barème qa/audit_4big) :
recompute par archétype l'ensemble des critères exclus depuis les checks de
quality_report.modules (zéro duplication du modèle de renormalisation du générateur ·
#6) et exige que la parenthèse du README l'énumère exactement (mauvais critère,
sous-ensemble, sur-ensemble → tous mordus). Bonus d'uniformité : tous les modules d'un
même archétype portent le même jeu applicable (« 1 archétype → 1 jeu de critères »,
base même de la carte — mord un référentiel où un module d'un archétype divergerait des
autres). Un claim (énumération) absent échoue AUSSI (traçabilité).
Preuve (3 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
data_room « sans HANDOFF » → « sans CLI » (mauvais critère exclu) |
✗ exclut [CLI] MAIS référentiel [HANDOFF] · exit 1 |
parser exclusion HANDOFF retirée (sous-ensemble) |
✗ exclut [CONTRAT] MAIS référentiel [CONTRAT, HANDOFF] · exit 1 |
énumération contract supprimée (claim disparu) |
✗ énumération contract INTROUVABLE · exit 1 |
| restauré | ✓ 4 archétypes uniformes · data_room/parser/contract == référentiel · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « carte de renormalisation par archétype 4Big »).
Suite CI complète re-verte (7 gates) : guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_133057 · Buffer S8 · Domaine QA/Audit 4Big : le barème de notation de l'auditeur (« 5 critères 4Big déterministes » + la table Critère | Poids + l'archétype « generator (5 critères) ») était transcrit À LA MAIN dans le README du module sans AUCUN gate — le bloc racine ne gatait que sa ligne « Résultat courant » (verdict/modules), jamais le référentiel de scoring lui-même.
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Restait une surface de « vert trompeur » non gatée dans le module qui NOTE les autres : son propre barème.
Dérive silencieuse fermée :
05_deliverables_mvp/qa/audit_4big/README.mdretranscrit trois surfaces data-derived du référentiel de scoring : la prose « Note chaque module sur 5 critères 4Big déterministes », la table « Critère | Poids | Fait vérifié » (une ligneid→poids par critère : DOC 20 · CONTRAT 20 · TESTS 25 · CLI 15 · HANDOFF 20) et l'archétype «generator(5 critères) ».- La source faisant autorité est
out/quality_report.json.criteria(byte-gaté parcheck_artifacts, recomputé du référentiel à chaque build). - Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter/retirer un critère
ou changer un poids ferait dériver le README en silence — dérive qu'aucune suite
tests/(qui teste des FONCTIONS, pas la prose) n'attrape.
Nouveau bloc dans ci/check_readme_claims.sh : recompute le nombre de critères
(prose + « generator »), et chaque poids depuis criteria[].weight (zéro
duplication du barème du générateur · #6), exige l'égalité avec le README. Cohérences
croisées : Σ poids == 100 (base de renormalisation documentée) ET tout module
d'archétype generator porte EXACTEMENT len(criteria) checks (« generator = tous
les critères » — 18 modules generator × 5 checks vérifiés). Une compensation de
poids (+5 / −5) laisserait Σ == 100 juste et deux lignes fausses → d'où le contrôle
par critère, pas seulement de la somme.
Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| README prose 5 critères → 6 (poids table intacts) | ✗ dit 6 MAIS criteria en compte 5 · exit 1 |
README poids TESTS 25 → 30 (compte intact) |
✗ dit poids 30 MAIS criteria dit 25 · exit 1 |
README archétype generator (5→4 critères) |
✗ dit 4 MAIS generator applique les 5 · exit 1 |
| README claim prose reformulé (chiffre disparu) | ✗ « N critères 4Big » INTROUVABLE · exit 1 |
| restauré | ✓ 5 critères · poids 20/20/25/15/20 · Σ=100 · 18 gen×5 == artefact · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « barème auditeur 4Big »).
Suite CI complète re-verte (7 gates) : guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_130055 · Buffer S8 · Domaine QA/Recette : le couple « 8 livrables de sprint · 7 métriques succès MVP » du module d'acceptation (qa/acceptance) était transcrit À LA MAIN dans DEUX docs de module (README du module + fiche agent QA) sans AUCUN gate — le bloc racine ne gate QUE le README d'entrée
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les
sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM
(pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D + audit 4big), DevOps
(run-book), Démo (run-sheet) et Frontend/Chat OTOIA. Cette session cible le module de
recette (qa/acceptance) — son couple data-derived « livrables de sprint /
métriques succès MVP », cité dans deux docs de module que le bloc racine ne couvrait pas.
Défaut trouvé — couple ungated, transcrit dans DEUX docs de module
05_deliverables_mvp/qa/acceptance/README.md:8-9: puces « 8 livrables de sprint (L33..L76) » et « 7 métriques succès MVP (L81..L87) ».03_agents/qa/AGENT.md:27: cellule « mappe chaque promesse roadmap (8 livrables + 7 métriques) vers sa preuve » — même couple dérivé de la même matrice (source unique · #5).- L'artefact source est byte-gaté par
check_artifacts:qa/acceptance/out/acceptance_matrix.jsonporte une ligne par promesse roadmap dekindsprint_deliverableOUmvp_metric. Le bloc racine decheck_readme_claimsrecompute déjà ce couple pour le SEUL README d'entrée (« N promesses (X sprint + Y métriques) ») — mais jamais ces deux docs de module. - Le roadmap-anchor (INV11) ancre les
roadmap_linede l'ARTEFACT au fichier roadmap ; il ne compare pas la prose des docs. Ajouter une promesse roadmap → l'artefact rebâtit 9/7 pendant que les deux docs restent à 8/7 en silence — « vert trompeur » qu'aucune suitetests/(qui teste des FONCTIONS, pas la prose) n'attrape. - État courant : aucun nombre périmé — 8/7 recoupe la matrice exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated.
Nouveau bloc dans ci/check_readme_claims.sh : recompute chaque nombre depuis
son propre genre de la matrice (kind sprint_deliverable / mvp_metric,
n_sprint/n_metric déjà dérivés plus haut · zéro duplication #5) et exige l'égalité
avec les DEUX docs. Recomputer les DEUX (sprint ET métriques) mord une dérive de l'un
là où l'autre resterait aveugle. Cohérence croisée en bonus :
MANIFEST.counts.{sprint_deliverables,mvp_metrics} == le compte de la matrice qu'il
résume — mord un compteur du manifeste désynchronisé de sa liste. Un claim absent
échoue AUSSI (traçabilité).
Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
README sprint 8→9 (métriques reste 7) |
✗ dit 9/7 MAIS matrice 8/7 · exit 1 |
README métriques 7→6 (sprint reste 8) |
✗ dit 8/6 MAIS matrice 8/7 · exit 1 |
fiche QA parenthèse 8→10 (README intact) |
✗ dit 10/7 MAIS matrice 8/7 · exit 1 |
| fiche QA claim supprimé (reformulé) | ✗ claim INTROUVABLE · exit 1 |
| restauré | ✓ 8/7 == matrice (2 docs) · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « module de recetteqa/acceptance»).
Suite CI complète re-verte : guard_constraints · validate_json · check_docs ·
check_artifacts · check_regression · check_ci_integrity · check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_123055 · Buffer S8 · Domaine Frontend/Chat OTOIA : le montage de l'assistant conversationnel par portail (frontend/chat_otoia) affichait « 5 Custom Block + 5 configs runtime » (fiche agent frontend_console) et « 5 portails · 5 fixtures · 5 configs runtime » (README du module) — trois nombres data-derived, TOUS = 5 (piège aveugle), transcrits À LA MAIN dans DEUX docs sans AUCUN gate
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM (pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D), DevOps (run-book) et Démo (run-sheet). Cette session cible le domaine Frontend/Chat OTOIA — le montage de l'assistant conversationnel par portail (livrable Sprint 6 « Chat OTOIA embedded »), seul grand livrable dont les comptes data-derived cités dans deux docs pointaient l'artefact généré sans être recomputés par aucun gate.
Défaut trouvé — triplet ungated, TOUS = 5 (piège aveugle), transcrit dans DEUX docs
03_agents/frontend_console/AGENT.md:29: cellule « 5Custom Block+ 5 configs runtime » — deux nombres (custom_blocks + mounts).05_deliverables_mvp/frontend/chat_otoia/README.md: « Portails (5,plateformeexclu) » (l.24) · « 5 fixturesCustom Block» (l.48) · « 5 configs runtime » (l.49) — trois transcriptions (portails + custom_blocks + mounts).- L'artefact source est byte-gaté par
check_artifacts:frontend/chat_otoia/out/MANIFEST.jsonrecomptecounts.custom_blocks,counts.mountsetcounts.portailsdu contrat RBAC à chaque build (bijection 1 block ⇔ 1 mount ⇔ 1 portail métier,plateformeexclu — invariant 2 du générateur). - Aucun gate ne comparait la VALEUR écrite à l'artefact. Piège aveugle
caractéristique (cf. DevOps phases ⁄ confirmations) : custom_blocks, mounts ET
portails valent tous trois 5 aujourd'hui — un gate qui n'en recompterait qu'un
serait aveugle à la dérive des deux autres (ajouter un portail au contrat RBAC →
chat_otoia rebâtit 6 ⁄ 6 ⁄ 6 pendant que les deux docs restent à 5). « Vert
trompeur » qu'aucune suite
tests/(qui teste des FONCTIONS, pas la prose des docs) n'attrape. - État courant : aucun nombre périmé — 5/5/5 recoupe MANIFEST exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated.
Nouveau bloc dans ci/check_readme_claims.sh : recompute chaque nombre depuis
son propre champ du MANIFEST (custom_blocks · mounts · portails, jamais figé
ici · zéro duplication du compteur du générateur #5) et exige l'égalité avec les deux
docs. Cohérence croisée : custom_blocks == mounts == portails == |MANIFEST.portails|
(la bijection du générateur) — mord un compteur désynchronisé de sa propre liste. Un
claim absent échoue AUSSI (traçabilité).
Preuve (5 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
fiche custom_block 5→6 (mounts reste 5, aveugle si 1 seul recompté) |
✗ dit 6/5 MAIS artefact 5/5 · exit 1 |
fiche configs runtime 5→4 (blocks reste 5) |
✗ dit 5/4 MAIS artefact 5/5 · exit 1 |
README portails 5→6 |
✗ dit 6 portails MAIS artefact 5 · exit 1 |
README fixtures 5→7 |
✗ dit 7 fixtures MAIS artefact 5 · exit 1 |
| README claim « configs runtime » supprimé (reformulé) | ✗ claim INTROUVABLE · exit 1 |
| restauré | ✓ 5/5/5 == artefact (2 docs) · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « montage Chat OTOIA par portail »).
Suite CI complète re-verte : guard_constraints · validate_json · check_docs ·
check_artifacts · check_regression · check_ci_integrity · check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_120054 · Buffer S8 · Domaine Démo : le run-sheet de pitch (demo/scenarios) affichait « 10 modules cités » dans le diagramme d'architecture de son README — un compte data-derived transcrit À LA MAIN sans AUCUN gate
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM (pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D) et DevOps (run-book). Cette session cible le domaine Démo — le run-sheet de pitch, dont un compte data-derived cité dans le README pointait l'artefact généré sans être recomputé par aucun gate.
Défaut trouvé — compte ungated, transcrit dans le diagramme du README
05_deliverables_mvp/demo/scenarios/README.md:23: diagramme d'architecture → «scenario_spec.json … out/ des modules livrés (10 modules cités)».- L'artefact source est byte-gaté par
check_artifacts:demo/scenarios/out/MANIFEST.jsonrecomptecounts.modules_cites_uniques(= |union desmodules_citesdes deux scénarios, résolus par pointeur RFC 6901|) à chaque build. Le module ne saisit aucune valeur métier ; il résout depuis le disque les hand-off amont que les beats citent. - Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter un beat qui cite un
module encore non tissé (11) ou retirer la dernière citation d'un module d'un
scénario (9) ferait dériver le diagramme en silence — « vert trompeur » qu'aucune
suite
tests/(qui teste des FONCTIONS, pas la prose du README) n'attrape. - État courant : aucun nombre périmé — 10 recoupe MANIFEST exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated.
Nouveau bloc dans ci/check_readme_claims.sh : recompute le nombre depuis
counts.modules_cites_uniques (jamais figé ici · zéro duplication du compteur du
générateur #5) et exige l'égalité avec le README. Cohérence croisée en bonus :
counts.modules_cites_uniques == |modules_cites| (liste dédupliquée du manifeste,
elle-même sans doublon) — mord un compteur désynchronisé de sa propre liste. Un claim
absent échoue AUSSI (traçabilité).
Preuve (2 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
README drift 10→11 |
✗ dit 11 modules cités MAIS artefact 10 · exit 1 |
| README claim supprimé (reformulé) | ✗ claim « (N modules cités) » INTROUVABLE · exit 1 |
| restauré | ✓ 10 modules cités == artefact · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « run-sheet de démo »).
Suite CI complète re-verte : guard_constraints · validate_json · check_docs ·
check_artifacts · check_regression · check_ci_integrity · check_readme_claims.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_113048 · Buffer S8 · Domaine DevOps : le run-book de déploiement VPS (devops/deploy_runbook) affichait « 7 phases » (fiche agent devops, ×2) et « 7 confirmations préalables » (README du module) — deux nombres data-derived transcrits À LA MAIN dans DEUX docs sans AUCUN gate
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM (pipeline vente), mobile, Legal (CONFOTUR) et QA (audit 5D). Cette session cible le domaine DevOps — le run-book de déploiement VPS, dont deux comptes data-derived cités dans deux docs pointaient l'artefact généré sans être recomputés par aucun gate.
Défaut trouvé — comptes ungated, transcrits dans DEUX docs
03_agents/devops/AGENT.md:25: « graphe de 7 phases ordonnées », puis ligne 88 « exécute les 7 phases sur le VPS » — DEUX transcriptions du nombre de phases dans la même fiche.05_deliverables_mvp/devops/deploy_runbook/README.md:94: « renseigner les 7 confirmations » + énumération de ces 7 confirmations (lignes 55-58).- L'artefact source est byte-gaté par
check_artifacts:devops/deploy_runbook/out/MANIFEST.jsonrecomptecounts.phases(=len(graph.phase_order)) etcounts.confirmations(=len(confirmations_ouvertes)) à chaque build. - Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter une phase au graphe OU une confirmation au catalogue ferait dériver les docs en silence. Piège aveugle caractéristique : phases ET confirmations valent tous deux 7 aujourd'hui — un gate qui n'en recompterait qu'un serait aveugle à la dérive de l'autre.
Nouveau bloc dans ci/check_readme_claims.sh : recompute chaque nombre
depuis son propre champ du MANIFEST (jamais figé ici · zéro duplication du
compteur du générateur #5) et exige l'égalité avec les docs. Le 22 (« suites
gated » de la fiche devops) est volontairement écarté : sa sémantique (suites
CI) ≠ counts.modules du run-book (qui s'auto-exclut) → pas de transcription propre,
pas de gate hasardeux. Bonus anti-dérive : l'énumération des confirmations du
README (tokens snake_case) doit être l'ensemble exact de
confirmations_ouvertes — mord l'échange d'un nom là où le seul compte reste
aveugle. Cohérences croisées : counts.phases == len(phase_order) ·
counts.confirmations == len(confirmations_ouvertes).
Preuve (5 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| fiche devops phases 7→8 (1re transcription) | ✗ dit 8 MAIS artefact 7 · exit 1 |
| fiche devops 2e transcription « exécute les 7 »→6 (aveugle si 1re reste 7) | ✗ dit 6 MAIS artefact 7 · exit 1 |
| README confirmations 7→8 (count) | ✗ dit 8 MAIS artefact 7 · exit 1 |
| README swap d'un nom de confirmation (count reste 7, aveugle au compte) | ✗ énumération ≠ confirmations_ouvertes · exit 1 |
| README claim confirmations supprimé (reformulé) | ✗ claim INTROUVABLE · exit 1 |
| restauré | ✓ 7 phases + 7 confirmations + énumération == artefacts · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « run-book de déploiement VPS »).
Suite CI complète re-verte : check_artifacts · check_readme_claims ·
check_docs · check_regression · check_ci_integrity (gates statiques
verrouillés) · guard_constraints · validate_json.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_103044 · Buffer S8 · Domaine Legal : la synthèse du DocType « CONFOTUR Application » (« 18 champs · 14 de donnée · 4 sections · 3 rôles · 2 évènements de dépôt ») était transcrite À LA MAIN dans DEUX docs (README du module legal/confotur + fiche agent onapi_legal) sans AUCUN gate — ajouter un champ/section/rôle au spec ferait dériver les deux docs en silence
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage). Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les comptes data-derived des domaines tests, portails, RBAC (3 volets), SEO, CRM (pipeline vente) et mobile. Cette session cible le domaine Legal — la structure du DocType CONFOTUR, dont un quintuplet data-derived cité dans deux docs pointait l'artefact généré sans être recomputé par aucun gate.
Défaut trouvé — comptes du DocType ungated, transcrits dans DEUX docs
05_deliverables_mvp/legal/confotur/README.md:17: ligne de synthèse « 18 champs (14 de donnée) · 4 sections · 3 rôles · soumissible · 2 évènements de dépôt ».03_agents/onapi_legal/AGENT.md:28: cellule de table reprenant les mêmes 5 nombres (chacun en gras), dérivés du même spec (source unique · #5).- Pourquoi non gaté :
check_readme_claimscouvrait tests, portails, RBAC, SEO, CRM pipeline et mobile — jamais la structure du DocType CONFOTUR.check_docsne valide que les liens ;check_artifactsprouve quelegal/confotur/out/MANIFEST.jsonest reproductible byte-for-byte (bloccounts= {fields, data_fields, sections, permissions, roles, depot_events} recomptés deconfotur_spec.jsonà chaque build) mais ne compare pas ce que les DEUX docs en écrivent. Ajouter un champ/section/rôle ferait dériver les deux docs en silence — même « vert trompeur ». - État courant : aucun nombre périmé — 18/14/4/3/2 recoupe MANIFEST exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated (faux-vert latent), pas un chiffre faux.
Fix — extension de check_readme_claims.sh (recompute les 5 volets depuis MANIFEST)
Nouveau bloc qui RECOMPUTE le quintuplet (fields, data_fields, sections, roles, depot_events) depuis legal/confotur/out/MANIFEST.counts (source déjà byte-gatée ·
zéro duplication du compteur du générateur) et exige l'égalité avec les deux docs :
la ligne du README (sans gras) et la cellule de la fiche onapi_legal (chaque
nombre en gras). Recomputer les 5 volets (et pas le seul total de champs) mord une
dérive de N'IMPORTE lequel là où un seul nombre resterait aveugle. Un claim absent
échoue AUSSI. En-tête du gate + ci/README.md (§ table + § détail) mis à jour.
Preuves de morsure
| Injection | Résultat |
|---|---|
README sections 4→5 |
exit 1 dit 18/14/5/3/2 MAIS MANIFEST dit 18/14/4/3/2 |
Fiche champs 18→19 (README intact) |
exit 1 dit 19/14/4/3/2 MAIS MANIFEST 18/14/4/3/2 — mord la 2e doc |
Fiche évènements dépôt 2→3 |
exit 1 dit 18/14/4/3/3 MAIS MANIFEST 18/14/4/3/2 — mord le dernier volet là où le total de champs est aveugle |
| Claim disparu (README reformulé) | exit 1 synthèse DocType INTROUVABLE (attendu champs=18 …) |
| Restauré | exit 0 (2 docs verts) |
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact) |
| Matrice de régression | 564/22 · PASS inchangée (ni tests/ ni doc-evidence touchés) |
| Diff | 2 fichiers (ci/check_readme_claims.sh + ci/README.md) · 0 untracked |
Anti-invention (#6) : rien inventé — le gate recompute les 5 volets depuis
confotur MANIFEST.counts au lieu de figer un compteur ; les nombres des deux docs
découlent de l'artefact.
Hors périmètre worker (VPS · #8) : néant (gate python/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_093041 · Buffer S8 · Domaine CRM (non couvert hors prose « tests ») : le pipeline vente « 9 états · 11 transitions » était transcrit À LA MAIN dans DEUX README de module (titre workflow_vente + « 9 états du pipeline » de dossier_vente, même compte du même graphe) sans AUCUN gate — ajouter un état ferait dériver les deux docs en silence
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage). Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les comptes data-derived des domaines tests, portails (structure), RBAC (3 volets) et SEO (mots-clés). Cette session cible le domaine CRM — la structure du pipeline vente, dont un compte data-derived cité dans deux README de module pointait le graphe généré sans être recomputé par aucun gate.
Défaut trouvé — comptes du pipeline ungated, transcrits dans DEUX docs
05_deliverables_mvp/crm/workflow_vente/README.md:23: titre de section « ## Le pipeline (9 états · 11 transitions) ».05_deliverables_mvp/crm/dossier_vente/README.md:30: « options = les 9 états du pipeline » — même compte d'états, dérivé du même graphe (source unique · #5), recopié à la main.- Pourquoi non gaté :
check_readme_claimscouvrait tests, portails, RBAC et SEO — jamais la structure du pipeline CRM.check_docsne valide que les liens ;check_artifactsprouve quecrm/workflow_vente/out/MANIFEST.jsonest reproductible byte-for-byte (bloccounts= {states, transitions, …} recomptés deworkflow_vente_spec.jsonà chaque build) mais ne compare pas ce que les DEUX docs en écrivent. Ajouter un état/une transition ferait dériver les deux docs en silence — même « vert trompeur ». - État courant : aucun nombre périmé — 9/11 recoupe MANIFEST exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated (faux-vert latent), pas un chiffre faux.
Fix — extension de check_readme_claims.sh (recompute states + transitions depuis MANIFEST)
Nouveau bloc qui RECOMPUTE le couple (states, transitions) depuis
crm/workflow_vente/out/MANIFEST.counts (source déjà byte-gatée · zéro
duplication du compteur du générateur) et exige l'égalité avec les deux docs :
le titre du README workflow (## Le pipeline (N états · M transitions)) et le
compte d'états du README dossier_vente (les **N états** du pipeline). Recomputer
transitions en plus de states mord une dérive du 2ᵉ nombre là où le compte
d'états seul (repris dans dossier_vente) resterait aveugle. Un claim absent
échoue AUSSI. En-tête du gate + ci/README.md (§ table + § détail) mis à jour.
Preuves de morsure
| Injection | Résultat |
|---|---|
README workflow états 9→10 (titre) |
exit 1 dit 10/11 MAIS MANIFEST dit 9/11 |
README workflow transitions 11→12 (états inchangés) |
exit 1 dit 9/12 MAIS MANIFEST dit 9/11 — mord le 2ᵉ nombre |
README dossier_vente états 9→8 (2e doc) |
exit 1 dit états=8 MAIS MANIFEST dit états=9 — mord là où le titre seul est aveugle |
| Claim disparu (dossier reformulé sans le motif) | exit 1 claim « N états du pipeline » INTROUVABLE (attendu états=9) |
| Restauré | exit 0 (2 docs verts) |
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact ; aucun README/AGENT.md touché) |
| Matrice de régression | 564/22 · PASS inchangée (ni tests/ ni doc-evidence touchés) |
| Diff | 2 fichiers (ci/check_readme_claims.sh + ci/README.md) · 0 untracked |
Anti-invention (#6) : rien inventé — le gate recompute states + transitions depuis
workflow_vente MANIFEST.counts au lieu de figer un compteur ; les nombres des deux
docs découlent de l'artefact.
Hors périmètre worker (VPS · #8) : néant (gate python/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_090037 · Buffer S8 · Domaine SEO : le total ET la ventilation par langue des mots-clés (« 258 mots-clés (fr=87 · en=87 · es=84) »), transcrits À LA MAIN dans DEUX docs (table du README module + ligne de la fiche agent), n'étaient couverts par AUCUN gate — ajouter un projet ferait dériver les deux docs en silence
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage). Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les comptes data-derived des domaines tests, portails (structure), RBAC (3 volets). Cette session cible le domaine SEO, dernier grand livrable dont un compte data-derived cité dans deux docs pointait un artefact sans être recomputé par aucun gate.
Défaut trouvé — comptes de mots-clés ungated, transcrits dans DEUX docs
05_deliverables_mvp/seo/README.md:15: table « Livrables » → «out/seo_keywords.json— 258 mots-clés (fr=87 · en=87 · es=84) ».03_agents/seo/AGENT.md:24: même ligne, mêmes 4 nombres recopiés.- Pourquoi non gaté :
check_readme_claimscouvrait les comptes tests, les triplets par workspace des portails et les 3 volets RBAC — jamais le domaine SEO.check_docsne valide que les liens ;check_artifactsprouve queseo/out/MANIFEST.jsonest reproductible byte-for-byte (bloccounts= {keywords_total, keywords_per_lang{fr,en,es}} recomptés deprojets_master.jsonà chaque build) mais ne compare pas ce que les DEUX docs en écrivent. Ajouter un projet / du vocabulaire éditorial ferait dériver les deux docs en silence pendant que l'artefact dit autre chose — même « vert trompeur ». - État courant : aucun nombre périmé — 258=(87+87+84) recoupe MANIFEST exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated (faux-vert latent), pas un chiffre faux.
Fix — extension de check_readme_claims.sh (recompute total + 3 langues depuis MANIFEST)
Nouveau bloc qui RECOMPUTE le quadruplet (total, fr, en, es) depuis
MANIFEST.counts (source déjà byte-gatée · zéro duplication du compteur du
générateur) et exige l'égalité avec les deux docs. Recomputer les trois
langues (pas seulement le total) attrape une compensation entre langues
(fr −1 / en +1) qui laisserait le total juste et la ventilation fausse. Un claim
absent échoue AUSSI. En-tête du gate + ci/README.md (§ table + § détail) mis à jour.
Preuves de morsure
| Injection | Résultat |
|---|---|
README total 258→259 |
exit 1 dit 259/87/87/84 MAIS MANIFEST 258/87/87/84 |
Fiche compensation fr=87/en=87 → fr=86/en=88 (total inchangé) |
exit 1 dit 258/86/88/84 MAIS MANIFEST 258/87/87/84 — mord là où le total seul est aveugle |
| Claim disparu (README) | exit 1 claim mots-clés INTROUVABLE (attendu total=258 …) |
| Restauré | exit 0 (2 docs verts) |
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact ; aucun README/AGENT.md touché) |
| Matrice de régression | 564/22 · PASS inchangée (ni tests/ ni doc-evidence touchés) |
| Diff | 2 fichiers (ci/check_readme_claims.sh + ci/README.md) · 0 untracked |
Anti-invention (#6) : rien inventé — le gate recompute total + 3 langues depuis
MANIFEST.counts au lieu de figer un compteur ; les nombres des deux docs découlent de l'artefact.
Hors périmètre worker (VPS · #8) : néant (gate python/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_080025 · Buffer S8 · Hors du domaine « tests » : les comptes PAR WORKSPACE (cartes/liens/rôles) des 5 portails frontend, transcrits À LA MAIN dans DEUX docs (table du README module + prose de la fiche agent), n'étaient couverts par AUCUN gate — ajouter un rôle/DocType au contrat RBAC ferait dériver les deux docs en silence
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage). Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les comptes du domaine tests (sommes, cellules par-suite, agrégats en prose). Cette session sort de ce domaine : audit des comptes data-derived cités dans les fiches et README de module qui pointent un artefact mais n'étaient recomputés par aucun gate.
Défaut trouvé — comptes par workspace ungated, transcrits dans DEUX docs
05_deliverables_mvp/frontend/portails/README.md:29-33: table « Portail | Workspace | Cartes | Liens | Rôles restreints » — 5 lignes × 3 nombres saisis à la main.03_agents/frontend_console/AGENT.md:28: prose « 5Workspacenatifs (OTO Ventes4 cartes/11 liens/12 rôles ·OTO Construction4/9/10 ·OTO Achat3/8/5 ·OTO Compta4/11/8 ·OTO Direction4/14/9) » — mêmes triplets recopiés.- Pourquoi non gaté :
check_readme_claimscouvrait les comptes de tests (README racine, fiches QA/Backend, cellules par-suite, prose CRM/RBAC/e-CF) etaudit_4big/acceptance— jamais les comptes de structure des portails.check_docsne valide que les liens ;check_artifactsprouve quefrontend/portails/out/MANIFEST.jsonest reproductible byte-for-byte (tableauworkspaces= {nb_cartes, nb_liens, nb_roles} recalculés depuis le contrat RBAC à chaque build) mais ne compare pas ce que les DEUX docs en écrivent. Ajouter un rôle/DocType au contrat ferait dériver les deux docs en silence pendant que l'artefact dit autre chose — même « vert trompeur » que les cellules « Tests ». - État courant : aucun nombre périmé — les 15 triplets et le total « 5 » des deux docs recoupent MANIFEST exactement (anti-invention #6 : rien à réécrire). Le défaut est la surface ungated (faux-vert latent), pas un chiffre faux.
Fix — extension de check_readme_claims.sh (recompute par workspace depuis MANIFEST)
Nouveau bloc qui, pour chaque workspace de MANIFEST.workspaces (source déjà
byte-gatée · zéro duplication du compteur du générateur), RECOMPUTE le triplet
(nb_cartes, nb_liens, nb_roles) et exige l'égalité avec les deux docs : la ligne
de table du README (ancrée | <portail> | \` | C | L | R |) **et** la cellule de prose de la fiche (`` C cartes/L liens/R rôles, forme abrégée C/L/Rtolérée). + contrôle du **total** « NWorkspacenatifs » == len(workspaces). Un workspace **absent** d'un doc échoue AUSSI. En-tête du gate +ci/README.md`
(§ table + § détail) mis à jour.
Preuves de morsure
| Injection | Résultat |
|---|---|
README table OTO Compta liens 11→12 |
exit 1 README dit 4/12/8 MAIS MANIFEST dit 4/11/8 |
Fiche prose OTO Achat 3/8/5→3/8/6 (table inchangée) |
exit 1 fiche dit 3/8/6 MAIS MANIFEST 3/8/5 — mord là où la table seule est aveugle |
Cellule disparue (OTO Direction retirée de la prose) |
exit 1 claim INTROUVABLE (attendu 4/14/9) |
Total 5→6 Workspace natifs |
exit 1 fiche dit 6 MAIS MANIFEST dit 5 |
| Restauré | exit 0 (10 lignes doc + total verts) |
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact ; aucun README/AGENT.md touché) |
| Matrice de régression | 564/22 · PASS inchangée (ni tests/ ni doc-evidence touchés) |
| Diff | 2 fichiers (ci/check_readme_claims.sh + ci/README.md) · 0 untracked |
Anti-invention (#6) : rien inventé — le gate recompute chaque triplet depuis
MANIFEST.workspaces au lieu de figer un compteur ; les nombres des deux docs
découlent de l'artefact.
Hors périmètre worker (VPS · #8) : néant (gate python/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_073024 · Buffer S8 · Dernière surface de la classe « compte par suite » : les agrégats de tests rédigés EN PROSE dans deux fiches (crm « 81 (25+31+25) » · erpnext_backend « RBAC 60 (10+11+12+11+16) + e-CF 39 ») n'étaient couverts par AUCUN gate — une compensation entre suites laisserait la table gatée juste et la prose fausse
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage).
Poursuite de la série anti-dérive (CLAUDE.md #6). La session précédente a gaté les
cellules « Tests » par suite des tables de fiches (recompute générique depuis
plan.suites[path].test_methods), motivée par le fait qu'une compensation +1/−1
entre deux suites passerait sous la seule somme agrégée. Recherche de la même classe
un cran au-dessus : les agrégats rédigés en prose (hors table) que ce recompute
par-cellule ne voit pas.
Défaut trouvé — 2 agrégats en PROSE ungated (total + composants saisis à la main)
03_agents/crm/AGENT.md:32: « Total CRM : 81 tests (25 + 31 + 25) ».03_agents/erpnext_backend/AGENT.md:39: « Total backend RBAC 60 tests (10 + 11 + 12 + 11 + 16) + e-CF 39 tests ».- Pourquoi non gaté :
check_readme_claimsrecompute les cellules de table (row_reexige un lien…/README.md+ dernier champ numérique) — ces phrases en prose n'en sont pas. Le total agrégé du repo (564) est gaté ailleurs mais ne couvre pas ces sous-totaux par domaine ni leurs composants. Une compensation entre deux suites CRM (l'une +1, l'autre −1) laisserait la table (gatée) juste, la somme repo (564) juste, et la prose « (25 + 31 + 25) » fausse en silence — exactement le piège per-suite corrigé la session précédente, au niveau agrégé. - État courant : aucun nombre périmé — 81=(25+31+25), RBAC 60=(10+16+11+11+12),
e-CF 39=fiscal/ecf_dgii, tous recoupés sur
plan.suites. Le défaut est la surface ungated (faux-vert latent), pas un chiffre faux (anti-invention #6 : rien à réécrire).
Fix — extension de check_readme_claims.sh (recompute total + multiset composants)
Nouveau bloc qui, pour chaque agrégat en prose, RECOMPUTE depuis auth
(plan.suites[path].test_methods, déjà chargé · zéro duplication) :
- le total == somme des suites du domaine ;
- le multiset des composants (trié, ordre-indépendant) == multiset des comptes par suite — c'est ce qui attrape la compensation que la somme seule rate ;
- la cohérence interne (total == somme des composants écrits).
- un contrôle à suite unique pour e-CF (pas de parenthèses). Un claim absent
échoue AUSSI. En-tête du gate +
ci/README.md(§ table + § détail) mis à jour.
Preuves de morsure
| Injection | Résultat |
|---|---|
Total périmé 81→82 |
exit 1 dit total=82 … MAIS source dit total=81 (interne_ok=False) |
Compensation (25+31+25)→(24+31+26) (somme=81 inchangée) |
exit 1 composants=[24,26,31] MAIS source [25,25,31] — le multiset mord là où la somme est aveugle |
| Claim e-CF disparu | exit 1 claim INTROUVABLE (attendu 39) |
| Restauré | exit 0 |
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact ; aucun AGENT.md touché) |
| Matrice de régression | 564/22 · PASS inchangée (ni tests/ ni doc-evidence touchés) |
| Diff | 2 fichiers (ci/check_readme_claims.sh + ci/README.md) · 0 untracked |
Anti-invention (#6) : rien inventé — le gate recompute total ET composants depuis
plan.suites au lieu de figer un compteur ; les nombres de prose découlent de l'artefact.
Hors périmètre worker (VPS · #8) : néant (gate python/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_070019 · Buffer S8 · « Vert trompeur » ironique : le README de l'auditeur 4Big — le module qui COMPTE les modules — figeait son PROPRE total à la main (« 17/17 modules ») alors que son artefact quality_report.json en dit 22 ; ungated
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage).
Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les
comptes agrégés/par-suite des docs d'entrée (README racine, fiches QA/Backend, tables
« Tests »). Recherche du même faux-vert hors de ce périmètre : audit des comptes
N modules / N/N dans les README de module (pas seulement l'entrée + les fiches).
Défaut trouvé — 2 comptes vivants « 17 » périmés dans qa/audit_4big/README.md
05_deliverables_mvp/qa/audit_4big/README.md:68(« Résultat courant ») : « VerdictPASS— 17/17 modules à 100/100 » — compte vivant saisi à la main, périmé (17 → 22 au fil des ajouts de modules). Ironie complète : c'est le README du module qui COMPTE les modules ; son propre artefact faisant autoritéqa/audit_4big/out/quality_report.json(totals.modules) dit 22 depuis longtemps.…/README.md:38(règle du verdict global) : «PASSssi couverture 100 % et les 17 modules ≥ 95/100 » — la règle (« TOUS les modules ≥ 95 ») ne dépend pas d'un compte ; le « 17 » n'était qu'une surface de dérive de plus.- Pourquoi non gaté :
check_readme_claimsrecomputait22/22pour le README d'entrée (racine) mais ne couvrait pas ce README de module.check_docsne valide que les liens ; l'auditeur s'auto-exclut (SoD) donc son propreout/ne reflète pas l'édition de son README → aucun signal.
Fix (2 volets · CLAUDE.md #4 — corriger + supprimer la surface + garder)
README.md:38: retrait du compte figé → « tous les modules gated ≥ 95/100 » (la règle, count-free ⇒ zéro surface de dérive, précédent050007/053011).README.md:68:17/17→ 22/22 (compte courant, désormais gaté).ci/check_readme_claims.sh: nouveau bloc qui RECALCULE verdict +pass/modules/ min_scoredepuisquality_report.json(totals, déjà chargé dansq/qv· zéro duplication) et exige l'égalité avec ce que « Résultat courant » écrit. Un claim absent échoue AUSSI. Header du gate mis à jour (doc couverte).
Preuves de morsure
- Bite 1 (valeur périmée) : réinjecté
17/17⇒ gate exit 1✗ … dit PASS/17/17/100 MAIS quality_report dit PASS/22/22/100 → régénérer le README. - Bite 2 (claim disparu) : reformulé sans le motif ⇒ exit 1
✗ … claim INTROUVABLE. - Restauré ⇒ exit 0.
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (auditeur auto-exclu SoD ⇒ quality_report.json byte-identique ; 0 régénération) |
| Matrice de régression | 564/22 · PASS inchangée (README édité, aucun tests/) |
Résidu 17 vivant repo-wide |
aucun (seul restant = daily_report 2026-07-30 point-in-time, conservé) |
| Diff | 2 fichiers · +39/−3 · 0 untracked |
Anti-invention (#6) : rien inventé — le gate recompute la vérité depuis totals
au lieu de figer un compteur ; le 22/22 du README découle de l'artefact.
Hors périmètre worker (VPS · #8) : néant (doc + gate bash/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_053011 · Buffer S8 · Comptes vivants « 551 tests » périmés DANS la machinerie CI elle-même (.gitea/workflows/ci.yml + ci/check_regression.sh + ci/check_readme_claims.sh) — non couverts par aucun gate
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close (22/22 modules · 558 tests · 7 gates verts). Poursuite de
la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté les comptes vivants
des docs d'entrée (README, fiches QA/Backend) puis nettoyé la doc du CI (ci/README.md,
session 050007). Recherche du même faux-vert hors de ce périmètre : audit git grep '551'
repo-wide (pas seulement *.md) → la machinerie CI opérationnelle (workflow + headers de
gates), qu'aucun gate ne couvre.
Défauts trouvés — 3 comptes vivants « 551 » périmés (→ 558), dans le code des gates
.gitea/workflows/ci.yml:67: le commentaire du jobcheck-regressiondécrivait l'artefact commité comme « 22 suites · 551 tests · PASS, cité dans toute la doc » — compte vivant figé périmé (matrice 534 → 551 → 558).ci/check_regression.sh:8(l'ironie, encore) : le header du gate de fraîcheur de la matrice — dont le rôle même est d'empêcher le compte agrégé de se périmer — s'illustrait par « totaux « 22 suites · 551 tests · verdict PASS » cité partout ». Le gardien anti-péremption avait lui-même péri.ci/check_readme_claims.sh:152: l'anecdote décrivant le défaut QA disait « … alors que le run dit 22 / 551 » — présent de l'indicatif ⇒ prétention vivante du compte courant, désormais fausse (558). (Le « 21 suites · 534 tests » de la même ligne est l'anecdote historique point-in-time du défaut corrigé — conservé.)- Pourquoi non gaté :
check_readme_claimsne couvre que README + fiches QA/Backend ; ces fichiers mélangent narratifs historiques figés volontaires (progression « 534 → 551 → 558 ») et comptes vivants — exactement le motif qui a laisséci/README.mddériver (session050007).
Fix (2 solutions · CLAUDE.md #4 — retenu : supprimer la surface de dérive · précédent 050007)
Plutôt que réécrire 551→558 (qui re-dériverait au prochain ajout de suite), retrait du nombre vivant figé au profit d'un renvoi à l'artefact — « les totaux agrégés (suites · tests · verdict) dont la doc dérive, jamais figés en dur ici ». Zéro nombre à périmer = zéro surface de dérive, structurellement plus fort qu'un gate sur de la prose (#6).
ci.yml:67&check_regression.sh:8: compte figé → renvoi descriptif àregression_run.json.check_readme_claims.sh:152: « le run dit 22 / 551 » → « le run agrégé faisant autorité en disait déjà davantage (recompté ci-dessous, jamais figé ici) » — formulation identique à celle appliquée àci/README.mden session050007. Le 534 point-in-time reste comme valeur du défaut.- (Alt. écartée : étendre
check_readme_claimsàci.yml/ci/*.sh— fragile, il faudrait distinguer chaque progression historique figée d'un compte vivant ; le retrait est plus fort.)
Preuves
| Contrôle | Résultat |
|---|---|
git grep '551' hors logs/daily_reports/progression 534→551→558 |
aucun compte vivant résiduel ✅ |
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte des out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact) |
check_ci_integrity (câblage gate.needs inchangé) |
✅ |
| Diff | 3 fichiers · +9/−6 · 0 untracked |
Hors périmètre worker (VPS · #8) : néant (commentaires CI en-repo, aucun touch prod).
Anti-invention (#6) : rien inventé — retrait de nombres figés, la vérité reste recomputée
par les gates depuis regression_run.json.
Auto-score 4Big : 96/100.
Session 20260731_050007 · Buffer S8 · Chiffre vivant périmé DANS la doc du CI (ci/README.md) — le gate check_regression était illustré par un compte de tests faux, et check_readme_claims sous-décrit (2 docs au lieu de 3)
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close (22/22 modules · 558 tests · 7 gates verts au démarrage).
Poursuite de la série anti-dérive : la classe « chiffre inventé/périmé » (README, fiche
QA, fiche Backend) est gatée sur 3 docs ; recherche d'un même faux-vert hors de ce
périmètre. Audit repo-wide des agrégats (git grep '<N> suites|<N> modules|551|534'
sur *.md hors logs/archives) + relecture des 7 gates pour un bug de logique.
Défauts trouvés — 3 dérives réelles, toutes dans ci/README.md (doc du livrable DevOps)
ci/README.md:80(le pire) : la section décrivantcheck_regression— le gate censé empêcher les compteurs de se périmer — s'illustrait elle-même par un compte vivant périmé : « 22 suites · 551 tests · PASS ». La matrice faisant autorité (qa/regression/out/regression_run.json) dit 558 depuis la session043004(534 → 551 → 558) ; l'exemple appariait le compte de suites courant (22) à un compte de tests périmé (551). Ironie complète : la doc du gate anti-dérive avait elle-même dérivé.ci/README.md:130: la ligne anecdote de la fiche QA citait « le run réel22 / 551» au présent — 551 ne correspond plus à rien (→ 558).ci/README.md:121-131: la section§check_readme_claimsaffirmait garder « les deux docs d'entrée » (README + fiche QA) et omettait la fiche ERPNext Backend — 3ᵉ doc pourtant ajouté à la couverture du gate en session043004. Le header du scriptcheck_readme_claims.sh(source de vérité · l.23/29/32) liste bien les 3 ; seulci/README.mda dérivé → doc qui sous-décrit son propre code.- Pourquoi non gaté : session
030001a délibérément laisséci/README.mdhors decheck_readme_claimscar il porte des chiffres historiques figés volontaires (anecdotes « demo 18→21 », « 21 suites · 534 tests »). C'est ce mélange historique/vivant qui a laissé le compte vivant (551) se périmer en silence.
Fix (2 solutions · CLAUDE.md #4 — retenu : supprimer la surface de dérive + réaligner)
- Correctif de fond (l.80) : le compte vivant figé « 22 suites · 551 tests » est
supprimé au profit d'un renvoi — « le compte agrégé (suites · tests · passés ·
verdict) … recompté par
check_readme_claimsdans les fiches d'entrée, jamais figé en dur ici ». Plus de nombre à périmer = zéro surface de dérive (plus robuste qu'un gate sur une prose ; #6). (Alt. écartée : étendrecheck_readme_claimsàci/README.md— fragile, il faudrait distinguer chaque anecdote historique figée d'un compte vivant ; supprimer le nombre est structurellement plus fort.) - Anecdote historique (l.130) : « le run réel
22 / 551» (chiffre vivant faux) remplacé par « alors que le run agrégé faisant autorité en disait davantage » ; le 534 est conservé comme valeur point-in-time du défaut corrigé (comme « demo 18→21 »), désormais sans nombre concurrent périmé. - Réalignement (l.121-134) : « les deux docs d'entrée » → « les trois » +
ajout du bloc fiche Backend (
N tests · M suites · verdict → regression_run.json), miroir du header du script. La doc décrit à nouveau exactement le code.
Preuves
| Contrôle | Résultat |
|---|---|
git grep '551' *.md (hors logs/archives) |
aucun chiffre vivant résiduel ✅ |
534 restant |
1 seule ligne = anecdote historique explicite (l.131) ✅ |
« trois docs » + fiche Backend décrite dans ci/README.md |
✅ |
7 gates (guard/json/docs/artifacts/regression/ci_integrity/readme_claims) |
✅ exit 0 |
check_artifacts (rebuild + égalité byte de tous les out/) |
✅ (édition hors 05_deliverables_mvp ⇒ 0 dérive d'artefact) |
| Arbre de travail | ✅ 1 fichier modifié, 0 untracked |
Anti-invention (#6) : rien inventé — la vérité (558) vient de regression_run.json
(gaté par check_regression) ; le fix retire le compteur figé plutôt que d'en poser
un nouveau. Matrice 558/22 · PASS inchangée.
Non-régression : édition doc pure hors 05_deliverables_mvp → qa/audit_4big
ne la lit pas comme evidence ⇒ quality_report.json inchangé ; aucune régénération
de consommateur nécessaire.
Hors périmètre worker (VPS · #8) : néant (doc en-repo ; runner Gitea = DevOps).
Auto-score 4Big : 96/100.
Session 20260731_040004 · Sprint 7/8 · Livrable démo : ajout du prompteur Markdown (out/run_sheet.md) — le support texte lisible manquait au « scénario démo prêt à jouer »
Tâche : Sprint 7 l.68 (« CRM + Faisabilité : Scénarios démo P07 banquier ·
P05 client ready ») × Sprint 8 buffer. Après audit : les 7 gates CI étaient
verts, 15 promesses in_repo, faisabilité 4 volets complète. Le seul livrable démo
(demo/scenarios) ne produisait qu'un run-sheet JSON machine — aucun support
lisible par un présentateur. Son propre README plafonnait l'auto-score à 96/100
en qualifiant le « prompteur » de hors périmètre (VPS). Or rendre un Markdown
in-repo depuis le JSON est exactement le pattern déjà utilisé par faisabilite
(rend des .md) — aucune écriture VPS. Gap réel, borné, à forte valeur.
Livraison
scenlib/render.py(nouveau) —render_markdown(run_sheet) -> strpur et déterministe. Ne lit que le run-sheet (déjà résolu, déjà anti-inventé) : aucun chiffre nouveau, aucun recalcul (anti-invention #6)._fmt: bool→oui/non, liste→jointe, placeholder survivant rendu entre back-ticks (jamais promu en nombre).demo_scenario_gen.py buildémet désormaisout/run_sheet.mden plus des deux JSON. Le rendu est un prompteur par scénario : en-tête (audience/projet/durée/ objectif), puis chaque beat en table « À dire | Chiffre | Source (preuve) » — chaque ligne trace son pointeur RFC 6901 amont.out/run_sheet.md(nouveau, commité) — 2 scénarios · 12 beats · 24 min cumulés.
Compatibilité architecture (vérifiée AVANT d'écrire)
- HANDOFF 4Big (
q4lib/criteria.py:91) nejson.loadque les fichiers.json— un.mddansout/est ignoré, aucune casse du critère. ci/check_artifacts.shdiffe tout fichier produit parbuild→ il exige désormaisrun_sheet.mdcommité + reproductible byte-for-byte (render déterministe ⇒ OK). Preuve de morsure implicite : fichier non suivi ⇒ gate exit 1 (constaté puis corrigé pargit add).
Consommateurs régénérés (dérive maîtrisée)
- +7 méthodes test (
TestRenderMarkdown) → régression 551→558 tests (22 suites, PASS) :regression run+buildrégénérés (regression_run/plan/MANIFEST.json). demo/scenarios: README5011→5575octets +32→39test_* →quality_report.jsonrégénéré (evidence DOC+TESTS) · verdict PASS · 22/22 · min 100 inchangé.03_agents/qa/AGENT.md: verdict agrégé551→558(gatecheck_readme_claimsvert).- README démo : Fichiers (+
render.py, +run_sheet.md), chaîne de valeur, « hors périmètre » (seul l'habillage visuel reste VPS), auto-score 96→97.
Vérifs
| Contrôle | Résultat |
|---|---|
7 gates CI (guard/json/docs/artifacts/regression/readme_claims/ci_integrity) |
✅ exit 0 |
| Tests démo | ✅ 39/39 |
| Tests audit_4big | ✅ 34/34 |
| Régression exhaustive | ✅ 22/22 suites · 558 tests · PASS |
run_sheet.md reproductible (build frais == commité) |
✅ (gate artifacts) |
| Arbre de travail | ✅ 0 fichier untracked |
Hors périmètre worker (VPS · #8) : habillage visuel deck / page démo otov7.com +
branchement data_room réel — inchangé. Le prompteur texte est le rendu de référence
in-repo que ce visuel habillera.
Auto-score 4Big : 97/100.
Session 20260731_033001 · Buffer S8 · « Vert trompeur » : la fiche QA affichait un « verdict agrégé courant » PÉRIMÉ (21/534) en se disant « jamais compté à la main » — hors du périmètre du gate README
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA). Roadmap fonctionnellement
close (22/22 modules 100/100, 551 tests verts, 7 gates statiques). Poursuite de la
série anti-dérive : le gate check_readme_claims créé la session précédente ne garde que
README.md ; recherche d'une même classe de faux-vert hors de son périmètre.
Défaut trouvé — 3 chiffres périmés en SILENCE dans 03_agents/qa/AGENT.md et ci/README.md
- Mécanisme : la session
030001a corrigé le README et claimé avoir corrigé « 3 commentaires CI (21 suites · 534 tests) dansci.ymletcheck_regression.sh». Mais son propre gate ne couvre queREADME.md. Deux autres docs d'entrée portaient encore le compte périmé — invisibles pour tous les gates (check_docsne valide que les liens) :03_agents/qa/AGENT.md:35(le pire) : section « Verdict agrégé courant » → « 21 suites gated · 534 tests · 534 passés · 0 échec · 0 erreur → verdictPASS» en déclarant, ligne suivante, « jamais compté à la main ». C'était pourtant un compte figé à la main qui citeqa/regression/out/regression_run.jsoncomme source — alors que le run réel dit 22 / 551. Une fiche d'agent QA qui affirme une matrice verte avec des chiffres faux est le « vert trompeur » le plus grave.ci/README.md:80: exemple « compteurs cités partout » figé à « 21 suites · 534 tests · PASS » (→ 22 · 551).ci/README.md:184: checklist DevOps « les 6 gates statiques » (→ 7 depuis l'ajout decheck-readme-claims).
- Preuve au sol :
git grep -nE '534|21 suites|6 gates'sur*.md(hors logs) ramenait les 3 lignes ;regression_run.jsonrecalculé = 22 suites · 551 tests · 551 passés · 0 échec · 0 erreur · PASS (somme des 22 suites du run commité).
Fix (2 solutions · CLAUDE.md #4 — retenu : correction + extension du gate au-delà du README)
- Correction des 3 chiffres pour refléter les artefacts (fiche QA 21/534→22/551 ;
ci/README.mdexemple 21·534→22·551 et checklist 6→7 gates). Ligne 132 « Les 6 gates commençaient… » → « Les gates… » (le compte historique contredisait la table §1 à 7). - Correctif de fond : extension de
ci/check_readme_claims.sh(7ᵉ gate) au-delà du README. Il RECOMPUTE désormais aussi le « Verdict agrégé courant » de la fiche QA depuisregression_run.json(compte agrégé faisant autorité, commité · déterministe) :N suites · M tests · M passés · E échec · E erreur · verdict, exige l'égalité avec ce qui est écrit dans03_agents/qa/AGENT.md. Un claim absent échoue AUSSI. (Alt. écartée : 2ᵉ gate dédié — même classe de contrôle « valeur↔artefact » ; l'ajouter au gate existant évite un doublon de câblage CI · #5.) Le nom de fichier reste inchangé (pas de ré-câblageci.yml/INV-B) ; en-tête du script +ci/README.md§1 table + §2 détail mis à jour pour la portée élargie (README et fiche QA).
Preuves de morsure
- Bite 1 (valeur périmée) : réintroduit « 21 suites · 534 tests » ⇒ gate exit 1
✗ Fiche QA … dit 21/534/534/0/0/PASS MAIS regression_run dit 22/551/551/0/0/PASS. - Bite 2 (claim disparu) : retire toute la ligne « Verdict agrégé » ⇒ exit 1
✗ Fiche QA · verdict agrégé — claim INTROUVABLE. - Restauré ⇒ exit 0 (les 6 claims README + le claim QA verts).
Anti-invention (#6) : rien inventé — le gate recompute la vérité depuis
regression_run.json (somme des suites du run commité) au lieu de figer un compteur ; les
3 chiffres corrigés sont tous recoupés sur cet artefact.
Non-régression : 7 gates locaux verts, dont check_artifacts (rebuild + égalité
byte de tous les out/) ⇒ aucune dérive d'artefact (l'audit_4big ne lit ni 03_agents/
ni ci/ comme evidence → pas de régénération quality_report). Matrice 551/22 · PASS
inchangée. Unique « 534 » résiduel = description historique volontaire du défaut
corrigé (ci/README.md:130, comme l'anecdote « demo 18→21 »).
Hors périmètre worker (VPS · #8) : néant (stdlib pur en-repo ; runner Gitea toujours du ressort DevOps).
Auto-score 4Big : 96/100.
Session 20260731_030001 · Buffer S8 · « Vert trompeur » : le README (point d'entrée) affichait des chiffres PÉRIMÉS que sa propre source contredisait — aucun gate ne les enforçait
Tâche : Sprint 8 · buffer L75 (DevOps CI/CD · QA). Roadmap fonctionnellement
close (22/22 modules 100/100, 551 tests verts, 6 gates statiques). Poursuite de la
série anti-dérive : recherche d'un faux-vert réel (une affirmation présentée comme
vérifiée mais qu'aucun gate ne vérifie), même classe que les bugs regression_run.json
/ out/ RBAC / cd "" des sessions précédentes.
Défaut trouvé — les chiffres du README dérivaient en SILENCE vs leurs artefacts cités
- Mécanisme :
README.mdest le point d'entrée du mandat ; sa section « État courant (sourcé) » déclare mot pour mot « Chaque chiffre ci-dessous est sourcé vers un artefact commité (anti-invention #6) ; ce README n'introduit aucune donnée nouvelle. » Cette promesse n'était gardée par aucun gate :check_docs.shne valide que les liens (la cible existe), jamais la VALEUR des nombres. Quand un module + son job CI ont été ajoutés au fil des sprints, les chiffres du README se sont périmés en silence tout en restant « sourcés » vers un artefact qui, lui, dit autre chose — un README qui se contredit avec sa propre source est le pire « vert trompeur » (CLAUDE.md #6 : « zéro invention de chiffres »), appliqué à la doc d'entrée. - Preuve au sol (3 dérives réelles constatées) :
- README : « 21/21 modules gated à 100/100 » —
qa/audit_4big/out/quality_report.jsontotalsdit 22/22 (min_score 100, verdict PASS) ; - README : « 21 suites gated » —
qa/regression/out/regression_plan.jsontotals.suitesdit 22 (551 tests) ; - README : « 15 promesses… 14
in_repoprouvées + 1out_of_scope» —qa/acceptance/out/acceptance_matrix.jsona 15in_repo(M5 est désormaisin_repoavec son hors-périmètre tracé per-promesse ; 0 promesse horsin_repo). Les liens étaient tous verts (check_docsOK) : la dérive était invisible.
- README : « 21/21 modules gated à 100/100 » —
Fix (2 solutions · CLAUDE.md #4 — retenu : correction README + gate qui l'enforce)
- Correction des 3 chiffres du README (21→22 modules, 21→22 suites, « 14+1 » → « 15 in_repo ») pour refléter les artefacts.
- Correctif de fond : nouveau gate
ci/check_readme_claims.sh(7ᵉ gate statique) qui RECOMPUTE chaque chiffre depuis l'artefact cité (jamais une liste à la main · #6) et exige l'égalité avec ce qui est écrit dans le README : modules/verdict (audit_4big), suites (régression), promesses/statuts/verdict (acceptance), nombre d'AGENT.md(×2 : nav + titre). Un claim absent du README échoue AUSSI (la dérive de formulation qui ferait disparaître un chiffre est elle-même une régression de traçabilité). (Alt. écartée : régénérer le README depuis un template — surdimensionné et fragile pour une section en prose ; un gate qui compare valeur↔source est la preuve la plus forte et la moins couplée à la mise en forme.) - Câblage CI : job
check-readme-claimsajouté àci.yml+ inséré dansgate.needs(30 jobs bloquants désormais).check_ci_integrity(INV-A/INV-B) valide le câblage :gate.needs== 32 jobs − 1 (gate) − 1 manuel ;ci/check_readme_claims.shlancé parcheck-readme-claims∈ gate, et sourceci/lib.sh(pas de code mort, échoue enexit 3hors arbre git comme les 6 autres).
Preuves de morsure
- Bite 1 (valeur périmée) : réintroduit « 21/21 modules » ⇒ gate exit 1
✗ README dit 21/21/100 MAIS artefact dit 22/22/100 → régénérer README. - Bite 2 (claim disparu) : retire
**22 suites gated**⇒ gate exit 1✗ claim INTROUVABLE dans README (attendu suites=22). - Restauré ⇒ exit 0 vert. Gate inerte tant que README == artefacts.
Anti-invention (#6) : rien inventé — le gate recompute la vérité depuis les
artefacts au lieu de figer un compteur ; les 3 chiffres corrigés sont tous recoupés
sur quality_report/regression_plan/acceptance_matrix.
Nettoyage cohérence (#5 · éliminer le vieux) : 3 commentaires CI mentionnaient
encore « 21 suites · 534 tests » (désormais 22 · 551) — corrigés dans
.gitea/workflows/ci.yml et ci/check_regression.sh. ci/README.md : table §1 + détail
§2 + commande §3 du nouveau gate ajoutés.
Vérifs : 7 gates locaux verts (guard_constraints/validate_json/check_docs/
check_artifacts/check_regression/check_ci_integrity/check_readme_claims) ;
ci.yml YAML valide ; check_ci_integrity : gate.needs = 30 jobs (32 − gate
− e2e-baseline manuel), 7 ci/*.sh câblés + lib.sh sourcée par les 7. Artefacts out/
inchangés (aucun module 05_deliverables_mvp/ touché ⇒ pas de régénération).
Hors périmètre worker (VPS · #8) : néant (stdlib pur en-repo ; enregistrement du runner Gitea toujours du ressort DevOps).
Auto-score 4Big : 96/100.
Session 20260731_015954 · Buffer S8 · « Vert trompeur » : les 6 gates ci/*.sh passaient VERT hors d'un arbre git en ne contrôlant RIEN
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs » · DevOps
CI/CD). Roadmap fonctionnellement close (21/21 modules 100/100, 534 tests verts,
6 gates statiques). Poursuite de la série anti-dérive : recherche d'un faux-vert
réel (gate qui passe sans rien vérifier), même classe que les bugs
regression_run.json / out/ RBAC des sessions précédentes.
Défaut trouvé — cd "$(git rev-parse --show-toplevel)" échoue en SILENCE hors d'un checkout git
- Mécanisme : les 6 gates commençaient par
cd "$(git rev-parse --show-toplevel)". HORS d'un arbre de travail git (tarball,git archive | tar -x,gitabsent du PATH, working-dir erroné),git rev-parsen'écrit rien sur stdout → la commande devientcd "", un no-op qui RETOURNE SUCCÈS (le script reste dans le cwd au lieu d'échouer). Les gates poursuivaient sur ungit ls-filesrenvoyant une liste VIDE →validate_json(« Aucun fichier .json suivi — rien à valider »),guard_constraintsetcheck_docssortaient exit 0 VERT en ayant contrôlé ZÉRO fichier (≈50 JSON non validés, contraintes CLAUDE.md non vérifiées). Un gate qui passe au vert sans rien enforcer est le pire « vert trompeur » — précisément ce que la série buffer traque. - Preuve au sol (avant fix) : copie de l'arbre sans
.git→validate_jsonexit 0 « rien à valider » ;guard_constraintsexit 0 « Contraintes respectées » ;check_docsexit 0. Ce piège avait déjà faussé une vérif « 6 gates verts sur git archive » d'une session antérieure (ungit archive | tar -xn'a pas de.git, alors que le runner Gitea a un vrai.gitviaactions/checkout@v4— donc la méthode « archive » n'est PAS équivalente au runner pour tout check qui interroge git).
Fix (2 solutions · CLAUDE.md #4 — retenu : helper partagé DRY + durcissement INV-B)
- Correctif de fond : nouveau helper
ci/lib.sh(sourcé, jamais exécuté seul) exposantcd_repo_root— se positionne à la racine, ou échoue bruyamment (exit 3, code distinct d'un échec de contrôle1) avec un message clair sigit rev-parsene renvoie rien. Les 6 gates remplacent leurcd "$(git rev-parse …)"parsource "$(dirname "${BASH_SOURCE[0]}")/lib.sh" || exit 3 ; cd_repo_root. (Alt. écartée : inliner le garde de 4 lignes × 6 fichiers = doublon, contraire à CLAUDE.md #5 · préférence lib partagée.) - Durcissement du gate de câblage (INV-B ·
check_ci_integrity.sh) : INV-B exigeait que chaqueci/*.shsoit lancé par un job —ci/lib.sh(jamais dans unrun:) aurait été flaggé « gate mort ». INV-B accepte désormais unci/*.shcâblé de deux façons : lancé par un job du gate, OU sourcé par ≥1 gate script (détecté par grep d'une lignesource …/<base>/. …/<base>,#exclu). C'est un renforcement réel : le gate prouve maintenant aussi que les libs partagées sont effectivement sourcées (pas du code mort).
Preuves de morsure
- Faux-vert corrigé : arbre copié sans
.git→ les 6 gates sortent désormais exit 3 avec « ❌ ERREUR gate : hors d'un arbre de travail git » (avant :validate_json/guard_constraints/check_docs= exit 0 vert). Idem surgit archive HEAD | tar -xaprès commit. - INV-B reconnaît la lib :
ci/lib.sh — lib sourcée par [les 6 gates] · non exécutée en propre (OK)→ exit 0. - INV-B attrape une lib morte :
ci/_dead.sh(sourcé par personne) ⇒✗ … lancé/sourcé par AUCUN job CI (gate mort)⇒ exit 1.
Non-régression : 6 gates verts dans l'arbre de travail (= ce que voit le
runner via actions/checkout@v4, avec un vrai .git) ; matrice 534/21 · PASS
inchangée ; qa/audit_4big out/ inchangé (les édits ci/ sont hors
05_deliverables_mvp → aucune dérive de consommateur) ; ci.yml YAML valide.
Anti-invention (#6) : rien inventé — garde purement défensif (bash/git stdlib),
INV-B relit ci.yml + grepe les gates sur disque à chaque run.
Hors périmètre worker (VPS · #8) : néant (stdlib en-repo ; enregistrement du runner Gitea toujours du ressort DevOps).
Auto-score 4Big : 96/100.
Session 20260731_012944 · Buffer S8 · Reproductibilité en CHECKOUT PROPRE — le hand-off out/ des 4 générateurs RBAC était .gitignoreé → gate rouge en CI, vert en local seulement
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs » · DevOps
CI/CD). Roadmap fonctionnellement close (21/21 modules 100/100, 534 tests verts,
6 gates statiques). Poursuite de la série anti-dérive : recherche d'un écart
green-local / red-en-CI réel (même classe que le bug regression_run.json
corrigé plus tôt aujourd'hui) plutôt qu'un correctif cosmétique.
Défaut trouvé — 2 gates ROUGES en checkout propre (check-artifacts + check-regression)
- Méthode : test au sol par
git archive HEAD | tar -x(= exactement ce que voit le runner Gitea), et nonbash ci/*.shdans l'arbre de travail. - Constat : les 4 générateurs RBAC (
rbac/fixtures_gen,userperm_gen,roleprofile_gen,apply_plan).gitignoreaient leurout/(« re-générable à la demande ») — alors qu'ils sont audités en archétypegenerator(critère HANDOFF =out/MANIFEST.json+ ≥1 artefact JSON). En checkout propre, leurout/est absent → l'audit 4Big (qa/audit_4big, dont lebuildrelit leout/de CHAQUE module) les note 80 < 95 ⇒ INV7 ⇒ build refusé. Cascade :check-artifactsrouge (rebuild audit_4big + demo échouent) etcheck-regressionrouge. Ça ne « passait » qu'en local, grâce auxout/non suivis laissés sur disque par desbuildmanuels — exactement la classe du bugregression_run.json(green-local/red-CI). - Preuve au sol :
git archivedeHEAD→check_artifactsexit 1 (INV7 rbac-* note 80 < 95·demo/scenarios build a échoué) etcheck_regressionexit 1. Les 15 autres générateurs commettent bien leurout/→ seuls les 4 RBAC étaient l'exception.
Fix (2 solutions · CLAUDE.md #4 — retenu : aligner sur les 15 autres modules)
- Correctif de fond : committer le
out/des 4 générateurs RBAC (retrait deout/des 4.gitignore+git add). Faisabilité prouvée : chaquebuildest byte-déterministe (2 builds successifs identiques ; tri stable, zéro horodatage).apply_planlit ses frères en process (imports), pas via leurout/— committer leout/sert uniquement le contrat HANDOFF audité. L'audit note désormais les 4 modules 100/100 en checkout propre. (Alt. écartée : reclasser les 4 en archétype sans HANDOFF — diverge du reste des générateurs et laissecheck_artifactsles ignorer, moins cohérent · #5.) - Durcissement du gate (empêche la récidive) :
ci/check_artifacts.shcomparait le build frais au fichier de l'arbre de travail — donc unout/non suivi (ignoré / jamaisgit add) le rendait vert en local alors qu'il serait absent en CI. Ajout d'une assertiongit ls-files --error-unmatch: tout fichier produit parbuildDOIT être suivi par git → l'écart devient une erreur locale honnête, plus une surprise en CI. - Régénération des consommateurs (mémoire projet) : l'édition des 4 README RBAC
(en-tête
out/« non commité » → « commité · byte-déterministe », +18 o chacun) a fait dériverqa/audit_4big/out/quality_report.json(evidence DOC = taille README) — régénéré (PASS · 21/21 · min 100).demo+regression runrejoués : inchangés (ils lisaient déjà l'état correct).
Preuve de morsure du durcissement : git rm --cached role.json (fichier laissé
sur disque, simulant un out/ ignoré) ⇒ check_artifacts exit 1 (✗ … NON SUIVI par git → absent en CI propre) ; re-git add ⇒ exit 0. Avant le
durcissement, ce même état passait vert.
Vérifs : 6 gates verts sur git archive propre (guard/json/docs/artifacts/
regression/ci_integrity) ; suites harnais OK (fixtures/userperm/roleprofile/
apply_plan/audit_4big/regression/demo · 32/32) ; matrice 534/21 inchangée ;
build de TOUS les consommateurs → zéro dérive résiduelle.
Anti-invention (#6) : rien inventé — out/ RBAC = sortie déterministe des
générateurs ; quality_report recalculé depuis les faits du dépôt.
Hors périmètre worker (VPS · #8) : néant (stdlib pur en-repo ; les fixtures
Frappe côté VPS restent .gitignoreées, hors de ce correctif).
Auto-score 4Big : 96/100.
Session 20260731_005934 · Buffer S8 · Intégrité du câblage CI — le gate d'agrégat verrouille TOUS les jobs (gates statiques compris)
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs » · DevOps CI/CD). Roadmap fonctionnellement close (21/21 modules à 100/100, 534 tests verts, 5 gates statiques tous verts au démarrage). Poursuite de la série anti-dérive (INV4, fraîcheur matrice, reproductibilité artefacts) : recherche d'un trou de câblage réel plutôt qu'un correctif cosmétique.
Défaut trouvé — les gates STATIQUES n'étaient gardés par aucun invariant d'appartenance au gate
- Constat : le job
gate(needs: [...]) est le seul verrou de merge — un check absent de sonneeds:ne bloque rien, même rouge. Les deux couvertures existantes (audit_4big/registry.coverage_report,qa/regression/discovery. coverage_report) prouvent l'appartenance au gate uniquement pour les jobs de module (ceux portant unworking-directory:sous05_deliverables_mvp). Les 5 gates statiques (constraints-guard/validate-json/check-docs/check-artifacts/check-regression→ scriptsci/*.sh) n'ont pas de working-directory : rien ne garantissait qu'ils alimentent le gate. Retirer l'un d'eux deneeds:(ou une typo dans la liste, ou unci/*.shdécâblé) rendait le gate vert en se court-circuitant — même classe de dérive silencieuse que INV4 (orphan_tests_dirs), côté câblage CI. - Fix : nouveau gate
ci/check_ci_integrity.sh(bash/awk/git, stdlib pur, zéro réseau) + job CIcheck-ci-integrity, lui-même ajouté àgate.needs. Deux invariants, lus par FAITS dansci.yml(jamais une liste à la main) :- INV-A :
gate.needs== { tous les jobs définis } − {gate} − { jobs manuelsif: … workflow_dispatch …, ex.e2e-baselinelégitimement hors du gate push/PR }. Détecte MISSING (job oublié → ne bloque pas), DANGLING (référence fantôme : typo / job supprimé) et MANUAL-IN-GATE (job manuel dansneeds→ gate en attente perpétuelle sur push). - INV-B : chaque
ci/*.shdu dépôt est lancé par exactement un job (run: bash ci/<script>), lui-même dansgate.needs— un gate statique non câblé (script mort) ou décâblé casse le check.
- INV-A :
- Preuve de morsure (5 modes, tous exit 1) :
- MISSING :
check-artifactsretiré deneeds⇒✗ ABSENT de gate.needs+✗INV-B surcheck_artifacts.sh; - DANGLING :
seo-tests→seo-testzdansneeds⇒✗ référence 'seo-testz' non définie+✗ seo-tests ABSENT; - MANUAL-IN-GATE :
e2e-baselineajouté àneeds⇒✗ job manuel … gate en attente; - SCRIPT-MORT :
ci/check_phantom.shcommité sans job ⇒✗ lancé par AUCUN job; - DÉCÂBLÉ : couvert par le mode MISSING (job hors
needs). État nominal restauré ⇒ exit 0 (gate.needs = 28 jobs = 30 définis − 1 (gate) − 1 manuel [e2e-baseline]).
- MISSING :
Non-régression : le nouveau job est statique (aucun working-directory) →
comme les 5 autres, il n'entre pas dans la matrice de régression ni le registre
audit_4big (dérivés des seuls jobs de module). Totaux 534/21 inchangés.
Suites harnais OK : qa/regression (21 suites · 534), qa/audit_4big (21/21 ·
min 100), qa/acceptance (bijectif=True). ci.yml YAML valide.
Anti-invention (#6) : rien inventé — le gate relit ci.yml et recalcule les
ensembles à chaque run (aucun compteur figé).
Vérifs : 6 gates statiques verts (guard_constraints/validate_json/
check_docs/check_artifacts/check_regression/check_ci_integrity) ; doc
ci/README.md mise à jour (tableau des jobs, section 2 détaillée, exécution locale,
checklist « 6 gates »).
Hors périmètre worker (VPS · #8) : néant (stdlib en-repo ; enregistrement du runner Gitea toujours du ressort DevOps).
Auto-score 4Big : 96/100.
Session 20260731_002924 · Buffer S8 · Intégrité de la matrice de régression (bug CI réel + invariant disque→CI)
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs » · DevOps
CI/CD) — roadmap fonctionnellement close (21/21 modules gated à 100/100, 534
tests verts, 5 gates statiques). Recherche d'une incohérence de reproductibilité
réelle plutôt qu'un correctif doc cosmétique. Deux défauts réels trouvés et
corrigés, tous deux dans qa/regression.
Défaut #1 (PRIORITAIRE · bug CI réel) — le gate check-regression était cassé en checkout propre
- Symptôme :
ci/check_regression.sh(l.52-53) exige queqa/regression/out/regression_run.jsonexiste dans le dépôt, puis compare unrunfrais à ce fichier commité (baseline byte-identique). - Contradiction : ce même fichier était
.gitignoreé (« non déterministe (temps machine) → jamais commitée ») → jamais présent dans un checkout propre. Le job Giteacheck-regressionne fait quecheckout+bash check_regression.sh, aucune étape ne génère le fichier. Conséquence : le gate échouait sur CHAQUE run CI propre (artefact absent du dépôt). Il ne « passait » qu'en local, par la présence d'unregression_run.jsonnon suivi laissé par unrunmanuel. Le commentaire deci.ymldisait déjà « le regression_run.json commité » — intention claire du commit précédent, jamais concrétisée (fichier resté ignoré). - Vérif de faisabilité (#1 analyser) : l'artefact
runest byte-déterministe — clés{ran,passed,failures,...}+pathrelatif, aucun horodatage/hôte/ durée. Deuxrunsuccessifs byte-identiques (vérifié). Le committer est donc licite et complète l'intention du gate. - Fix :
runest désormais commité (baseline du gate) →.gitignore: retrait deout/regression_run.json+ note expliquant qu'il est commité parce que déterministe ;README.md: sectionrunréécrite (byte-déterministe · commité · baselinecheck_regression· distinction d'aveccheck_artifactsqui ne rejoue quebuild) ;out/regression_run.json: ajouté au suivi (21 suites · 534 tests · PASS).
- Preuve de morsure : fichier retiré (simule checkout propre) ⇒ gate exit 1
(
artefact absent) — l'état exact en CI ; restauré ⇒ exit 0 (run == commité).
Défaut #2 (invariant manquant) — dérive silencieuse disque→CI non gardée
- Gap :
discovery.coverage_reportprouvait le sens CI→disque (tout module gated a untests/) mais pas l'inverse : un module gagnant untests/sur disque sans job CI n'entrait jamais dans la matrice, ses tests ne tournaient jamais, et la couverture ne le voyait pas (elle n'itère que les suites dérivées du CI) — la matrice restait « PASS » en sous-comptant en silence. Même classe de dérive que « demo 18→21 », direction opposée. - Fix : nouvel invariant INV4 —
discovery.disk_test_modules(): marche disque pure listant tout module portant untests/(POSIX, trié, déterministe) ;coverage_report: champorphan_tests_dirs = disk − ci_paths;okexige désormaisnot orphan_tests_dirs;regression.schema.json:orphan_tests_dirsajouté (required · strict) ;regression_gen.py: message INV4 surface les orphelins ;tests/test_regression.py: +2 tests (sous-ensemble disque⊆CI sur le dépôt réel ; preuve de morsure monkeypatch d'un orphelin ⇒ok=False).
- Preuve de morsure end-to-end :
mkdir zz_orphan/tests+build⇒ build refuséINV4 … orphan_tests_dirs=['zz_orphan']; répertoire retiré ⇒ build OK.
Anti-invention (#6) : rien inventé — les deux gates régénèrent la vérité (run frais / couverture recomputée) au lieu de figer un compteur.
Régénération des consommateurs (mémoire projet) : l'édition des tests + du README
de qa/regression a fait dériver qa/audit_4big/out/quality_report.json (evidence
« README.md 3710→4236 octets » et « 24→26 méthodes test_* ») — régénéré, verdict
PASS · 21/21 · min 100. Totaux matrice 534/21 inchangés : le harnais
qa/regression est self-exclu (INV3) → +2 méthodes test_* n'affectent pas la
matrice.
Vérifs : 5 gates locaux verts (guard_constraints/validate_json/check_docs/
check_artifacts/check_regression) ; ci.yml YAML valide ; tests harnais
26/26 OK (régression) + 34/34 OK (audit_4big) ; matrice 534/534 · 21/21 ·
PASS ; artefacts out/ reproductibles à l'identique.
Hors périmètre worker (VPS · #8) : néant (stdlib pur en-repo ; enregistrement du runner Gitea toujours du ressort DevOps).
Auto-score 4Big : 96/100.
Session 20260731_043004 · Buffer S8 · Chiffre périmé dans une fiche agent (vert-trompeur ungated) + extension du gate
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs ») — roadmap fonctionnellement close (22/22 modules gated · 558 tests · 7 gates statiques verts). Recherche d'un défaut réel (chiffre inventé/périmé · CLAUDE.md #6) plutôt qu'un correctif cosmétique.
Défaut trouvé — 03_agents/erpnext_backend/AGENT.md : « repo : 560 tests au total »
- Symptôme : la ligne « source unique » situait les tests backend dans le repo par un compte agrégé saisi à la main (« repo : 560 tests au total »).
- Contradiction :
560ne correspond à AUCUN compte courant —- matrice de régression faisant autorité (
qa/regression/out/regression_run.json) : 558 tests · 22 suites · PASS ; - repo-wide (méthodes
test_*sur disque, 23 fichiers) : 584.560= ancien534(matrice d'alors)+ 26(harnaisqa/regression, self-exclu INV3) — périmé en silence au fil de 534 → 551 → 558. Même classe de « vert trompeur » que la fiche QA (21/534) corrigée la session précédente, mais dans une fiche agent qu'aucun gate ne couvrait.
- matrice de régression faisant autorité (
- Vérif ciblée (#1 analyser) : recomputé toutes les autres bornes chiffrées
des 13
AGENT.md—crm 81=(25+31+25),RBAC 60=(10+16+11+11+12),e-CF 39,portails 19,chat 31,confotur 44,seo 36: toutes exactes. Le560était le seul nombre dérivé.
Fix (2 volets · corriger + garder)
03_agents/erpnext_backend/AGENT.md:560→ « matrice de régression du repo : 558 tests · 22 suites · verdict PASS, sourceqa/regression/out/regression_run.json— jamais compté à la main · #6 » (le nombre pointe désormais vers l'artefact gaté, pas une saisie).ci/check_readme_claims.sh: nouveau bloc qui recalcule tests+suites+ verdict depuisregression_run(même source que la fiche QA) et exige l'égalité avec ce que la fiche backend écrit ; un claim absent échoue aussi. Header du gate mis à jour (liste des docs couverts).
Preuves
- Passe :
Fiche Backend · … 558/22/PASS == regression_run(✓). - Morsure :
560réinjecté ⇒ gate rouge (dit 560/22/PASS MAIS regression_run dit 558/22/PASS → régénérer la fiche) ; restauré ⇒ vert. - Régression nulle : édition d'un
AGENT.md(doc) → aucune dérive d'artefactout/(check_artifactsvert :quality_report.jsoninchangé).
Vérifs : 7 gates locaux verts (guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims) ; matrice 558/22 · PASS.
Anti-invention (#6) : rien inventé — le gate régénère la vérité (matrice re-agrégée) au lieu de figer un compteur ; le chiffre de la fiche découle de l'artefact.
Hors périmètre worker (VPS · #8) : néant (docs + gate bash/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_060012 · Buffer S8 · Traçabilité NON vérifiée : les n° de ligne roadmap_line de la matrice d'acceptation pouvaient pointer à côté en silence
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs ») — roadmap
fonctionnellement close (22/22 modules gated · 558 tests · 7 gates verts au démarrage).
Recherche d'un défaut réel (traçabilité CLAUDE.md #6) plutôt qu'un cosmétique.
Défaut trouvé — qa/acceptance : roadmap_line déclaré, jamais ancré
- Symptôme : chaque promesse de
acceptance_spec.json(8 livrables S1..S8 + 7 métriques M1..M7) porte unroadmap_line(33, 39, … 87) censé pointer son bullet dans04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md. Le docstring promet des « CITATIONS verbatim de la roadmap » (#6), mais le seul invariant (INV10) ne vérifiait que «roadmap_lineest un entier positif ». - Contradiction : si la roadmap est éditée (une ligne insérée/retirée, un sprint
ré-ordonné), tous les
roadmap_linese décalent en silence — ils pointeraient un bullet qui n'existe plus, tout en restant « vert ». Même classe de « vert trompeur » que les compteurs périmés (534→551→558) ou l'orphan_tests_dirs, appliquée cette fois à une référence de ligne (traçabilité déclarée mais non prouvée). - Vérif (#1 analyser) : recomputé la structure réelle → 8 bullets
**DELIVERABLE**(S1@33 … S8@76) + 7 bullets métrique- ✅(81..87). Lesroadmap_linedu spec sont exacts aujourd'hui — la faille est l'absence de garde, pas une valeur fausse.
Fix (2 volets · vérifier + garder)
acclib/deps.py— nouveauparse_roadmap_anchors(roadmap_ref)qui DÉRIVE la structure de la roadmap (jamais une liste de n° saisie · #6) :{SX → ligne du DELIVERABLE}(SX lu dans le titre## SPRINT n · SX (…) + lignes ordonnées des bullets métrique.OSErrorhonnête si la roadmap est absente (référence fantôme = red).acceptance_gen.py· INV11 (ancrage roadmap) — exige que (a) le nombre de bullets DELIVERABLE == nb de livrables du spec et nb de bullets métrique == nb de métriques (le « 8 + 7 » devient dérivé du fichier, plus un compte figé), (b) chaque livrable SX pointe le DELIVERABLE de SX, (c) chaque métrique (dans l'ordre) pointe le k-ième bullet- ✅. Docstring #6 mis à jour (« n° de ligne VÉRIFIÉ, pas déclaré »).- Tests — fixture roadmap synthétique (temp file, mêmes motifs en miniature :
S2@L3, S3@L5, M1@L7) pour contrôler l'écart injecté ; classe
TestRoadmapAnchor(+6 méthodes) : parse réel (8+7), DELIVERABLE désancré, métrique décalée, mauvais propriétaire de sprint, compte en trop, roadmap absente. 37 méthodes (31→37).
Preuves
- Passe : build/validate réel vert ;
parse_roadmap_anchorsréel = {S1:33 … S8:76} + métriques [81..87] ; les 15roadmap_linedu spec matchent. - Morsure (spec RÉEL) :
S1.roadmap_line=999⇒roadmap: S1: … ne pointe pas le DELIVERABLE de S1 (ligne 33);M3=200⇒… ≠ 3e bullet métrique (ligne 83).
Régénération des consommateurs (mémoire projet) : +6 méthodes test dans
qa/acceptance → matrice 558→564 (22 suites inchangées). Régénérés :
qa/regression/out/{regression_run,regression_plan,MANIFEST}.json (564/564 · PASS),
qa/audit_4big/out/quality_report.json (evidence acceptance « 31→37 méthodes », score
100/100 inchangé), fiches QA et Backend (558→564, gate check_readme_claims
vert), README acceptance (« 10→11 invariants »). L'artefact acceptance_matrix.json est
byte-identique (INV11 ne change que la validation, pas la sortie).
Vérifs : 7 gates locaux verts (guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims) ; matrice 564/22 · PASS ; 22/22 modules 100/100.
Anti-invention (#6) : rien inventé — INV11 dérive la vérité de la roadmap et compare ; le « 8 + 7 » cesse d'être un compte à la main.
Hors périmètre worker (VPS · #8) : néant (stdlib pur en-repo).
Auto-score 4Big : 96/100.
Session 20260731_063014 · Buffer S8 · Comptes de tests PAR SUITE périmés dans la fiche QA — dérive silencieuse (aucun gate ne couvrait le grain fin)
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs ») — roadmap fonctionnellement close (22/22 modules gated · 564 tests · 7 gates verts au démarrage). Chasse d'un défaut réel de la classe « vert trompeur » (#6) plutôt qu'un cosmétique.
Défaut trouvé — fiche QA : colonne « Tests » par suite figée, DÉJÀ fausse
- Symptôme : le tableau des livrables de
03_agents/qa/AGENT.md(col « Tests ») affichait un compte par suite saisi à la main. Trois lignes étaient déjà périmées au démarrage :audit_4big35 (réel 34),regression25 (réel 26),acceptance31 (réel 37 — la session précédente avait fait 31→37 sans propager à cette table). - Contradiction : le gate amont
check_readme_claimsne couvrait que la somme agrégée (« 564 tests »). Une compensation entre deux suites (+1 / −1) laisserait la somme juste et les deux lignes fausses — même classe de « vert trompeur » que les compteurs agrégés périmés (534→551→558→564) ou INV11, mais au grain le plus fin (par suite), et non gardée. - Vérif systématique (#1 analyser) : recomputé la carte faisant autorité des 22
suites gated + le self_module → diffusée contre toutes les tables de fiches
(
03_agents/*/AGENT.md). Seule la fiche QA dérivait (3 lignes) ; CRM (3), faisabilité (2), publiciste (1) étaient justes. READMEs de module : justes aussi.
Fix (2 volets · corriger + garder)
03_agents/qa/AGENT.md— 3 nombres corrigés vers le réel : 35→34, 25→26, 31→37.ci/check_readme_claims.sh— nouveau bloc générique : scanne toutes les fiches, extrait chaque ligne de table pointant05_deliverables_mvp/<path>/README.md- sa cellule « Tests », et recompute l'attendu depuis la source faisant
autorité —
regression_plan.json(suites[path].test_methods) pour les 22 suites gated, etreglib.discovery.count_tests(même fonction que le plan · zéro duplication · #5) pour leself_moduleqa/regressionexclu de la matrice par SoD. Table « Tests » absente ⇒ red (disparition = régression de traçabilité).
- sa cellule « Tests », et recompute l'attendu depuis la source faisant
autorité —
Preuves
- Passe : gate vert, 10 lignes par-suite vérifiées (crm×3 · faisabilite×2 · publiciste×1 · qa×4), toutes == source.
- Morsure :
acceptancereforcé à 31 ⇒✗ … Tests 31 MAIS source dit 37 (plan.suites/count_tests) → régénérer la fiche+ verdict ❌ ; restauration ⇒ PASS.
Régénération des consommateurs : néant — aucun compte de test n'a changé (fix
= docs + gate). Artefacts out/ byte-identiques (check_artifacts vert) ;
matrice 564/22 PASS inchangée.
Vérifs : 7 gates locaux verts (guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims) ; matrice 564/22 · PASS ; 22/22 modules 100/100.
Anti-invention (#6) : rien inventé — le gate recompute chaque cellule depuis plan.suites / count_tests ; les nombres de la fiche découlent de la source.
Hors périmètre worker (VPS · #8) : néant (docs + gate bash/stdlib en-repo).
Auto-score 4Big : 96/100.
Session 20260731_083034 · Buffer S8 · Comptes RBAC des 3 volets recopiés à la main — surface non gatée
Tâche : Sprint 8 · buffer L75 (« Regression tests exhaustifs ») — roadmap fonctionnellement close (22/22 modules gated · 564 tests · 7 gates verts au démarrage). Chasse d'une surface « vert trompeur » (#6) encore non gatée.
Défaut trouvé — 3 READMEs RBAC transcrivent à la main des comptes de MANIFEST byte-gatés
- Symptôme : les trois générateurs RBAC émettent chacun un
out/MANIFEST.jsonrecalculé du contratrbac_50_roles.jsonà chaque build (byte-gaté parcheck_artifacts⇒ autorité). Trois READMEs de module recopiaient à la main des comptes qui en dérivent, sans AUCUN gate :-
rbac/roleprofile_gen/README.md— table « Nb rôles » par profil (6 lignes- Ventes 12 · Construction 10 · Direction 9 · Compta 8 · Achat 5 · Plateforme
-
- total bijectif 50.
rbac/apply_plan/README.md— ligne agrégée « Génération réelle : 6 étapes · 50 rôles / 116 DocPerm / 28 UP templates / 6 Role Profile · 4 DocType custom + 5 Company + 4 rôles equipe » : 8 comptes de TOUS les volets en une phrase.rbac/userperm_gen/README.md— ventilation « 28 entite · 16 groupe · 2 own · 4 equipe » (par mécanisme d'enforcement).
-
- Contradiction latente : ajouter un rôle/DocType au contrat ferait dériver les trois docs en silence pendant que les MANIFEST (byte-gatés) disent autre chose — même classe que les cellules « Tests », la prose agrégée CRM/RBAC et les triplets par workspace, mais sur le domaine RBAC (contrainte NON-NÉGOCIABLE #5 « 50 rôles »), au grain le plus fin (par profil) et au niveau consolidé.
- État au démarrage : les 10 nombres étaient justes (aucune dérive déjà survenue) — le défaut est l'absence de garde, pas une valeur périmée.
Fix (1 volet · garder — aucun chiffre à corriger)
ci/check_readme_claims.sh— nouveau bloc RBAC recomputant chaque nombre depuis leout/MANIFEST.jsondu volet (zéro duplication des compteurs des générateurs · #6) et exigeant l'égalité avec ce que la doc ÉCRIT :- roleprofile : chaque
nb_rolespar profil (ancré sur nom+portail) + totalcounts.roles_couverts; - apply_plan : les 8 comptes (
len(apply_plan.json)·counts.{roles, custom_docperm,user_permission_templates,role_profiles}·len(confirmations_ vps.{custom_doctypes,companies,roles_scope_equipe})) en un n-uplet ordonné ; - userperm :
by_mechanism.{user_permission_company,none_consolidated, docperm_if_owner,vps_confirm_team}. Claim absent ⇒ red (disparition = régression de traçabilité). Header + ci/README documentés.
- roleprofile : chaque
Preuves
- Passe : gate vert, 10 nouvelles lignes RBAC vérifiées (6 profils + total
- agrégat apply_plan + ventilation userperm), toutes == MANIFEST.
- Morsures (3, une par surface) : Ventes 12→13 ⇒
✗ … dit 13 MAIS MANIFEST dit 12; apply_plan 116→117 DocPerm ⇒✗ … 6/50/117/… MAIS … 6/50/116/…; userperm 28→29 entite ⇒✗ … 29/16/2/4 MAIS … 28/16/2/4. Exit 1 dans les trois cas ; restauration ⇒ PASS (exit 0).
Régénération des consommateurs : néant — aucun compte n'a changé (fix = gate
seul). Artefacts out/ byte-identiques (check_artifacts vert) ; matrice
564/22 PASS inchangée.
Vérifs : 7 gates locaux verts (guard_constraints · validate_json ·
check_docs · check_artifacts · check_regression · check_ci_integrity ·
check_readme_claims) ; matrice 564/22 · PASS ; 22/22 modules 100/100.
Anti-invention (#6) : rien inventé — le gate recompute chaque nombre depuis les MANIFEST byte-gatés ; les comptes des READMEs découlent du contrat RBAC.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Sprint 8 · buffer L75 · Gate claims mobile — récap app Expo non gatée (onglets/rôles/langues/identifiants)
Diagnostic
- Symptôme : le générateur
mobile/app_configémetout/MANIFEST.jsondontcountsporte 4 nombres recomptés à chaque build depuis les sources faisant autorité —rbac_50_roles.json(portails métier + rôles →onglets,roles_couverts),seo/seo_spec.json(langues) et la listea_confirmer(credentials EAS/App Store #32/Play Store jamais fabriqués · #6/#8 →identifiants_a_confirmer). Leout/est byte-gaté parcheck_artifacts(build frais == commité) ⇒ artefact faisant autorité. - Contradiction latente : la ligne récap du README du module
(
05_deliverables_mvp/mobile/app_config/README.mdL66-67 — « 5 onglets · 44 rôles couverts · 3 langues · 13 identifiantsa_confirmer») recopiait ce quadruplet à la main, sans AUCUN gate. Ajouter un rôle/portail au contrat RBAC, une langue au site, ou un credential store ferait dériver le doc en silence pendant que le MANIFEST (byte-gaté) dit autre chose — même classe de « vert trompeur » que les mots-clés SEO (total + fr/en/es), les triplets par workspace des portails, ou le pipeline vente CRM. - État au démarrage : les 4 nombres étaient justes (5/44/3/13 == MANIFEST) — le défaut est l'absence de garde, pas une valeur périmée.
Fix (1 fichier gate + 1 doc CI · aucun chiffre à corriger)
ci/check_readme_claims.sh— nouveau bloc Mobile recomputant le quadruplet(onglets, roles_couverts, langues, identifiants_a_confirmer)depuismobile/app_config/out/MANIFEST.json · counts(zéro duplication du compteur du générateur · #6) et exigeant l'égalité avec ce que le README ÉCRIT. Recomputer les 4 volets (et pas le seul total de rôles) mord une dérive de N'IMPORTE lequel là où un seul nombre resterait aveugle. Un claim absent échoue AUSSI (INTROUVABLE = régression de traçabilité).ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « app mobile Expo »).
Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| onglets 5→6 | ✗ dit 6/44/3/13 MAIS MANIFEST 5/44/3/13 · exit 1 |
| rôles 44→43 | ✗ dit 5/43/3/13 MAIS MANIFEST 5/44/3/13 · exit 1 |
| langues 3→2 | ✗ dit 5/44/2/13 MAIS MANIFEST 5/44/3/13 · exit 1 |
| identifiants 13→12 | ✗ dit 5/44/3/12 MAIS MANIFEST 5/44/3/13 · exit 1 |
| claim supprimé | ✗ récap app INTROUVABLE · exit 1 |
| restauré | ✓ 5/44/3/13 == MANIFEST · exit 0 |
Suite CI complète re-verte : check_artifacts · check_readme_claims ·
check_docs · check_regression · check_ci_integrity (gates statiques
verrouillés) · guard_constraints.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Sprint 8 · buffer L75 · Gate chiffres audit 5D (contrôles/dimensions ×2 docs + ventilation verdict)
Contexte : poursuite de la fermeture des surfaces de « vert trompeur » non
gatées dans check_readme_claims.sh. Après RBAC, SEO, CRM pipeline, Mobile et
Legal CONFOTUR, restait la synthèse de l'audit de conformité 5D (livrable
qa/audit_5d), transcrite À LA MAIN dans DEUX docs sans aucun gate.
Dérive silencieuse fermée :
- Le README du module (
05_deliverables_mvp/qa/audit_5d/README.md) ouvre par « 17 contrôles répartis en 5 dimensions (5D) » ET affiche la ventilation du verdict « 13 PASS · 0 FAIL · 4 à confirmer ». - La fiche agent QA (
03_agents/qa/AGENT.md) recopie le même couple « 17 contrôles / 5 dimensions ». - Les DEUX artefacts source sont byte-gatés par
check_artifacts(recomptés du catalogue de contrôles à chaque build) :out/MANIFEST.json(controls_total,dimensions) ETout/audit_report.json(totals.{pass,fail,a_confirmer}). - Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter un contrôle/une dimension au catalogue, ou faire basculer un contrôle en A_CONFIRMER, ferait dériver les deux docs (et la ligne verdict) en silence.
Nouveau bloc dans ci/check_readme_claims.sh : recompute le couple
(contrôles + dimensions) depuis le MANIFEST et la ventilation (pass/fail/à-confirmer)
depuis audit_report.totals — jamais figés en dur ici, zéro duplication du
compteur du générateur (#6) — et exige l'égalité avec les deux docs. La ventilation
est recomputée SÉPARÉMENT car une bascule (pass 13→12, à confirmer 4→5) est aveugle
au seul total de contrôles (reste 17). Cohérence croisée en bonus :
MANIFEST.controls_total == audit_report.totals.controls.
Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| README contrôles 17→18 | ✗ dit 18/5 MAIS artefact 17/5 · exit 1 |
| README verdict 13 PASS→12 / 4→5 à confirmer (bascule aveugle au total) | ✗ dit 12/0/5 MAIS totals 13/0/4 · exit 1 |
| fiche QA dimensions 5→6 | ✗ dit 17/6 MAIS artefact 17/5 · exit 1 |
| claim README verdict supprimé (reformulé) | ✗ ventilation verdict INTROUVABLE · exit 1 |
| restauré | ✓ 17/5 + 13/0/4 == artefacts · exit 0 |
ci/README.md— surface ajoutée à la table récap du pipeline + paragraphe descriptif (classe « audit 5D »).
Suite CI complète re-verte : check_artifacts · check_readme_claims ·
check_docs · check_regression · check_ci_integrity (gates statiques
verrouillés) · guard_constraints · validate_json.
Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo).
Auto-score 4Big : 96/100.
Sprint 8 · buffer · CRM séparation des pouvoirs (2e surface workflow_vente)
- Constat : le README
crm/workflow_venteénumérait À LA MAIN les transitions quatre-yeux (« confirmer réservation, signer contrat, approuver CONFOTUR, annuler/résilier —allow_self_approval = 0»). Le bloc CRM existant ne gatait que le COMPTE états/transitions (9/11) — aveugle à l'IDENTITÉ des transitions àseparation_of_duties. - Gate ajouté (
ci/check_readme_claims.sh) : recompute l'ENSEMBLE{action | allow_self_approval == 0}depuisout/workflow.json(byte-gaté) et exige que la phrase README l'énumère EXACTEMENT (set-diff absent ET en trop ; casse ignorée + suffixe « (perdu) » élidé). Bonus :0 < |quatre-yeux| < |trans|. - 4 morsures vérifiées : déclassement
Signer contrat(self 0→1) capté là où le compte reste 9/11 · promotionDéposer CONFOTUR(self 1→0) captée · échange prosesigner contrat→signer bailcapté · énumération supprimée = INTROUVABLE ; restauré = green ({annuler, approuver confotur, confirmer réservation, résilier, signer contrat}== workflow.json). - État courant : aucune action périmée (anti-invention #6) — le défaut est la
surface ungated.
ci/README.md(table + détail) mis à jour · 7 gates re-verts.