# 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/