[DTP-Worker] Sprint 8 · buffer L75 · Intégrité matrice régression : fix gate check_regression cassé en CI (run baseline commité) + invariant disque→CI (INV4 orphan_tests_dirs)
Défaut #1 (bug CI réel) : ci/check_regression.sh exigeait regression_run.json commité comme baseline, mais le fichier était .gitignore → absent en checkout propre → le job Gitea check-regression échouait sur CHAQUE run CI (ne passait qu'en local via un fichier non suivi). L'artefact run est byte-déterministe (aucun horodatage/hôte/durée · path relatif) → committer est licite. Fix : run désormais commité (baseline), .gitignore + README réécrits. Défaut #2 (invariant manquant) : coverage_report prouvait CI→disque mais pas l'inverse. Nouveau disk_test_modules() + orphan_tests_dirs dans INV4 : un module gagnant un tests/ sans job CI fait chuter la couverture (fini la sous-comptée silencieuse de la matrice). Schéma + 2 tests de morsure. Consommateur régénéré : qa/audit_4big/quality_report.json (README 3710→4236 o, 24→26 méthodes). Matrice 534/21/PASS inchangée (harnais self-exclu, INV3). 5 gates verts · 26+34 tests OK · YAML valide.
This commit is contained in:
@@ -1,60 +1,76 @@
|
||||
# Activity Log · 2026-07-31 · Claude Code DTP
|
||||
|
||||
## Session `20260730_235919` · Buffer S8 · Gate fraîcheur matrice de régression (5ᵉ gate CI)
|
||||
## 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, chantier cross-linking clos). Recherche d'une **incohérence de
|
||||
reproductibilité réelle** plutôt qu'un correctif doc cosmétique.
|
||||
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`.
|
||||
|
||||
**Gap comblé (réel, sourcé)** : l'artefact le plus **cité** de tout le dépôt —
|
||||
`qa/regression/out/regression_run.json` (« **21 suites · 534 tests · verdict
|
||||
PASS** », référencé dans des dizaines d'entrées de log, README d'agents et fiches)
|
||||
— n'avait **aucun garde-fou CI** prouvant qu'il reflète le disque :
|
||||
- `check_artifacts.sh` ne rejoue **que** la sous-commande déterministe `build`
|
||||
(→ `regression_plan.json`) et **exclut explicitement** (l.20-22) les artefacts
|
||||
d'**exécution** issus de `run` ;
|
||||
- le job `qa-regression-tests` ne lance que les **25 tests unitaires du harnais**
|
||||
(`unittest discover -s tests`), jamais `regression_gen.py run` complet ;
|
||||
- résultat : la matrice agrégée pouvait se **périmer en silence** (module + job CI
|
||||
ajoutés sans régénérer → « 21 suites » faux) ou une matrice **rouge être commitée
|
||||
verte**. C'est exactement la dérive **« demo 18→21 »** corrigée à la main au commit
|
||||
précédent, mais côté `run`.
|
||||
### Défaut #1 (PRIORITAIRE · bug CI réel) — le gate `check-regression` était cassé en checkout propre
|
||||
|
||||
**Vérif de faisabilité avant d'agir (#1 analyser)** : `regression_run.json` **ne
|
||||
contient aucun horodatage/hôte/chemin absolu** — deux `run` successifs sont
|
||||
**byte-identiques** (vérifié) et le résumé **matche le commité**. Le run complet
|
||||
prend **~5,3 s** (stdlib pur). L'égalité exacte est donc licite et bon marché.
|
||||
- **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é`).
|
||||
|
||||
**Fichiers** (3 · aucun code métier / chiffre touché) :
|
||||
- **`ci/check_regression.sh`** (nouveau, exécutable) : rejoue `regression_gen.py
|
||||
run` vers un tmp, exige `regression_run.json` **byte-identique** au commité, puis
|
||||
`verdict == PASS` ; sur dérive, imprime le **diff des `totals`** et l'ordre de
|
||||
régénérer. Complément direct de `check_artifacts` (documenté dans l'en-tête).
|
||||
- **`.gitea/workflows/ci.yml`** : + job **`check-regression`** (5ᵉ gate statique,
|
||||
en-tête explicatif) + ajout dans les `needs` du job `gate` agrégat.
|
||||
- **`ci/README.md`** : ligne du tableau des jobs, section `check_regression.sh`,
|
||||
commande d'exécution locale, checklist « 4 jobs → 5 gates statiques », et note du
|
||||
§`check_artifacts` renvoyant désormais à `check-regression` pour l'artefact `run`.
|
||||
### Défaut #2 (invariant manquant) — dérive silencieuse disque→CI non gardée
|
||||
|
||||
**Anti-invention (#6)** : rien inventé — le gate **régénère** la vérité au lieu de
|
||||
figer un compteur. Les nombres du log (« 534 / 21 / PASS ») sont **relus** du run
|
||||
frais faisant autorité.
|
||||
- **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.
|
||||
|
||||
**Non-régression du reste du système (vérifié)** : le nouveau job **n'a pas de
|
||||
`working-directory`** → `q4lib.registry.parse_ci` (qui recense les modules depuis
|
||||
les `working-directory`, réutilisé par audit_4big **et** acceptance) est **inchangé**
|
||||
→ aucun décalage du `ci_modules_count` consommé par `demo/scenarios`. Preuve :
|
||||
`check_artifacts.sh` **exit 0** (0 dérive des artefacts dérivés) ; suites
|
||||
`qa/audit_4big`, `qa/acceptance`, `demo/scenarios` **vertes**.
|
||||
**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.
|
||||
|
||||
**Vérifs** : preuve de morsure — artefact commité **falsifié** (534→999) ⇒ gate
|
||||
**exit 1** avec diff des `totals` ; restauré ⇒ **exit 0**. Les **5 gates** locaux
|
||||
verts (`guard_constraints`/`validate_json`/`check_docs`/`check_artifacts`/
|
||||
`check_regression`) ; `ci.yml` **YAML valide** ; matrice **534/534 · 21/21 · PASS**
|
||||
inchangée.
|
||||
**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.
|
||||
|
||||
**Hors périmètre worker (VPS · #8)** : néant (gate stdlib pur en-repo ; enregistrement
|
||||
runner Gitea toujours du ressort DevOps, §4 du `ci/README`).
|
||||
**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.
|
||||
|
||||
Reference in New Issue
Block a user