[DTP-Worker] Sprint 8 · buffer L75 · Gate fraîcheur matrice régression (ci/check_regression.sh) : run==commité + verdict PASS
L'artefact le plus cité du dépôt (qa/regression/out/regression_run.json · 21 suites · 534 tests · PASS) n'avait aucun garde-fou CI : check_artifacts ne rejoue que 'build' et exclut les artefacts d'exécution 'run'. Nouveau 5e gate statique rejoue 'regression_gen.py run' (~5s, déterministe) et exige byte-identité + verdict PASS. Rend impossible la re-commission d'une matrice périmée (dérive type demo 18->21) ou rouge commitée verte. parse_ci inchangé (gate sans working-directory) → 0 dérive des counts dérivés (check_artifacts exit 0). Preuve de morsure OK (534->999 => exit 1). 5 gates verts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-1
@@ -60,6 +60,23 @@ jobs:
|
|||||||
- name: Contrôle reproductibilité artefacts
|
- name: Contrôle reproductibilité artefacts
|
||||||
run: bash ci/check_artifacts.sh
|
run: bash ci/check_artifacts.sh
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Fraîcheur de la matrice de régression (Sprint 8 · QA « Regression tests
|
||||||
|
# exhaustifs »). Complète check-artifacts : ce dernier ne rejoue que `build`
|
||||||
|
# et exclut les artefacts d'EXÉCUTION ; ce gate rejoue `run` et exige que le
|
||||||
|
# `qa/regression/out/regression_run.json` commité (21 suites · 534 tests ·
|
||||||
|
# PASS, cité dans toute la doc) soit byte-identique au run frais. Rend
|
||||||
|
# impossible la re-commission d'une matrice périmée (ex. dérive « demo 18→21 »)
|
||||||
|
# ou rouge commitée verte. stdlib pur, zéro pip, ~5 s.
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
check-regression:
|
||||||
|
name: Fraîcheur matrice de régression (run == commité)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Contrôle fraîcheur matrice de régression
|
||||||
|
run: bash ci/check_regression.sh
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Publiciste (Sprint 2) : parser faisabilité → projets_master.json + generator.
|
# Publiciste (Sprint 2) : parser faisabilité → projets_master.json + generator.
|
||||||
# Tests unitaires stdlib pur (unittest) → aucune installation pip requise sur
|
# Tests unitaires stdlib pur (unittest) → aucune installation pip requise sur
|
||||||
@@ -445,7 +462,7 @@ jobs:
|
|||||||
gate:
|
gate:
|
||||||
name: Gate qualité (agrégat)
|
name: Gate qualité (agrégat)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [constraints-guard, validate-json, check-docs, check-artifacts, publiciste-tests, rbac-tests, faisabilite-gen-tests, rbac-fixtures-tests, rbac-userperm-tests, rbac-roleprofile-tests, rbac-applyplan-tests, bancable-tests, crm-workflow-vente-tests, crm-dossier-vente-tests, crm-commissions-tests, fiscal-ecf-tests, frontend-portails-tests, legal-confotur-tests, qa-audit-5d-tests, seo-tests, chat-otoia-tests, qa-audit-4big-tests, demo-scenario-tests, qa-regression-tests, devops-deploy-runbook-tests, qa-acceptance-tests]
|
needs: [constraints-guard, validate-json, check-docs, check-artifacts, check-regression, publiciste-tests, rbac-tests, faisabilite-gen-tests, rbac-fixtures-tests, rbac-userperm-tests, rbac-roleprofile-tests, rbac-applyplan-tests, bancable-tests, crm-workflow-vente-tests, crm-dossier-vente-tests, crm-commissions-tests, fiscal-ecf-tests, frontend-portails-tests, legal-confotur-tests, qa-audit-5d-tests, seo-tests, chat-otoia-tests, qa-audit-4big-tests, demo-scenario-tests, qa-regression-tests, devops-deploy-runbook-tests, qa-acceptance-tests]
|
||||||
steps:
|
steps:
|
||||||
- name: Résultat
|
- name: Résultat
|
||||||
run: echo "✅ Gate qualité 4Big franchi — tous les checks verts."
|
run: echo "✅ Gate qualité 4Big franchi — tous les checks verts."
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# Activity Log · 2026-07-31 · Claude Code DTP
|
||||||
|
|
||||||
|
## Session `20260730_235919` · Buffer S8 · Gate fraîcheur matrice de régression (5ᵉ gate 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.
|
||||||
|
|
||||||
|
**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`.
|
||||||
|
|
||||||
|
**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é.
|
||||||
|
|
||||||
|
**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`.
|
||||||
|
|
||||||
|
**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é.
|
||||||
|
|
||||||
|
**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**.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
**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`).
|
||||||
|
|
||||||
|
**Auto-score 4Big** : 96/100.
|
||||||
+20
-3
@@ -18,6 +18,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa
|
|||||||
| `validate-json` | `ci/validate_json.sh` | Parse strict de tous les `*.json` (schémas Faisabilité↔Publiciste) | ✅ oui |
|
| `validate-json` | `ci/validate_json.sh` | Parse strict de tous les `*.json` (schémas Faisabilité↔Publiciste) | ✅ oui |
|
||||||
| `check-docs` | `ci/check_docs.sh` | Liens Markdown internes + présence auto-score 4Big | ✅ oui (liens) |
|
| `check-docs` | `ci/check_docs.sh` | Liens Markdown internes + présence auto-score 4Big | ✅ oui (liens) |
|
||||||
| `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui |
|
| `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui |
|
||||||
|
| `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui |
|
||||||
| `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
|
| `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
|
||||||
|
|
||||||
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec
|
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec
|
||||||
@@ -60,7 +61,22 @@ artefacts d'**autres** modules (ex. `demo/scenarios` lit
|
|||||||
consommateur devient périmé s'il n'est pas régénéré — dérive qu'aucune suite
|
consommateur devient périmé s'il n'est pas régénéré — dérive qu'aucune suite
|
||||||
`tests/` (qui teste des fonctions, pas le fichier commité) n'attrape. Correctif :
|
`tests/` (qui teste des fonctions, pas le fichier commité) n'attrape. Correctif :
|
||||||
`build -o out` puis commit. Les artefacts d'**exécution** non produits par `build`
|
`build -o out` puis commit. Les artefacts d'**exécution** non produits par `build`
|
||||||
(p.ex. `qa/regression/out/regression_run.json`, issu de `run`) ne sont pas comparés.
|
(p.ex. `qa/regression/out/regression_run.json`, issu de `run`) sont hors de ce
|
||||||
|
gate — ils sont couverts par `check-regression` ci-dessous.
|
||||||
|
|
||||||
|
### `check_regression.sh`
|
||||||
|
Rejoue la **matrice de régression** complète (`qa/regression/regression_gen.py run`,
|
||||||
|
~5 s, stdlib pur) vers un tmp et exige que le `regression_run.json` frais soit
|
||||||
|
**byte-identique** au commité, puis que son verdict soit `PASS`. Complément direct
|
||||||
|
de `check_artifacts` : celui-ci ne rejoue que `build` (→ `regression_plan.json`) et
|
||||||
|
laisse hors périmètre l'artefact d'**exécution** `regression_run.json` — pourtant
|
||||||
|
c'est *lui* qui porte les compteurs cités partout dans la doc et les logs
|
||||||
|
(« 21 suites · 534 tests · PASS »). Sans ce gate, ce chiffre pouvait se périmer en
|
||||||
|
silence (module + job CI ajoutés sans régénérer la matrice → « 21 suites » faux ;
|
||||||
|
c'est la dérive « demo 18→21 » corrigée à la main), ou une matrice rouge être
|
||||||
|
commitée verte. `regression_run.json` ne contient aucun horodatage/hôte → le run est
|
||||||
|
déterministe et l'égalité exacte licite. Correctif : `regression_gen.py run -o out`
|
||||||
|
puis commit.
|
||||||
|
|
||||||
## 3. Exécution locale (avant push)
|
## 3. Exécution locale (avant push)
|
||||||
|
|
||||||
@@ -68,7 +84,8 @@ consommateur devient périmé s'il n'est pas régénéré — dérive qu'aucune
|
|||||||
bash ci/guard_constraints.sh # contraintes CLAUDE.md
|
bash ci/guard_constraints.sh # contraintes CLAUDE.md
|
||||||
bash ci/validate_json.sh # schémas JSON
|
bash ci/validate_json.sh # schémas JSON
|
||||||
bash ci/check_docs.sh # liens + score
|
bash ci/check_docs.sh # liens + score
|
||||||
bash ci/check_artifacts.sh # reproductibilité out/
|
bash ci/check_artifacts.sh # reproductibilité out/ (build)
|
||||||
|
bash ci/check_regression.sh # fraîcheur matrice régression (run)
|
||||||
```
|
```
|
||||||
|
|
||||||
Chaque script retourne `0` si conforme, `1` sinon. Reproduit exactement ce que
|
Chaque script retourne `0` si conforme, `1` sinon. Reproduit exactement ce que
|
||||||
@@ -100,7 +117,7 @@ Actions** doit être activé, et le runner doit apparaître « Idle ».
|
|||||||
|
|
||||||
- `[ ]` Gitea Actions activé au niveau instance ET repo.
|
- `[ ]` Gitea Actions activé au niveau instance ET repo.
|
||||||
- `[ ]` `act_runner` enregistré, label `ubuntu-latest`, statut Idle.
|
- `[ ]` `act_runner` enregistré, label `ubuntu-latest`, statut Idle.
|
||||||
- `[ ]` Push de test → les 4 jobs apparaissent et passent au vert.
|
- `[ ]` Push de test → les 5 gates statiques apparaissent et passent au vert.
|
||||||
- `[ ]` PR de test avec violation volontaire → `constraints-guard` bloque (rouge).
|
- `[ ]` PR de test avec violation volontaire → `constraints-guard` bloque (rouge).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Executable
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# check_regression.sh · OTO Enterprise OS DTP
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Gate de FRAÎCHEUR de la matrice de régression (roadmap Sprint 8 · QA
|
||||||
|
# « Regression tests exhaustifs » · CLAUDE.md #5 — zéro régression silencieuse).
|
||||||
|
# Prouve, en une commande, que l'artefact d'EXÉCUTION commité
|
||||||
|
# `qa/regression/out/regression_run.json` (totaux « 21 suites · 534 tests ·
|
||||||
|
# verdict PASS » cité partout dans la doc et les logs) reflète TOUJOURS la
|
||||||
|
# réalité du disque — en le régénérant et en exigeant l'identité byte-for-byte.
|
||||||
|
#
|
||||||
|
# Pourquoi un gate DÉDIÉ (et non `check_artifacts.sh`) : ce dernier ne rejoue
|
||||||
|
# QUE la sous-commande déterministe `build` (→ regression_plan.json) et exclut
|
||||||
|
# explicitement les artefacts d'EXÉCUTION issus de `run`. Or `regression_run.json`
|
||||||
|
# EST un artefact de `run` : il porte les compteurs réellement exécutés
|
||||||
|
# (ran/passed/failures) que `build` ne connaît pas. Résultat : sans ce gate, la
|
||||||
|
# matrice agrégée pouvait se périmer en silence (ex. un module + son job CI
|
||||||
|
# ajoutés sans régénérer la matrice → « 21 suites » devenu faux) sans qu'aucune
|
||||||
|
# suite `tests/` — qui teste des FONCTIONS, pas le fichier commité — ne l'attrape.
|
||||||
|
# C'est exactement la dérive « demo 18→21 » déjà corrigée à la main : ce gate la
|
||||||
|
# rend impossible à re-commettre.
|
||||||
|
#
|
||||||
|
# Déterminisme : `regression_run.json` ne contient AUCUN horodatage / hôte /
|
||||||
|
# chemin absolu — deux `run` successifs sont byte-identiques (vérifié). La
|
||||||
|
# comparaison exacte est donc licite et constitue la preuve la plus forte.
|
||||||
|
#
|
||||||
|
# Contrat vérifié : `regression_gen.py run` régénéré vers un tmp DOIT produire un
|
||||||
|
# `regression_run.json` byte-identique au commité. Toute divergence (compteur,
|
||||||
|
# suite ajoutée/retirée, verdict) échoue le gate avec l'ordre de régénérer.
|
||||||
|
# En bonus : le verdict fraîchement calculé DOIT être « PASS » (une matrice
|
||||||
|
# rouge ne doit jamais être commitée verte).
|
||||||
|
#
|
||||||
|
# Dépendances : bash, git, python3, diff, mktemp. Aucun réseau requis
|
||||||
|
# (stdlib pur · CLAUDE.md #2 — runner Gitea sans pip).
|
||||||
|
# ============================================================================
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
FAIL=0
|
||||||
|
report() { printf ' \033[31m✗\033[0m %s\n' "$1"; FAIL=1; }
|
||||||
|
ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
|
||||||
|
|
||||||
|
MOD="05_deliverables_mvp/qa/regression"
|
||||||
|
GEN="regression_gen.py"
|
||||||
|
ARTIFACT="out/regression_run.json"
|
||||||
|
COMMITTED="$MOD/$ARTIFACT"
|
||||||
|
|
||||||
|
echo "== Fraîcheur de la matrice de régression (run frais == commité ?) =="
|
||||||
|
|
||||||
|
if [[ ! -f "$MOD/$GEN" ]]; then
|
||||||
|
report "$MOD/$GEN — générateur introuvable (module déplacé/renommé ?)"
|
||||||
|
elif [[ ! -f "$COMMITTED" ]]; then
|
||||||
|
report "$COMMITTED — artefact d'exécution absent du dépôt (lancer 'run -o out')"
|
||||||
|
else
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
if ! ( cd "$MOD" && python3 "$GEN" run -o "$tmp" ) >/dev/null 2>"$tmp/.err"; then
|
||||||
|
report "$MOD — 'run' a échoué (matrice non ré-exécutable) :"
|
||||||
|
sed 's/^/ /' "$tmp/.err"
|
||||||
|
else
|
||||||
|
fresh="$tmp/regression_run.json"
|
||||||
|
if [[ ! -f "$fresh" ]]; then
|
||||||
|
report "$MOD — 'run' n'a produit aucun regression_run.json (générateur cassé ?)"
|
||||||
|
elif ! diff -q "$fresh" "$COMMITTED" >/dev/null 2>&1; then
|
||||||
|
report "$ARTIFACT — DÉRIVE : le run frais diffère du commité (régénérer)"
|
||||||
|
printf ' \033[90m# diff résumé (commité → frais) :\033[0m\n'
|
||||||
|
diff <(python3 -c "import json,sys;print(json.dumps(json.load(open(sys.argv[1]))['totals'],indent=1,sort_keys=True))" "$COMMITTED" 2>/dev/null) \
|
||||||
|
<(python3 -c "import json,sys;print(json.dumps(json.load(open(sys.argv[1]))['totals'],indent=1,sort_keys=True))" "$fresh" 2>/dev/null) \
|
||||||
|
| sed 's/^/ /'
|
||||||
|
else
|
||||||
|
verdict="$(python3 -c "import json;print(json.load(open('$COMMITTED'))['verdict'])" 2>/dev/null)"
|
||||||
|
totals="$(python3 -c "import json;t=json.load(open('$COMMITTED'))['totals'];print(f\"{t['suites']} suites · {t['passed']}/{t['ran']} tests\")" 2>/dev/null)"
|
||||||
|
if [[ "$verdict" != "PASS" ]]; then
|
||||||
|
report "$ARTIFACT — reproductible MAIS verdict=$verdict (matrice rouge commitée)"
|
||||||
|
else
|
||||||
|
ok "$ARTIFACT — reproductible à l'identique ($totals · PASS)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -rf "$tmp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
if [[ "$FAIL" -eq 0 ]]; then
|
||||||
|
echo -e "\033[32m✅ Matrice de régression à jour (run frais == commité · PASS).\033[0m"
|
||||||
|
else
|
||||||
|
echo -e "\033[31m❌ Matrice périmée/rouge — régénérer avec 'regression_gen.py run -o out'.\033[0m"
|
||||||
|
fi
|
||||||
|
exit "$FAIL"
|
||||||
Reference in New Issue
Block a user