[DTP-Worker 20260803_070711] Sprint 8 QA · fix · 3 surfaces jumelles résiduelles du mensonge regression_run.json « non déterministe/non commité »

Le fix 89eb8e6 (session 053701) avait corrigé regression_gen.py docstring +
README, mais la même contre-vérité subsistait dans 3 surfaces manquées :
 · reglib/builder.py:10-13 (docstring module — pointait vers .gitignore comme
   autorité alors que le .gitignore affirme l'INVERSE)
 · reglib/builder.py:71 (docstring run_matrix)
 · regression_gen.py:82-85 (champ hors_perimetre_worker → coule dans MANIFEST.json
   commité/gaté ; contredisait le docstring corrigé 12 lignes plus haut)

Ground-truth (4 sources) : git ls-files (commité) · .gitignore (commité+byte-
déterministe) · ci/check_regression.sh (exige byte-identité d'un run frais) ·
docstring regression_gen.py:26 déjà réaligné. MANIFEST.json régénéré (build),
plan + regression_run.json inchangés. Consommateur aval : aucun. run_ci.sh 30
PASS 0 FAIL. Classe prose-facts-vs-numeric-drift / docstring-vs-code, gate-neutre.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-03 07:15:25 +00:00
parent db817309a0
commit dbde8fac46
4 changed files with 49 additions and 6 deletions
+39
View File
@@ -592,3 +592,42 @@ gate (`check_readme_claims` ne touche que les chiffres « Génération réelle
**Gate-neutre.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Zéro nouveau module, zéro **Gate-neutre.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Zéro nouveau module, zéro
gate ajouté (#5) — la dérive était une citation mal-sourcée, seule la prose mentait. gate ajouté (#5) — la dérive était une citation mal-sourcée, seule la prose mentait.
## Session `20260803_070711` · Sprint 8 QA — fix · 3e & 4e surfaces jumelles du mensonge `regression_run.json` « non déterministe / non commité »
**Contexte.** Le fix `053701` (commit `89eb8e6`) avait corrigé deux surfaces
(`regression_gen.py` docstring + `qa/regression/README.md`) qui prétendaient à tort que
la sortie `run` est « non déterministe → non commitée ». Sweep de contrôle
`grep -rniE "non commit|non déterministe|…"` sur `05_deliverables_mvp/` : **trois**
surfaces résiduelles portaient encore la même contre-vérité, toutes manquées par le fix
précédent (dont mon agent Explore, scope limité aux `*_gen.py`, ne couvrait pas
`reglib/`) :
1. `reglib/builder.py:10-13` — docstring module : « Non déterministe (dépend de la
machine) → non commité (voir .gitignore) ». **Doublement faux** : pointe vers
`.gitignore` comme autorité alors que le `.gitignore` affirme l'**inverse**
(« `out/regression_run.json` (mode `run`) EST commité … byte-déterministe »).
2. `reglib/builder.py:71` — docstring `run_matrix()` : « destiné à un artefact non
commité ».
3. `regression_gen.py:82-85` — champ `hors_perimetre_worker` : « Le mode `run` reste
manuel (non déterministe) » — contredisait le docstring **corrigé 12 lignes plus
haut du même fichier** (« deux `run` sont byte-identiques »). Ce champ coule dans
`out/MANIFEST.json` **commité** → surface **gatée** par la reproductibilité.
**Preuve ground-truth (4 sources concordantes).** `git ls-files` → `regression_run.json`
est commité · `.gitignore` documente commité+byte-déterministe · `ci/check_regression.sh`
(step `check-regression` de `ci.yml`) régénère un run frais et exige la **byte-identité**
avec le commité (toute dérive = CI rouge) · docstring `regression_gen.py:26` déjà réaligné.
**Correctif.** Trois prose-facts réalignées sur la réalité (aucun horodatage/hôte/chemin
absolu → byte-déterministe · commité · gaté par `ci/check_regression.sh`). Surface #3
(gatée) : `MANIFEST.json` régénéré (`build -o out`, 1 ligne). Plan + `regression_run.json`
**inchangés** (aucun compteur touché). Consommateur aval : `grep` = **aucun** module ne
lit ce MANIFEST ; `qa-audit-4big` score depuis la couverture CI, pas depuis ce fichier →
`quality_report.json` inchangé (confirmé par le gate reproductibilité vert).
**Gate-neutre.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Zéro module, zéro gate
ajouté (#5) : classe `prose-facts-vs-numeric-drift` / `docstring-vs-code`, la prose seule
mentait. Leçon mémoire : une correction jumelle doit balayer **toutes** les surfaces (src
`*_gen.py` **+** helpers `*lib/` **+** champs coulant dans artefacts gatés), pas seulement
les deux premières trouvées.
@@ -11,5 +11,5 @@
"regression_plan.json" "regression_plan.json"
], ],
"roadmap": "Sprint 8 · QA · Regression tests exhaustifs", "roadmap": "Sprint 8 · QA · Regression tests exhaustifs",
"hors_perimetre_worker": "Exécution planifiée de `run` sur le runner CI/VPS + publication du compte agrégé dans le desk ERPNext / le pipeline de release → agent QA / DevOps (#8). Le mode `run` reste manuel (non déterministe)." "hors_perimetre_worker": "Exécution planifiée de `run` sur le runner CI/VPS + publication du compte agrégé dans le desk ERPNext / le pipeline de release → agent QA / DevOps (#8) : hors périmètre car planifiée sur le runner/VPS, non parce que la sortie dériverait — `regression_run.json` est byte-déterministe · commité · gaté par `ci/check_regression.sh`."
} }
@@ -8,9 +8,10 @@ Deux artefacts, deux natures :
Verdict PASS ssi couverture prouvée + chaque suite ≥ planchers. Verdict PASS ssi couverture prouvée + chaque suite ≥ planchers.
run → MATRICE live : exécute réellement chaque suite et agrège les compteurs run → MATRICE live : exécute réellement chaque suite et agrège les compteurs
(ran/passed/failures/errors/skipped) + verdict global. Non déterministe (ran/passed/failures/errors/skipped) + verdict global. Aucun horodatage /
(dépend de la machine) → non commité (voir .gitignore), c'est la sortie hôte / chemin absolu → deux `run` sont byte-identiques : l'artefact
qui fait autorité sur « N tests verts ». `regression_run.json` EST commité et byte-gaté par `ci/check_regression.sh`
(voir .gitignore), c'est la sortie qui fait autorité sur « N tests verts ».
Aucune date/horodatage → build reproductible (gate CI stable).""" Aucune date/horodatage → build reproductible (gate CI stable)."""
@@ -68,7 +69,8 @@ def build_plan(spec: dict[str, Any]) -> dict[str, Any]:
def run_matrix(spec: dict[str, Any], *, python: str | None = None) -> dict[str, Any]: def run_matrix(spec: dict[str, Any], *, python: str | None = None) -> dict[str, Any]:
"""Exécute réellement toutes les suites du plan et agrège les compteurs. """Exécute réellement toutes les suites du plan et agrège les compteurs.
Non déterministe (temps, machine) → destiné à un artefact non commité.""" Byte-déterministe (aucun horodatage/hôte/chemin absolu) → l'artefact
`regression_run.json` EST commité et byte-gaté par `ci/check_regression.sh`."""
suites = discovery.discover_suites(spec) suites = discovery.discover_suites(spec)
discover_cmd = list(spec["discover_cmd"]) discover_cmd = list(spec["discover_cmd"])
# `python3 -m unittest ...` → on retire le binaire, on garde les args. # `python3 -m unittest ...` → on retire le binaire, on garde les args.
@@ -82,7 +82,9 @@ def _manifest(spec: dict, plan: dict) -> dict:
"hors_perimetre_worker": ( "hors_perimetre_worker": (
"Exécution planifiée de `run` sur le runner CI/VPS + publication du " "Exécution planifiée de `run` sur le runner CI/VPS + publication du "
"compte agrégé dans le desk ERPNext / le pipeline de release → agent " "compte agrégé dans le desk ERPNext / le pipeline de release → agent "
"QA / DevOps (#8). Le mode `run` reste manuel (non déterministe)." "QA / DevOps (#8) : hors périmètre car planifiée sur le runner/VPS, "
"non parce que la sortie dériverait — `regression_run.json` est "
"byte-déterministe · commité · gaté par `ci/check_regression.sh`."
), ),
} }