# Activity Log · 2026-07-31 · Claude Code DTP
## Session `20260731_040004` · Sprint 7/8 · Livrable démo : ajout du **prompteur Markdown** (`out/run_sheet.md`) — le support texte lisible manquait au « scénario démo prêt à jouer »
**Tâche** : **Sprint 7 l.68** (« CRM + Faisabilité : Scénarios démo P07 banquier ·
P05 client ready ») × **Sprint 8 buffer**. Après audit : les 7 gates CI étaient
verts, 15 promesses `in_repo`, faisabilité 4 volets complète. Le seul livrable démo
(`demo/scenarios`) ne produisait qu'un **run-sheet JSON machine** — aucun support
**lisible par un présentateur**. Son propre README plafonnait l'auto-score à 96/100
en qualifiant le « prompteur » de **hors périmètre (VPS)**. Or rendre un Markdown
in-repo depuis le JSON est exactement le pattern déjà utilisé par `faisabilite`
(rend des `.md`) — **aucune écriture VPS**. Gap réel, borné, à forte valeur.
### Livraison
- **`scenlib/render.py`** (nouveau) — `render_markdown(run_sheet) -> 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 …/` / `. …/`, `#` 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/