Défaut réel (#6 anti-invention) : 03_agents/erpnext_backend/AGENT.md situait les tests backend par "repo : 560 tests au total" saisi à la main. 560 ne correspond à aucun compte courant (matrice qa/regression = 558/22 ; repo-wide = 584 méthodes test_*) — périmé en silence (534→551→558 ; 560 = ancien 534 + 26 harnais self-exclu INV3). Même classe de vert-trompeur que la fiche QA (21/534), mais ungated. - Vérif ciblée : toutes les autres bornes chiffrées des 13 AGENT.md exactes (crm 81, RBAC 60, e-CF 39, portails 19, chat 31, confotur 44, seo 36). - Fix : le nombre pointe désormais vers l'artefact gaté (regression_run.json). - Gate : ci/check_readme_claims.sh recalcule tests+suites+verdict depuis regression_run et exige l'égalité avec la fiche backend (comme la fiche QA). - Preuve morsure : 560 réinjecté ⇒ gate rouge ; restauré ⇒ vert. - 7 gates verts ; édition doc ⇒ aucune dérive out/ (check_artifacts vert). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
34 KiB
Activity Log · 2026-07-31 · Claude Code DTP
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.