a30ff937f0
Défaut réel (même classe que le bug regression_run.json corrigé plus tôt) : les 4 générateurs RBAC (fixtures_gen/userperm_gen/roleprofile_gen/apply_plan) .gitignore-aient leur out/, alors qu'ils sont audités en archétype `generator` (critère HANDOFF). En checkout PROPRE (git archive HEAD = ce que voit le runner Gitea) leur out/ est absent → qa/audit_4big (dont le build relit le out/ de CHAQUE module) les note 80<95 ⇒ INV7 ⇒ build refusé ⇒ check-artifacts ET check-regression ROUGES. Ça ne passait qu'en local via les out/ non suivis laissés par des build manuels. Fix : committer le out/ des 4 générateurs (build byte-déterministe prouvé ; apply_plan lit ses frères en process, pas via out/) → alignement sur les 15 autres générateurs ; audit 100/100 en checkout propre. Durcissement : check_artifacts.sh exige désormais `git ls-files --error-unmatch` sur chaque fichier produit → un out/ ignoré/non commité devient une erreur LOCALE honnête au lieu d'une surprise en CI. Bite-proof : git rm --cached d'un artefact (laissé sur disque) ⇒ exit 1 ; re-add ⇒ exit 0. Régénéré : quality_report.json (dérive DOC = taille des 4 README édités). Vérifs : 6 gates verts sur git archive propre ; 534/21 inchangé ; suites OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
211 lines
13 KiB
Markdown
211 lines
13 KiB
Markdown
# Activity Log · 2026-07-31 · Claude Code DTP
|
||
|
||
## 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.
|