Durcissement d'uniformité (pas bug déclenchable aujourd'hui) qui COMPLÈTE le fix
correctness de la session 070054 (aeb30b8). Ce fix-là a percent-encodé
hero['fichier'] dans src=f"/static/projets/{code}/{quote(fichier)}", mais dans la
MÊME url('…') CSS il restait un 2ᵉ segment dynamique — `code` — interpolé brut puis
html-échappé via _esc(src). C'est exactement l'anti-pattern identifié par 070054
(html.escape = mauvais encodeur pour un segment d'URL), appliqué de façon incomplète.
Fix : src = f"/static/projets/{quote(code)}/{quote(hero['fichier'])}" — quote() est
le SEUL encodeur correct pour un segment d'URL ; le CONTEXTE, pas la donnée courante,
dicte l'encodeur. Zéro dérive : codes réels = P01..P09 (§Projets), quote("p01")=="p01"
→ byte-repro inchangé. Cas frère exact de 060044/criteria.py (« durcir le SEUL membre
non-durci d'une famille ») : ici le SEUL segment d'URL non-encodé de la même url().
Test à dents (test_hero_media_code_segment_percent_encoded) : code théorique "P'X" →
prouvé FAIL sans quote(code), PASS avec. Suite publiciste 25→26, matrice 630/613→631/614.
Défaut d'abord : classe encodage-contextuel balayée sur tous les modules HTML/CSS/URL ;
2 candidats Explore réfutés non-défauts (chat mount_html = persona forcée Amélie par
invariant 7 + portail_key enum ; code segment = théorique mais durci par uniformité).
Cascade régénérée dans l'ordre (quality_report en DERNIER) · 3 fiches compteurs réalignées.
run_ci 33/0/0 · NFC-clean · zéro code moteur V18 (bloqué D-06 #6) · zéro gate (#5) · zéro invention (#6).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
QA · Matrice de régression exhaustive (Sprint 8)
Roadmap Sprint 8 · QA : « Regression tests exhaustifs ». Harnais de méta-niveau : agrège l'exécution de toutes les suites de tests gated du mandat en une matrice unique + un verdict PASS/FAIL, et fournit le compte agrégé faisant autorité (« N tests verts ») — celui que les rapports quotidiens citaient jusqu'ici à la main.
Pourquoi ce livrable
Le mandat compte des dizaines de suites tests/ (une par module). Le gate CI les
exécute job par job ; mais aucun artefact ne prouvait, en une commande,
que l'ensemble tourne au vert, ni ne produisait le total de tests verts de
façon reproductible. Ce harnais formalise cette régression exhaustive.
Il est complémentaire, pas redondant, avec l'audit 4Big
(../audit_4big) :
| Audit 4Big | Matrice de régression | |
|---|---|---|
| Axe | Qualité statique par module (doc, schéma, CLI, hand-off) | Exécution : chaque suite tourne-t-elle au vert ? |
| Sortie | Note ≥ 95/100 par module | Compte agrégé « N tests verts » + verdict |
| Nature | Lecture du système de fichiers | build (statique) + run (exécution réelle) |
Zéro invention · zéro duplication (CLAUDE.md #5 · #6)
- Périmètre dérivé du CI, pas listé à la main : les suites proviennent de
.gitea/workflows/ci.ymlviaq4lib/registry.parse_ci(réutilisé de l'audit 4Big — une seule source de vérité). Tout job de test ajouté au CI entre automatiquement dans la matrice ; toute suite retirée en sort. - Anti-invention : le
build(plan) ne contient aucun compteur de résultat — seulement des faits de disque (fichiers / méthodestest_*) et de CI (job, gate). Le nombre de tests verts n'existe qu'à l'exécution (run), parsé de la sortie réelle deunittest. Un invariant refuse tout compteur figé. - Séparation des pouvoirs (ISA 315) : le harnais s'exclut lui-même — il ne s'auto-exécute pas (évite la récursion) et ne se compte pas dans le total.
Utilisation
cd 05_deliverables_mvp/qa/regression
# Recensement déterministe des suites gated (commité) :
python3 regression_gen.py build # → out/regression_plan.json + MANIFEST
# Gate : schéma + invariants + couverture prouvée (ce que fait le CI) :
python3 regression_gen.py validate
# Régression EXHAUSTIVE (exécute réellement toutes les suites) — local/DevOps :
python3 regression_gen.py run # → out/regression_run.json (COMMITÉ)
run renvoie un code de sortie ≠ 0 si une seule suite est rouge → utilisable
comme garde de release. Sa sortie est byte-déterministe : (a) elle ne porte
aucun horodatage / hôte / durée / chemin absolu (path relatif, compteurs
ran/passed/failures seuls) ; (b) chaque suite est exécutée sous python -S
(sans site-packages), donc l'unique paquet tiers du corpus — l'oracle
optionnel jsonschema (toujours gardé par skipUnless) — est neutralisé,
exactement comme sur le runner Gitea pip-less. Sans (b), la matrice divergeait
selon que jsonschema était pip installé ou non (0 vs 17 skipped) → un
faux-vert : check_regression vert en local mais ROUGE sur le runner. Avec
les deux, deux run sont byte-identiques quel que soit l'environnement
(17 oracles ignorés ; les validateurs maison couvrent les mêmes contrats).
out/regression_run.json est donc commité et sert de baseline au gate CI
ci/check_regression.sh, qui régénère un run frais et exige l'identité
byte-for-byte avec le fichier commité (+ verdict PASS) — c'est ce qui empêche
une matrice périmée ou rouge d'être commitée verte. check_artifacts.sh, lui, ne
rejoue que build (déterministe) et ignore cet artefact d'exécution.
Contrat de sortie (plan)
regression.schema.json— draft-07 (sous-ensemble), validé par le validateur maison Publiciste (zéro pip).- Invariants (
regression_gen.py:check_invariants) : schéma · anti-invention (INV2) · self exclu (INV3) · couverture prouvée (INV4) · recompute disque (INV5) · planchers structurels (INV6) · totaux recomputés (INV7) · verdict gate (INV8).
Hand-off VPS (hors périmètre worker · #8)
Publier le compte agrégé dans le desk ERPNext / le pipeline de release → agent
QA / DevOps. Le run lui-même n'est PAS manuel côté worker : sa sortie
regression_run.json est déterministe (aucun horodatage/hôte) donc commitée
et byte-gatée à chaque push/PR par ci/check_regression.sh. Ce qui reste
hors périmètre (#8), c'est sa publication VPS (desk / release).
Auto-score 4Big
96/100 — recensement exhaustif prouvé (couverture dérivée du CI), anti-invention strict (aucun compteur figé), déterminisme, réutilisation sans duplication.