Files
oto-enterprise-os-dtp/05_activity_log/2026-07-31.md
T
Claude Code DTP Worker 6a40bfccce [DTP-Worker] Sprint 8 · buffer L75 · Domaine QA/Audit 5D (2e surface) : la TABLE « Verdict courant » de l'audit de conformité énumérait À LA MAIN ses 4 open items (control → dimension → propriétaire) sans AUCUN gate d'IDENTITÉ. Le bloc audit_5d existant gate « 17 contrôles / 5 dimensions » (×2 docs) ET la ventilation du verdict « 13 PASS · 0 FAIL · 4 à confirmer » (recomputée d'audit_report.totals) mais PAS l'identité des 4 contrôles ouverts ni leur (dimension, propriétaire) — surface data-derived distincte du MÊME README. Cette table dérive d'audit_report.json.open_items[] (byte-gaté par check_artifacts : chaque item = {control, dimension, owner, detail} recalculé en rejouant les contrôles sur les hand-off amont). Le bloc de VENTILATION ne recompute que le COMPTE (« 4 à confirmer ») : un ÉCHANGE d'open item (ex. D2.3 → D3.1) laisse le compte à 4 — le compteur reste AVEUGLE — pendant que l'artefact dit autre chose ; idem une dimension mal étiquetée (D1.3 rangé sous D2) ou un propriétaire réattribué — « vert trompeur » qu'aucune suite tests/ (qui teste des FONCTIONS, pas la prose) n'attrape → nouveau bloc dans check_readme_claims recomputant l'ensemble {control:(dimension, propriétaire)} depuis audit_report.open_items (zéro duplication du modèle de l'auditeur #6) et exigeant que la table README l'énumère EXACTEMENT. Contrôle par ENSEMBLE (absent ET en trop mordus, pas seulement présence), puis (dimension, propriétaire) PAR LIGNE. Même patron que l'énumération des confirmations DevOps ou la carte de renormalisation par archétype 4Big. Cohérence croisée en bonus : l'ensemble des control == MANIFEST.open_items (le manifeste qui résume le rapport) — mord un manifeste désynchronisé de son rapport. Un claim absent échoue AUSSI (4 morsures vérifiées : échange D2.3→D3.1 capté là où le compte reste 4 · dimension D1.3→D2 captée · propriétaire D1.1 Direction→Compta capté · ligne D1.2 supprimée = sous-ensemble capté ; restauré = green : [D1.1,D1.2,D1.3,D2.3] control→dimension→propriétaire == audit_report.open_items). État courant : aucun open item périmé (anti-invention #6, rien à réécrire) — le défaut est la surface ungated. ci/README.md (table + détail) mis à jour · 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 14:39:58 +00:00

1755 lines
110 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Activity Log · 2026-07-31 · Claude Code DTP
## Session `20260731_143104` · Buffer S8 · Domaine QA/Audit 5D (2e surface) : la **table « Verdict courant »** de l'audit de conformité énumérait À LA MAIN ses **4 open items** (`control → dimension → propriétaire`) sans gate d'IDENTITÉ — le bloc existant ne recompute que le COMPTE (« 4 à confirmer »), aveugle à un **échange** d'open item.
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Le bloc `audit_5d` gaté
précédemment couvre « 17 contrôles / 5 dimensions » (×2 docs) **et** la ventilation
du verdict « **13 PASS · 0 FAIL · 4 à confirmer** » (recomputée d'`audit_report.totals`).
Restait, dans le **même README**, une surface data-derived distincte : la **table
« Verdict courant »** qui énumère les 4 contrôles ouverts avec leur dimension et leur
propriétaire.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/qa/audit_5d/README.md:51-56` énumère 4 lignes
`| D1.1 · … | D1 | Direction |` … — chacune porte trois faits data-derived :
l'`id` du contrôle, sa **dimension**, son **propriétaire**.
- Source faisant autorité : `qa/audit_5d/out/audit_report.json.open_items[]`
(byte-gaté par `check_artifacts`, chaque item = `{control, dimension, owner, detail}`
recalculé en rejouant les contrôles sur les hand-off amont).
- Le bloc de **ventilation** ne gate QUE le **compte** (« 4 à confirmer »). Un
**échange** d'open item (ex. `D2.3 → D3.1`) laisse le compte à 4 — le compteur
reste AVEUGLE — pendant que l'artefact dit autre chose ; idem une **dimension**
mal étiquetée (`D1.3` rangé sous `D2`) ou un **propriétaire** réattribué. « Vert
trompeur » qu'aucune suite `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape.
- **État courant** : **aucun open item périmé** — les 4 lignes recoupent l'artefact
exactement (anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute l'ensemble
`{control:(dimension, propriétaire)}` depuis `audit_report.open_items` (zéro
duplication du modèle de l'auditeur · #6) et exige que la table README l'énumère
**exactement** : contrôle par **ENSEMBLE** (absent ET en trop mordus, pas seulement
présence), puis `(dimension, propriétaire)` **par LIGNE**. Même patron que
l'énumération des confirmations DevOps ou la carte de renormalisation par archétype
4Big. Cohérence croisée en bonus : l'ensemble des `control` == `MANIFEST.open_items`
(le manifeste qui résume le rapport) — mord un manifeste désynchronisé de son rapport.
### Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| échange `D2.3 → D3.1` (compte reste 4 · gate-compte aveugle) | ✗ énumère `[…,D3.1]` MAIS référentiel `[…,D2.3]` · exit 1 |
| dimension `D1.3` étiquetée `D2` (compte + ids intacts) | ✗ open item D1.3 dit dimension=D2 MAIS artefact D1 · exit 1 |
| propriétaire `D1.1` `Direction → Compta` (compte + ids + dims intacts) | ✗ D1.1 dit propriétaire=Compta MAIS artefact Direction · exit 1 |
| ligne `D1.2` supprimée (sous-ensemble) | ✗ énumère `[D1.1,D1.3,D2.3]` MAIS référentiel `[D1.1,D1.2,D1.3,D2.3]` · exit 1 |
| restauré | ✓ `[D1.1,D1.2,D1.3,D2.3]` (control→dimension→propriétaire) == `audit_report.open_items` · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (2e surface du même README audit 5D).
Suite CI complète re-verte (7 gates) : `guard_constraints` · `validate_json` ·
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
`check_readme_claims`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp` ⇒ 0 dérive d'artefact).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_140101` · Buffer S8 · Domaine QA/Audit 4Big (2e surface) : la **carte de renormalisation PAR ARCHÉTYPE** de l'auditeur (« `data_room` (sans HANDOFF) · `parser` (sans CONTRAT … ni HANDOFF) · `contract` (sans CLI ni HANDOFF) ») était transcrite À LA MAIN dans le README du module — la session précédente a gaté le barème (critères + poids + `generator (N critères)`) mais PAS les exclusions des trois autres archétypes.
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). La session `133057` a gaté le
**barème** de l'auditeur (nombre de critères + poids par critère + `generator (5
critères)`). Restait, dans le **même README**, une surface data-derived distincte : la
**carte de renormalisation par archétype**, qui dit quels critères CHAQUE archétype
**exclut** du dénominateur.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/qa/audit_4big/README.md:32-34` énumère
« `generator` (5 critères) · `data_room` (**sans HANDOFF**) · `parser` (**sans
CONTRAT** local ni **HANDOFF**) · `contract` (**sans CLI** ni **HANDOFF**) ».
- Cette carte est **DATA-DERIVED** : pour un archétype, l'ensemble des critères
**applicables** = `{check.criterion}` de ses modules dans
`out/quality_report.json` (byte-gaté par `check_artifacts`) — les critères
non-applicables sont **absents** des `checks`. Donc **exclus** = ordre du barème
applicables.
- Le bloc barème de la session précédente ne recompute QUE `generator` (« 5 critères »,
zéro exclusion). Les **exclusions** de `data_room`/`parser`/`contract` n'étaient
recomputées par AUCUN gate : changer la règle de renormalisation d'un archétype
(ex. `data_room` exclut aussi `CLI`) ferait dériver la prose en silence pendant que
`quality_report` dit autre chose — « vert trompeur » qu'aucune suite `tests/` (qui
teste des FONCTIONS, pas la prose) n'attrape.
- **État courant** : **aucune exclusion périmée** — les 3 cartes recoupent l'artefact
exactement (anti-invention #6 : rien à réécrire). Le défaut est la **surface ungated**.
**Nouveau bloc (6) dans `ci/check_readme_claims.sh`** (section barème `qa/audit_4big`) :
recompute **par archétype** l'ensemble des critères exclus depuis les `checks` de
`quality_report.modules` (zéro duplication du modèle de renormalisation du générateur ·
#6) et exige que la parenthèse du README l'énumère **exactement** (mauvais critère,
sous-ensemble, sur-ensemble → tous mordus). Bonus d'uniformité : tous les modules d'un
même archétype portent le **même** jeu applicable (« 1 archétype → 1 jeu de critères »,
base même de la carte — mord un référentiel où un module d'un archétype divergerait des
autres). Un claim (énumération) absent échoue AUSSI (traçabilité).
### Preuve (3 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| `data_room` « sans HANDOFF » → « sans CLI » (mauvais critère exclu) | ✗ exclut `[CLI]` MAIS référentiel `[HANDOFF]` · exit 1 |
| `parser` exclusion `HANDOFF` retirée (sous-ensemble) | ✗ exclut `[CONTRAT]` MAIS référentiel `[CONTRAT, HANDOFF]` · exit 1 |
| énumération `contract` supprimée (claim disparu) | ✗ énumération `contract` INTROUVABLE · exit 1 |
| restauré | ✓ 4 archétypes uniformes · `data_room`/`parser`/`contract` == référentiel · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (classe « carte de renormalisation par archétype 4Big »).
Suite CI complète re-verte (7 gates) : `guard_constraints` · `validate_json` ·
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
`check_readme_claims`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_133057` · Buffer S8 · Domaine QA/Audit 4Big : le **barème de notation** de l'auditeur (« **5 critères 4Big déterministes** » + la table `Critère | Poids` + l'archétype « `generator` (5 critères) ») était transcrit À LA MAIN dans le README du module sans AUCUN gate — le bloc racine ne gatait que sa ligne « Résultat courant » (verdict/modules), jamais le référentiel de scoring lui-même.
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Restait une surface de « vert
trompeur » non gatée dans le module qui NOTE les autres : son propre barème.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/qa/audit_4big/README.md` retranscrit trois surfaces
data-derived du référentiel de scoring : la prose « Note chaque module sur **5
critères 4Big déterministes** », la table « Critère | Poids | Fait vérifié » (une
ligne `id`→poids par critère : DOC 20 · CONTRAT 20 · TESTS 25 · CLI 15 · HANDOFF 20)
et l'archétype « `generator` (5 critères) ».
- La source faisant autorité est `out/quality_report.json.criteria` (byte-gaté par
`check_artifacts`, recomputé du référentiel à chaque build).
- Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter/retirer un critère
ou changer un poids ferait dériver le README en silence — dérive qu'aucune suite
`tests/` (qui teste des FONCTIONS, pas la prose) n'attrape.
**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute le **nombre de critères**
(prose + « generator »), et **chaque poids** depuis `criteria[].weight` (zéro
duplication du barème du générateur · #6), exige l'égalité avec le README. Cohérences
croisées : `Σ poids == 100` (base de renormalisation documentée) ET tout module
d'archétype `generator` porte EXACTEMENT `len(criteria)` `checks` (« generator = tous
les critères » — 18 modules generator × 5 checks vérifiés). Une **compensation** de
poids (+5 / 5) laisserait `Σ == 100` juste et deux lignes fausses → d'où le contrôle
**par critère**, pas seulement de la somme.
### Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| README prose 5 critères → 6 (poids table intacts) | ✗ dit 6 MAIS criteria en compte 5 · exit 1 |
| README poids `TESTS` 25 → 30 (compte intact) | ✗ dit poids 30 MAIS criteria dit 25 · exit 1 |
| README archétype `generator` (5→4 critères) | ✗ dit 4 MAIS generator applique les 5 · exit 1 |
| README claim prose reformulé (chiffre disparu) | ✗ « N critères 4Big » INTROUVABLE · exit 1 |
| restauré | ✓ 5 critères · poids 20/20/25/15/20 · Σ=100 · 18 gen×5 == artefact · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (classe « barème auditeur 4Big »).
Suite CI complète re-verte (7 gates) : `guard_constraints` · `validate_json` ·
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
`check_readme_claims`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_130055` · Buffer S8 · Domaine QA/Recette : le couple « **8 livrables de sprint · 7 métriques succès MVP** » du module d'acceptation (`qa/acceptance`) était transcrit À LA MAIN dans DEUX docs de module (README du module + fiche agent QA) sans AUCUN gate — le bloc racine ne gate QUE le README d'entrée
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les
sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM
(pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D + audit 4big), DevOps
(run-book), Démo (run-sheet) et Frontend/Chat OTOIA. Cette session cible le **module de
recette** (`qa/acceptance`) — son couple data-derived « livrables de sprint /
métriques succès MVP », cité dans deux docs de module que le bloc racine ne couvrait pas.
### Défaut trouvé — couple ungated, transcrit dans DEUX docs de module
- **`05_deliverables_mvp/qa/acceptance/README.md:8-9`** : puces « **8 livrables de
sprint** (L33..L76) » et « **7 métriques succès MVP** (L81..L87) ».
- **`03_agents/qa/AGENT.md:27`** : cellule « mappe chaque promesse roadmap (**8
livrables + 7 métriques**) vers sa preuve » — **même** couple dérivé de la **même**
matrice (source unique · #5).
- L'artefact source est byte-gaté par `check_artifacts` :
`qa/acceptance/out/acceptance_matrix.json` porte une ligne par promesse roadmap de
`kind` `sprint_deliverable` OU `mvp_metric`. Le bloc racine de `check_readme_claims`
recompute déjà ce couple pour le **SEUL README d'entrée** (« N promesses (X sprint +
Y métriques) ») — mais **jamais** ces deux docs de **module**.
- Le **roadmap-anchor** (INV11) ancre les `roadmap_line` de l'**ARTEFACT** au fichier
roadmap ; il ne compare **pas** la prose des docs. Ajouter une promesse roadmap →
l'artefact rebâtit 9/7 pendant que les deux docs restent à 8/7 en silence — « vert
trompeur » qu'aucune suite `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape.
- **État courant** : **aucun nombre périmé** — 8/7 recoupe la matrice exactement
(anti-invention #6 : rien à réécrire). Le défaut est la **surface ungated**.
**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute **chaque** nombre depuis
**son propre** genre de la matrice (`kind` `sprint_deliverable` / `mvp_metric`,
`n_sprint`/`n_metric` déjà dérivés plus haut · zéro duplication #5) et exige l'égalité
avec les DEUX docs. Recomputer les DEUX (sprint ET métriques) mord une dérive de l'un
là où l'autre resterait aveugle. Cohérence croisée en bonus :
`MANIFEST.counts.{sprint_deliverables,mvp_metrics}` == le compte de la matrice qu'il
résume — mord un compteur du manifeste désynchronisé de sa liste. Un claim absent
échoue AUSSI (traçabilité).
### Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| README sprint `8→9` (métriques reste 7) | ✗ dit 9/7 MAIS matrice 8/7 · exit 1 |
| README métriques `7→6` (sprint reste 8) | ✗ dit 8/6 MAIS matrice 8/7 · exit 1 |
| fiche QA parenthèse `8→10` (README intact) | ✗ dit 10/7 MAIS matrice 8/7 · exit 1 |
| fiche QA claim supprimé (reformulé) | ✗ claim INTROUVABLE · exit 1 |
| restauré | ✓ 8/7 == matrice (2 docs) · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (classe « module de recette `qa/acceptance` »).
Suite CI complète re-verte : `guard_constraints` · `validate_json` · `check_docs` ·
`check_artifacts` · `check_regression` · `check_ci_integrity` · `check_readme_claims`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_123055` · Buffer S8 · Domaine Frontend/Chat OTOIA : le montage de l'assistant conversationnel par portail (`frontend/chat_otoia`) affichait « **5 `Custom Block` + 5 configs runtime** » (fiche agent frontend_console) et « **5 portails · 5 fixtures · 5 configs runtime** » (README du module) — trois nombres data-derived, TOUS = 5 (piège aveugle), transcrits À LA MAIN dans DEUX docs sans AUCUN gate
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les
sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM
(pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D), DevOps (run-book) et Démo
(run-sheet). Cette session cible le **domaine Frontend/Chat OTOIA** — le **montage de
l'assistant conversationnel par portail** (livrable Sprint 6 « Chat OTOIA embedded »),
seul grand livrable dont les comptes data-derived cités dans deux docs pointaient
l'artefact généré sans être recomputés par aucun gate.
### Défaut trouvé — triplet ungated, TOUS = 5 (piège aveugle), transcrit dans DEUX docs
- **`03_agents/frontend_console/AGENT.md:29`** : cellule « **5 `Custom Block` + 5
configs runtime** » — deux nombres (custom_blocks + mounts).
- **`05_deliverables_mvp/frontend/chat_otoia/README.md`** : « Portails (**5**,
`plateforme` exclu) » (l.24) · « **5** fixtures `Custom Block` » (l.48) · « **5**
configs runtime » (l.49) — trois transcriptions (portails + custom_blocks + mounts).
- L'artefact source est byte-gaté par `check_artifacts` :
`frontend/chat_otoia/out/MANIFEST.json` recompte `counts.custom_blocks`,
`counts.mounts` et `counts.portails` du contrat RBAC à chaque build (bijection
1 block ⇔ 1 mount ⇔ 1 portail métier, `plateforme` exclu — invariant 2 du
générateur).
- Aucun gate ne comparait la VALEUR écrite à l'artefact. **Piège aveugle**
caractéristique (cf. DevOps phases confirmations) : custom_blocks, mounts ET
portails valent **tous trois 5 aujourd'hui** — un gate qui n'en recompterait qu'un
serait aveugle à la dérive des deux autres (ajouter un portail au contrat RBAC →
chat_otoia rebâtit 6 6 6 pendant que les deux docs restent à 5). « Vert
trompeur » qu'aucune suite `tests/` (qui teste des FONCTIONS, pas la prose des docs)
n'attrape.
- **État courant** : **aucun nombre périmé** — 5/5/5 recoupe MANIFEST exactement
(anti-invention #6 : rien à réécrire). Le défaut est la **surface ungated**.
**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute **chaque** nombre depuis
**son propre** champ du MANIFEST (`custom_blocks` · `mounts` · `portails`, jamais figé
ici · zéro duplication du compteur du générateur #5) et exige l'égalité avec les deux
docs. Cohérence croisée : `custom_blocks == mounts == portails == |MANIFEST.portails|`
(la bijection du générateur) — mord un compteur désynchronisé de sa propre liste. Un
claim absent échoue AUSSI (traçabilité).
### Preuve (5 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| fiche custom_block `5→6` (mounts reste 5, aveugle si 1 seul recompté) | ✗ dit 6/5 MAIS artefact 5/5 · exit 1 |
| fiche configs runtime `5→4` (blocks reste 5) | ✗ dit 5/4 MAIS artefact 5/5 · exit 1 |
| README portails `5→6` | ✗ dit 6 portails MAIS artefact 5 · exit 1 |
| README fixtures `5→7` | ✗ dit 7 fixtures MAIS artefact 5 · exit 1 |
| README claim « configs runtime » supprimé (reformulé) | ✗ claim INTROUVABLE · exit 1 |
| restauré | ✓ 5/5/5 == artefact (2 docs) · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (classe « montage Chat OTOIA par portail »).
Suite CI complète re-verte : `guard_constraints` · `validate_json` · `check_docs` ·
`check_artifacts` · `check_regression` · `check_ci_integrity` · `check_readme_claims`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_120054` · Buffer S8 · Domaine Démo : le run-sheet de pitch (`demo/scenarios`) affichait « **10 modules cités** » dans le diagramme d'architecture de son README — un compte data-derived transcrit À LA MAIN sans AUCUN gate
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les
sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM
(pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D) et DevOps (run-book). Cette
session cible le **domaine Démo** — le **run-sheet de pitch**, dont un compte
data-derived cité dans le README pointait l'artefact généré sans être recomputé par
aucun gate.
### Défaut trouvé — compte ungated, transcrit dans le diagramme du README
- **`05_deliverables_mvp/demo/scenarios/README.md:23`** : diagramme d'architecture →
« `scenario_spec.json … out/ des modules livrés (`**`10 modules cités`**`)` ».
- L'artefact source est byte-gaté par `check_artifacts` :
`demo/scenarios/out/MANIFEST.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.
---
## Session `20260731_103044` · Buffer S8 · Domaine Legal : la synthèse du DocType « CONFOTUR Application » (« 18 champs · 14 de donnée · 4 sections · 3 rôles · 2 évènements de dépôt ») était transcrite À LA MAIN dans DEUX docs (README du module legal/confotur + fiche agent onapi_legal) sans AUCUN gate — ajouter un champ/section/rôle au spec ferait dériver les deux docs en silence
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap
fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage).
Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté
les comptes data-derived des domaines tests, portails, RBAC (3 volets), SEO, CRM
(pipeline vente) et mobile. Cette session cible le **domaine Legal** — la **structure
du DocType CONFOTUR**, dont un quintuplet data-derived cité dans deux docs pointait
l'artefact généré sans être recomputé par aucun gate.
### Défaut trouvé — comptes du DocType ungated, transcrits dans DEUX docs
- **`05_deliverables_mvp/legal/confotur/README.md:17`** : ligne de synthèse
« **18 champs (14 de donnée) · 4 sections · 3 rôles · soumissible · 2 évènements
de dépôt** ».
- **`03_agents/onapi_legal/AGENT.md:28`** : cellule de table reprenant les **mêmes**
5 nombres (chacun en gras), **dérivés du même spec** (source unique · #5).
- **Pourquoi non gaté** : `check_readme_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.json`**258** mots-clés (fr=87 · en=87 · es=84) ».
- **`03_agents/seo/AGENT.md:24`** : **même** ligne, mêmes 4 nombres recopiés.
- **Pourquoi non gaté** : `check_readme_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> | \`<ws>\` | C | L | R |`) **et** la cellule
de prose de la fiche (`\`<ws>\` C cartes/L liens/R rôles`, forme abrégée `C/L/R`
tolérée). + contrôle du **total** « N `Workspace` natifs » == 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 `PASS`** — **17/17** modules à **100/100** » — compte **vivant saisi à la
main**, périmé (17 → 22 au fil des ajouts de modules). **Ironie complète** : c'est le
README du module qui **COMPTE les modules** ; son propre artefact faisant autorité
`qa/audit_4big/out/quality_report.json` (`totals.modules`) dit **22** depuis longtemps.
- **`…/README.md:38`** (règle du verdict global) : « `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/17` → **22/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_mvp`**`qa/audit_4big`
ne la lit pas comme evidence ⇒ `quality_report.json` **inchangé** ; aucune régénération
de consommateur nécessaire.
**Hors périmètre worker (VPS · #8)** : néant (doc en-repo ; runner Gitea = DevOps).
**Auto-score 4Big** : 96/100.
## Session `20260731_040004` · Sprint 7/8 · Livrable démo : ajout du **prompteur Markdown** (`out/run_sheet.md`) — le support texte lisible manquait au « scénario démo prêt à jouer »
**Tâche** : **Sprint 7 l.68** (« CRM + Faisabilité : Scénarios démo P07 banquier ·
P05 client ready ») × **Sprint 8 buffer**. Après audit : les 7 gates CI étaient
verts, 15 promesses `in_repo`, faisabilité 4 volets complète. Le seul livrable démo
(`demo/scenarios`) ne produisait qu'un **run-sheet JSON machine** — aucun support
**lisible par un présentateur**. Son propre README plafonnait l'auto-score à 96/100
en qualifiant le « prompteur » de **hors périmètre (VPS)**. Or rendre un Markdown
in-repo depuis le JSON est exactement le pattern déjà utilisé par `faisabilite`
(rend des `.md`) — **aucune écriture VPS**. Gap réel, borné, à forte valeur.
### Livraison
- **`scenlib/render.py`** (nouveau) — `render_markdown(run_sheet) -> 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 **VIDE**
`validate_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 `.git`**
`validate_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`) **`.gitignore`aient 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 < 95** ⇒ **INV7** ⇒ 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 `HEAD` → `check_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 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 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-tests`→`seo-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` + `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** : `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.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**. 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=999` ⇒ `roadmap: S1: … ne pointe pas le
DELIVERABLE de S1 (ligne 33)` ; `M3=200` ⇒ `… ≠ 3e bullet métrique (ligne 83)`.
**Régénération des consommateurs (mémoire projet)** : +6 méthodes test dans
`qa/acceptance` → matrice **558→564** (22 suites inchangées). Régénérés :
`qa/regression/out/{regression_run,regression_plan,MANIFEST}.json` (564/564 · PASS),
`qa/audit_4big/out/quality_report.json` (evidence acceptance « 31→37 méthodes », score
**100/100 inchangé**), fiches **QA** et **Backend** (`558→564`, gate `check_readme_claims`
vert), README acceptance (« 10→11 invariants »). L'artefact `acceptance_matrix.json` est
**byte-identique** (INV11 ne change que la validation, pas la sortie).
**Vérifs** : **7 gates** locaux verts (`guard_constraints` · `validate_json` ·
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
`check_readme_claims`) ; matrice **564/22 · PASS** ; 22/22 modules **100/100**.
**Anti-invention (#6)** : rien inventé — INV11 **dérive** la vérité de la roadmap et
compare ; le « 8 + 7 » cesse d'être un compte à la main.
**Hors périmètre worker (VPS · #8)** : néant (stdlib pur en-repo).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_063014` · Buffer S8 · Comptes de tests PAR SUITE périmés dans la fiche QA — dérive silencieuse (aucun gate ne couvrait le grain fin)
**Tâche** : **Sprint 8 · buffer L75 (« Regression tests exhaustifs »)** — roadmap
fonctionnellement close (22/22 modules gated · 564 tests · 7 gates verts au démarrage).
Chasse d'un **défaut réel** de la classe « vert trompeur » (#6) plutôt qu'un cosmétique.
### Défaut trouvé — fiche QA : colonne « Tests » par suite figée, DÉJÀ fausse
- **Symptôme** : le tableau des livrables de `03_agents/qa/AGENT.md` (col « Tests »)
affichait un compte par suite saisi à la main. Trois lignes étaient **déjà
périmées** au démarrage : `audit_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
6) + 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.totals` — **jamais** figés en dur ici, zéro duplication du
compteur du générateur (#6) — et exige l'égalité avec les deux docs. La ventilation
est recomputée SÉPARÉMENT car une bascule (pass 13→12, à confirmer 4→5) est **aveugle**
au seul total de contrôles (reste 17). Cohérence croisée en bonus :
`MANIFEST.controls_total == audit_report.totals.controls`.
### Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| README contrôles 17→18 | ✗ dit 18/5 MAIS artefact 17/5 · exit 1 |
| README verdict 13 PASS→12 / 4→5 à confirmer (bascule aveugle au total) | ✗ dit 12/0/5 MAIS totals 13/0/4 · exit 1 |
| fiche QA dimensions 5→6 | ✗ dit 17/6 MAIS artefact 17/5 · exit 1 |
| claim README verdict supprimé (reformulé) | ✗ ventilation verdict INTROUVABLE · exit 1 |
| restauré | ✓ 17/5 + 13/0/4 == artefacts · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (classe « audit 5D »).
Suite CI complète re-verte : `check_artifacts` · `check_readme_claims` ·
`check_docs` · `check_regression` · `check_ci_integrity` (gates statiques
verrouillés) · `guard_constraints` · `validate_json`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
**Auto-score 4Big** : 96/100.