[DTP-Worker] Sprint 8 · buffer L75 · Chiffre périmé fiche agent : "repo 560 tests" (vert-trompeur ungated) → 558/22 sourcé matrice + gate étendu

Défaut réel (#6 anti-invention) : 03_agents/erpnext_backend/AGENT.md situait les
tests backend par "repo : 560 tests au total" saisi à la main. 560 ne correspond à
aucun compte courant (matrice qa/regression = 558/22 ; repo-wide = 584 méthodes
test_*) — périmé en silence (534→551→558 ; 560 = ancien 534 + 26 harnais self-exclu
INV3). Même classe de vert-trompeur que la fiche QA (21/534), mais ungated.

- Vérif ciblée : toutes les autres bornes chiffrées des 13 AGENT.md exactes
  (crm 81, RBAC 60, e-CF 39, portails 19, chat 31, confotur 44, seo 36).
- Fix : le nombre pointe désormais vers l'artefact gaté (regression_run.json).
- Gate : ci/check_readme_claims.sh recalcule tests+suites+verdict depuis
  regression_run et exige l'égalité avec la fiche backend (comme la fiche QA).
- Preuve morsure : 560 réinjecté ⇒ gate rouge ; restauré ⇒ vert.
- 7 gates verts ; édition doc ⇒ aucune dérive out/ (check_artifacts vert).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-31 04:36:20 +00:00
parent aebe9b7ba9
commit 504bfb0f10
3 changed files with 96 additions and 2 deletions
+3 -2
View File
@@ -36,8 +36,9 @@ le VPS reçoit — jamais l'inverse.
`rbac_50_roles.json` est le **référentiel de rôles de tout le repo** — le workflow `rbac_50_roles.json` est le **référentiel de rôles de tout le repo** — le workflow
vente, le barème commissions, le DocType Dossier Vente et le plan e-CF **résolvent** vente, le barème commissions, le DocType Dossier Vente et le plan e-CF **résolvent**
leurs rôles depuis ce fichier, jamais un nom Frappe en dur. Total backend **RBAC 60 leurs rôles depuis ce fichier, jamais un nom Frappe en dur. Total backend **RBAC 60
tests** (10 + 11 + 12 + 11 + 16) + **e-CF 39 tests**, tous gated dans le CI (repo : tests** (10 + 11 + 12 + 11 + 16) + **e-CF 39 tests**, tous gated dans le CI (matrice
**560 tests** au total). de régression du repo : **558 tests · 22 suites** · verdict `PASS`, source
`qa/regression/out/regression_run.json` — jamais compté à la main · #6).
## Hand-off reçus (à appliquer sur le VPS, dans l'ordre) ## Hand-off reçus (à appliquer sur le VPS, dans l'ordre)
- **CRM** → `crm/dossier_vente/out/` (DocType porteur) **AVANT** `crm/workflow_vente/out/` - **CRM** → `crm/dossier_vente/out/` (DocType porteur) **AVANT** `crm/workflow_vente/out/`
+57
View File
@@ -481,3 +481,60 @@ PASS** ; artefacts `out/` reproductibles à l'identique.
runner Gitea toujours du ressort DevOps). runner Gitea toujours du ressort DevOps).
**Auto-score 4Big** : 96/100. **Auto-score 4Big** : 96/100.
---
## Session `20260731_043004` · Buffer S8 · Chiffre périmé dans une fiche agent (vert-trompeur ungated) + extension du gate
**Tâche** : **Sprint 8 · buffer L75 (« Regression tests exhaustifs »)** — roadmap
fonctionnellement close (22/22 modules gated · 558 tests · 7 gates statiques verts).
Recherche d'un **défaut réel** (chiffre inventé/périmé · CLAUDE.md #6) plutôt qu'un
correctif cosmétique.
### Défaut trouvé — `03_agents/erpnext_backend/AGENT.md` : « repo : **560 tests** au total »
- **Symptôme** : la ligne « source unique » situait les tests backend dans le repo
par un compte agrégé **saisi à la main** (« repo : 560 tests au total »).
- **Contradiction** : `560` ne correspond **à AUCUN** compte courant —
- matrice de régression faisant autorité (`qa/regression/out/regression_run.json`) :
**558 tests · 22 suites · PASS** ;
- repo-wide (méthodes `test_*` sur disque, 23 fichiers) : **584**.
`560` = ancien `534` (matrice d'alors) `+ 26` (harnais `qa/regression`, self-exclu
INV3) — **périmé en silence** au fil de 534 → 551 → 558. Même classe de « vert
trompeur » que la fiche QA (21/534) corrigée la session précédente, mais dans une
fiche agent qu'**aucun gate ne couvrait**.
- **Vérif ciblée (#1 analyser)** : recomputé **toutes** les autres bornes chiffrées
des 13 `AGENT.md` — `crm 81=(25+31+25)`, `RBAC 60=(10+16+11+11+12)`, `e-CF 39`,
`portails 19`, `chat 31`, `confotur 44`, `seo 36` : **toutes exactes**. Le `560`
était le **seul** nombre dérivé.
### Fix (2 volets · corriger + garder)
1. **`03_agents/erpnext_backend/AGENT.md`** : `560` → **« matrice de régression du
repo : 558 tests · 22 suites · verdict PASS, source
`qa/regression/out/regression_run.json` — jamais compté à la main · #6 »** (le
nombre pointe désormais vers l'artefact gaté, pas une saisie).
2. **`ci/check_readme_claims.sh`** : nouveau bloc qui **recalcule** tests+suites+
verdict depuis `regression_run` (même source que la fiche QA) et exige l'égalité
avec ce que la fiche backend **écrit** ; un claim absent échoue aussi. Header du
gate mis à jour (liste des docs couverts).
### Preuves
- **Passe** : `Fiche Backend · … 558/22/PASS == regression_run` (✓).
- **Morsure** : `560` réinjecté ⇒ gate **rouge** (`dit 560/22/PASS MAIS regression_run
dit 558/22/PASS → régénérer la fiche`) ; restauré ⇒ vert.
- **Régression nulle** : édition d'un `AGENT.md` (doc) → **aucune dérive** d'artefact
`out/` (`check_artifacts` vert : `quality_report.json` inchangé).
**Vérifs** : **7 gates** locaux verts (`guard_constraints` · `validate_json` ·
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
`check_readme_claims`) ; matrice **558/22 · PASS**.
**Anti-invention (#6)** : rien inventé — le gate **régénère** la vérité (matrice
re-agrégée) au lieu de figer un compteur ; le chiffre de la fiche **découle** de
l'artefact.
**Hors périmètre worker (VPS · #8)** : néant (docs + gate bash/stdlib en-repo).
**Auto-score 4Big** : 96/100.
+36
View File
@@ -29,6 +29,9 @@
# 03_agents/qa/AGENT.md (fiche QA · « Verdict agrégé courant ») : # 03_agents/qa/AGENT.md (fiche QA · « Verdict agrégé courant ») :
# • N suites · M tests · M passés · E échec · E erreur · verdict # • N suites · M tests · M passés · E échec · E erreur · verdict
# → qa/regression run # → qa/regression run
# 03_agents/erpnext_backend/AGENT.md (« source unique ») :
# • N tests · M suites · verdict (matrice de régression du repo)
# → qa/regression run
# Un claim absent échoue AUSSI (la dérive de formulation qui ferait disparaître # Un claim absent échoue AUSSI (la dérive de formulation qui ferait disparaître
# un chiffre est elle-même une régression de traçabilité). # un chiffre est elle-même une régression de traçabilité).
# #
@@ -179,6 +182,39 @@ else:
bad(f"Fiche QA · {QA} dit {'/'.join(mqa.groups())} MAIS regression_run dit " bad(f"Fiche QA · {QA} dit {'/'.join(mqa.groups())} MAIS regression_run dit "
f"{'/'.join(agg)} ({qa_src}) → régénérer la fiche") f"{'/'.join(agg)} ({qa_src}) → régénérer la fiche")
# ============================================================================
# Fiche ERPNext Backend (03_agents/erpnext_backend/AGENT.md) · même classe.
# ----------------------------------------------------------------------------
# Sa ligne « source unique » situait les tests backend dans le repo par un
# compte agrégé (« repo : 560 tests au total ») saisi à la main. Ce nombre s'est
# PÉRIMÉ en silence (matrice 534 → 551 → 558) : 560 ne correspondait plus ni à la
# matrice (558) ni au repo-wide (584 méthodes test_*). Aucun gate ne le couvrait.
# On le RECALCULE depuis la même matrice faisant autorité (regression_run) et on
# exige tests + suites + verdict — comme la fiche QA.
# ============================================================================
BE = "03_agents/erpnext_backend/AGENT.md"
try:
be_txt = re.sub(r"\s+", " ", open(BE, encoding="utf-8").read())
except OSError as e:
bad(f"Fiche Backend · {BE} illisible : {e}"); be_txt = ""
be_exp = (
str(sum(s["ran"] for s in suites)), # tests exécutés (matrice)
str(len(suites)), # suites
run["verdict"], # verdict
)
be_pat = (r"matrice\s+de régression du repo : \*\*(\d+) tests · (\d+) suites\*\* · "
r"verdict `(PASS|FAIL)`")
be_src = f"tests={be_exp[0]} suites={be_exp[1]} verdict={be_exp[2]}"
mbe = re.search(be_pat, be_txt)
if mbe is None:
bad(f"Fiche Backend · matrice repo — claim INTROUVABLE dans {BE} (attendu {be_src})")
elif mbe.groups() == be_exp:
good(f"Fiche Backend · matrice repo — {BE} {'/'.join(mbe.groups())} == regression_run ({be_src})")
else:
bad(f"Fiche Backend · {BE} dit {'/'.join(mbe.groups())} MAIS regression_run dit "
f"{'/'.join(be_exp)} ({be_src}) → régénérer la fiche")
sys.exit(1 if FAIL else 0) sys.exit(1 if FAIL else 0)
PY PY
rc=$? rc=$?