Files
oto-enterprise-os-dtp/05_activity_log/2026-07-31.md
T
Claude Code DTP Worker 504bfb0f10 [DTP-Worker] Sprint 8 · buffer L75 · Chiffre périmé fiche agent : "repo 560 tests" (vert-trompeur ungated) → 558/22 sourcé matrice + gate étendu
Défaut réel (#6 anti-invention) : 03_agents/erpnext_backend/AGENT.md situait les
tests backend par "repo : 560 tests au total" saisi à la main. 560 ne correspond à
aucun compte courant (matrice qa/regression = 558/22 ; repo-wide = 584 méthodes
test_*) — périmé en silence (534→551→558 ; 560 = ancien 534 + 26 harnais self-exclu
INV3). Même classe de vert-trompeur que la fiche QA (21/534), mais ungated.

- Vérif ciblée : toutes les autres bornes chiffrées des 13 AGENT.md exactes
  (crm 81, RBAC 60, e-CF 39, portails 19, chat 31, confotur 44, seo 36).
- Fix : le nombre pointe désormais vers l'artefact gaté (regression_run.json).
- Gate : ci/check_readme_claims.sh recalcule tests+suites+verdict depuis
  regression_run et exige l'égalité avec la fiche backend (comme la fiche QA).
- Preuve morsure : 560 réinjecté ⇒ gate rouge ; restauré ⇒ vert.
- 7 gates verts ; édition doc ⇒ aucune dérive out/ (check_artifacts vert).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 04:36:20 +00:00

541 lines
34 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_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.