Files
oto-enterprise-os-dtp/05_activity_log/2026-07-31.md
T

91 KiB
Raw Blame History

Activity Log · 2026-07-31 · Claude Code DTP

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.json recompte counts.modules_cites_uniques (= |union des modules_cites des 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.json recompte counts.phases (= len(graph.phase_order)) et counts.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.


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_claims couvrait tests, portails, RBAC, SEO, CRM pipeline et mobile — jamais la structure du DocType CONFOTUR. check_docs ne valide que les liens ; check_artifacts prouve que legal/confotur/out/MANIFEST.json est reproductible byte-for-byte (bloc counts = {fields, data_fields, sections, permissions, roles, depot_events} recomptés de confotur_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_claims couvrait tests, portails, RBAC et SEO — jamais la structure du pipeline CRM. check_docs ne valide que les liens ; check_artifacts prouve que crm/workflow_vente/out/MANIFEST.json est reproductible byte-for-byte (bloc counts = {states, transitions, …} recomptés de workflow_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.json258 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_claims couvrait les comptes tests, les triplets par workspace des portails et les 3 volets RBAC — jamais le domaine SEO. check_docs ne valide que les liens ; check_artifacts prouve que seo/out/MANIFEST.json est reproductible byte-for-byte (bloc counts = {keywords_total, keywords_per_lang{fr,en,es}} recomptés de projets_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 « 5 Workspace natifs (OTO Ventes 4 cartes/11 liens/12 rôles · OTO Construction 4/9/10 · OTO Achat 3/8/5 · OTO Compta 4/11/8 · OTO Direction 4/14/9) » — mêmes triplets recopiés.
  • Pourquoi non gaté : check_readme_claims couvrait les comptes de tests (README racine, fiches QA/Backend, cellules par-suite, prose CRM/RBAC/e-CF) et audit_4big/acceptance — jamais les comptes de structure des portails. check_docs ne valide que les liens ; check_artifacts prouve que frontend/portails/out/MANIFEST.json est reproductible byte-for-byte (tableau workspaces = {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_claims recompute les cellules de table (row_re exige 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 ») : « Verdict PASS17/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) : « PASS ssi 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_claims recomputait 22/22 pour le README d'entrée (racine) mais ne couvrait pas ce README de module. check_docs ne valide que les liens ; l'auditeur s'auto-exclut (SoD) donc son propre out/ ne reflète pas l'édition de son README → aucun signal.

Fix (2 volets · CLAUDE.md #4 — corriger + supprimer la surface + garder)

  1. 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édent 050007/053011).
  2. README.md:68 : 17/1722/22 (compte courant, désormais gaté).
  3. ci/check_readme_claims.sh : nouveau bloc qui RECALCULE verdict + pass/modules/ min_score depuis quality_report.json (totals, déjà chargé dans q/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 job check-regression dé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_claims ne 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.md dériver (session 050007).

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.md en session 050007. 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écrivant check_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 session 043004 (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éel 22 / 551 » au présent — 551 ne correspond plus à rien (→ 558).
  • ci/README.md:121-131 : la section §check_readme_claims affirmait 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 session 043004. Le header du script check_readme_claims.sh (source de vérité · l.23/29/32) liste bien les 3 ; seul ci/README.md a dérivé → doc qui sous-décrit son propre code.
  • Pourquoi non gaté : session 030001 a délibérément laissé ci/README.md hors de check_readme_claims car 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_claims dans 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 : étendre check_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_mvpqa/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) -> str pur 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ésormais out/run_sheet.md en 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) ne json.load que les fichiers .json — un .md dans out/ est ignoré, aucune casse du critère.
  • ci/check_artifacts.sh diffe tout fichier produit par build → il exige désormais run_sheet.md commité + reproductible byte-for-byte (render déterministe ⇒ OK). Preuve de morsure implicite : fichier non suivi ⇒ gate exit 1 (constaté puis corrigé par git 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 + build régénérés (regression_run/plan/MANIFEST.json).
  • demo/scenarios : README 5011→5575 octets + 32→39 test_* → quality_report.json régénéré (evidence DOC+TESTS) · verdict PASS · 22/22 · min 100 inchangé.
  • 03_agents/qa/AGENT.md : verdict agrégé 551→558 (gate check_readme_claims vert).
  • 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 030001 a corrigé le README et claimé avoir corrigé « 3 commentaires CI (21 suites · 534 tests) dans ci.yml et check_regression.sh ». Mais son propre gate ne couvre que README.md. Deux autres docs d'entrée portaient encore le compte périmé — invisibles pour tous les gates (check_docs ne 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 → verdict PASS » en déclarant, ligne suivante, « jamais compté à la main ». C'était pourtant un compte figé à la main qui cite qa/regression/out/regression_run.json comme 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 de check-readme-claims).
  • Preuve au sol : git grep -nE '534|21 suites|6 gates' sur *.md (hors logs) ramenait les 3 lignes ; regression_run.json recalculé = 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.md exemple 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 depuis regression_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 dans 03_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âblage ci.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.md est 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.sh ne 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.json totals dit 22/22 (min_score 100, verdict PASS) ;
    • README : « 21 suites gated » — qa/regression/out/regression_plan.json totals.suites dit 22 (551 tests) ;
    • README : « 15 promesses… 14 in_repo prouvées + 1 out_of_scope » — qa/acceptance/out/acceptance_matrix.json a 15 in_repo (M5 est désormais in_repo avec son hors-périmètre tracé per-promesse ; 0 promesse hors in_repo). Les liens étaient tous verts (check_docs OK) : la dérive était invisible.

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-claims ajouté à ci.yml + inséré dans gate.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.sh lancé par check-readme-claims ∈ gate, et source ci/lib.sh (pas de code mort, échoue en exit 3 hors 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, git absent du PATH, working-dir erroné), git rev-parse n'écrit rien sur stdout → la commande devient cd "", un no-op qui RETOURNE SUCCÈS (le script reste dans le cwd au lieu d'échouer). Les gates poursuivaient sur un git ls-files renvoyant une liste VIDEvalidate_json (« Aucun fichier .json suivi — rien à valider »), guard_constraints et check_docs sortaient 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 .gitvalidate_json exit 0 « rien à valider » ; guard_constraints exit 0 « Contraintes respectées » ; check_docs exit 0. Ce piège avait déjà faussé une vérif « 6 gates verts sur git archive » d'une session antérieure (un git archive | tar -x n'a pas de .git, alors que le runner Gitea a un vrai .git via actions/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) exposant cd_repo_root — se positionne à la racine, ou échoue bruyamment (exit 3, code distinct d'un échec de contrôle 1) avec un message clair si git rev-parse ne renvoie rien. Les 6 gates remplacent leur cd "$(git rev-parse …)" par source "$(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 chaque ci/*.sh soit lancé par un job — ci/lib.sh (jamais dans un run:) aurait été flaggé « gate mort ». INV-B accepte désormais un ci/*.sh câblé de deux façons : lancé par un job du gate, OU sourcé par ≥1 gate script (détecté par grep d'une ligne source …/<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 sur git archive HEAD | tar -x aprè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 non bash ci/*.sh dans l'arbre de travail.
  • Constat : les 4 générateurs RBAC (rbac/fixtures_gen, userperm_gen, roleprofile_gen, apply_plan) .gitignoreaient leur out/ (« re-générable à la demande ») — alors qu'ils sont audités en archétype generator (critère HANDOFF = out/MANIFEST.json + ≥1 artefact JSON). En checkout propre, leur out/ est absent → l'audit 4Big (qa/audit_4big, dont le build relit le out/ de CHAQUE module) les note 80 < 95INV7 ⇒ build refusé. Cascade : check-artifacts rouge (rebuild audit_4big + demo échouent) et check-regression rouge. Ça ne « passait » qu'en local, grâce aux out/ non suivis laissés sur disque par des build manuels — exactement la classe du bug regression_run.json (green-local/red-CI).
  • Preuve au sol : git archive de HEADcheck_artifacts exit 1 (INV7 rbac-* note 80 < 95 · demo/scenarios build a échoué) et check_regression exit 1. Les 15 autres générateurs commettent bien leur out/ → 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 de out/ des 4 .gitignore + git add). Faisabilité prouvée : chaque build est byte-déterministe (2 builds successifs identiques ; tri stable, zéro horodatage). apply_plan lit ses frères en process (imports), pas via leur out/ — committer le out/ 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 laisse check_artifacts les ignorer, moins cohérent · #5.)
  • Durcissement du gate (empêche la récidive) : ci/check_artifacts.sh comparait le build frais au fichier de l'arbre de travail — donc un out/ non suivi (ignoré / jamais git add) le rendait vert en local alors qu'il serait absent en CI. Ajout d'une assertion git ls-files --error-unmatch : tout fichier produit par build DOIT ê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ériver qa/audit_4big/out/quality_report.json (evidence DOC = taille README) — régénéré (PASS · 21/21 · min 100). demo + regression run rejoué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 addexit 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 son needs: 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 un working-directory: sous 05_deliverables_mvp). Les 5 gates statiques (constraints-guard/validate-json/check-docs/ check-artifacts/check-regression → scripts ci/*.sh) n'ont pas de working-directory : rien ne garantissait qu'ils alimentent le gate. Retirer l'un d'eux de needs: (ou une typo dans la liste, ou un ci/*.sh dé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 CI check-ci-integrity, lui-même ajouté à gate.needs. Deux invariants, lus par FAITS dans ci.yml (jamais une liste à la main) :
    • INV-A : gate.needs == { tous les jobs définis } { gate } { jobs manuels if: … workflow_dispatch …, ex. e2e-baseline lé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 dans needs → gate en attente perpétuelle sur push).
    • INV-B : chaque ci/*.sh du dépôt est lancé par exactement un job (run: bash ci/<script>), lui-même dans gate.needs — un gate statique non câblé (script mort) ou décâblé casse le check.
  • Preuve de morsure (5 modes, tous exit 1) :
    • MISSING : check-artifacts retiré de needs✗ ABSENT de gate.needs + INV-B sur check_artifacts.sh ;
    • DANGLING : seo-testsseo-testz dans needs✗ référence 'seo-testz' non définie + ✗ seo-tests ABSENT ;
    • MANUAL-IN-GATE : e2e-baseline ajouté à needs✗ job manuel … gate en attente ;
    • SCRIPT-MORT : ci/check_phantom.sh commité 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]).

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 que qa/regression/out/regression_run.json existe dans le dépôt, puis compare un run frais à 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 Gitea check-regression ne fait que checkout + 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'un regression_run.json non suivi laissé par un run manuel. Le commentaire de ci.yml disait 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 run est byte-déterministe — clés {ran,passed,failures,...} + path relatif, aucun horodatage/hôte/ durée. Deux run successifs byte-identiques (vérifié). Le committer est donc licite et complète l'intention du gate.
  • Fix : run est désormais commité (baseline du gate) →
    • .gitignore : retrait de out/regression_run.json + note expliquant qu'il est commité parce que déterministe ;
    • README.md : section run réécrite (byte-déterministe · commité · baseline check_regression · distinction d'avec check_artifacts qui ne rejoue que build) ;
    • 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_report prouvait le sens CI→disque (tout module gated a un tests/) mais pas l'inverse : un module gagnant un tests/ 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 un tests/ (POSIX, trié, déterministe) ;
    • coverage_report : champ orphan_tests_dirs = disk ci_paths ; ok exige désormais not orphan_tests_dirs ;
    • regression.schema.json : orphan_tests_dirs ajouté (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 + buildbuild 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 : 560 ne 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 = ancien 534 (matrice d'alors) + 26 (harnais qa/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.
  • Vérif ciblée (#1 analyser) : recomputé toutes les autres bornes chiffrées des 13 AGENT.mdcrm 81=(25+31+25), RBAC 60=(10+16+11+11+12), e-CF 39, portails 19, chat 31, confotur 44, seo 36 : toutes exactes. Le 560 était le seul nombre dérivé.

Fix (2 volets · corriger + garder)

  1. 03_agents/erpnext_backend/AGENT.md : 560« matrice de régression du repo : 558 tests · 22 suites · verdict PASS, source qa/regression/out/regression_run.json — jamais compté à la main · #6 » (le nombre pointe désormais vers l'artefact gaté, pas une saisie).
  2. ci/check_readme_claims.sh : nouveau bloc qui recalcule tests+suites+ verdict depuis regression_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 : 560 ré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'artefact out/ (check_artifacts vert : quality_report.json inchangé).

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 un roadmap_line (33, 39, … 87) censé pointer son bullet dans 04_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_line est un entier positif ».
  • Contradiction : si la roadmap est éditée (une ligne insérée/retirée, un sprint ré-ordonné), tous les roadmap_line se 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). Les roadmap_line du spec sont exacts aujourd'hui — la faille est l'absence de garde, pas une valeur fausse.

Fix (2 volets · vérifier + garder)

  1. acclib/deps.py — nouveau parse_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. OSError honnête si la roadmap est absente (référence fantôme = red).
  2. 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é »).
  3. 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_anchors réel = {S1:33 … S8:76} + métriques [81..87] ; les 15 roadmap_line du spec matchent.
  • Morsure (spec RÉEL) : S1.roadmap_line=999roadmap: 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_4big 35 (réel 34), regression 25 (réel 26), acceptance 31 (réel 37 — la session précédente avait fait 31→37 sans propager à cette table).
  • Contradiction : le gate amont check_readme_claims ne 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)

  1. 03_agents/qa/AGENT.md — 3 nombres corrigés vers le réel : 35→34, 25→26, 31→37.
  2. ci/check_readme_claims.sh — nouveau bloc générique : scanne toutes les fiches, extrait chaque ligne de table pointant 05_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, et reglib.discovery.count_tests (même fonction que le plan · zéro duplication · #5) pour le self_module qa/regression exclu de la matrice par SoD. Table « Tests » absente ⇒ red (disparition = régression de traçabilité).

Preuves

  • Passe : gate vert, 10 lignes par-suite vérifiées (crm×3 · faisabilite×2 · publiciste×1 · qa×4), toutes == source.
  • Morsure : acceptance reforcé à 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.json recalculé du contrat rbac_50_roles.json à chaque build (byte-gaté par check_artifacts ⇒ autorité). Trois READMEs de module recopiaient à la main des comptes qui en dérivent, sans AUCUN gate :
    1. 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.
    2. 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.
    3. 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 le out/MANIFEST.json du volet (zéro duplication des compteurs des générateurs · #6) et exigeant l'égalité avec ce que la doc ÉCRIT :
    • roleprofile : chaque nb_roles par profil (ancré sur nom+portail) + total counts.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.

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 émet out/MANIFEST.json dont counts porte 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 liste a_confirmer (credentials EAS/App Store #32/Play Store jamais fabriqués · #6/#8 → identifiants_a_confirmer). Le out/ est byte-gaté par check_artifacts (build frais == commité) ⇒ artefact faisant autorité.
  • Contradiction latente : la ligne récap du README du module (05_deliverables_mvp/mobile/app_config/README.md L66-67 — « 5 onglets · 44 rôles couverts · 3 langues · 13 identifiants a_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) depuis mobile/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) ET out/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.totalsjamais 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.