[DTP-Worker] Sprint 8 · buffer L75 · Dernière surface de la classe « compte par suite » : les agrégats de tests EN PROSE (crm « 81 (25+31+25) » · erpnext_backend « RBAC 60 (10+11+12+11+16) + e-CF 39 ») n'étaient couverts par AUCUN gate — une compensation entre suites laisserait la table gatée juste et la prose fausse → recompute total ET multiset des composants (ordre-indépendant) depuis plan.suites dans check_readme_claims (3 morsures : total périmé · compensation captée par le multiset · claim disparu)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,10 @@
|
||||
# 03_agents/*/AGENT.md (tables de livrables · colonne « Tests ») :
|
||||
# • chaque cellule « Tests » par suite gatée → plan.suites[path]
|
||||
# (+ count_tests pour le self_module qa/regression, exclu de la matrice)
|
||||
# 03_agents/{crm,erpnext_backend}/AGENT.md (agrégats EN PROSE) :
|
||||
# • « Total CRM N (a+b+c) » · « RBAC N (…) » · « e-CF N » — total ET multiset
|
||||
# des composants recomputés (une compensation entre suites laisserait la
|
||||
# table juste et la prose fausse) → plan.suites[path]
|
||||
# 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é).
|
||||
#
|
||||
@@ -317,6 +321,69 @@ if seen_rows == 0:
|
||||
bad("Comptes par suite · AUCUNE ligne de table « Tests » trouvée dans les "
|
||||
"fiches — la disparition du recensement est elle-même une régression.")
|
||||
|
||||
# ============================================================================
|
||||
# Fiches agents · comptes AGRÉGÉS EN PROSE (« Total X : N tests (a + b + c) ») —
|
||||
# dernière surface de la MÊME classe, un cran au-dessus des cellules de table.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Les cellules « Tests » des tables sont recomputées ci-dessus, mais deux fiches
|
||||
# rédigent EN PROSE un agrégat multi-suites : `crm` (« Total CRM : 81 tests
|
||||
# (25 + 31 + 25) ») et `erpnext_backend` (« RBAC 60 tests (10 + 11 + 12 + 11 + 16)
|
||||
# + e-CF 39 tests »). Ces totaux ET leurs composants sont SAISIS À LA MAIN et
|
||||
# n'étaient couverts par AUCUN gate : une compensation entre deux suites (l'une
|
||||
# +1, l'autre −1) laisserait la table juste et la prose fausse — exactement le
|
||||
# motif per-suite corrigé la session précédente. On RECOMPUTE ici le total ET le
|
||||
# multiset des composants (ordre-indépendant) depuis `auth` (plan.suites, source
|
||||
# faisant autorité déjà chargée), et on exige l'égalité avec ce que la fiche ÉCRIT.
|
||||
# ============================================================================
|
||||
PROSE = [
|
||||
("03_agents/crm/AGENT.md", "Total CRM",
|
||||
r"Total CRM : \*\*(\d+) tests\*\* \(([\d + ]+)\)",
|
||||
["crm/commissions", "crm/dossier_vente", "crm/workflow_vente"]),
|
||||
("03_agents/erpnext_backend/AGENT.md", "RBAC backend",
|
||||
r"\*\*RBAC (\d+) tests\*\* \(([\d + ]+)\)",
|
||||
["rbac", "rbac/apply_plan", "rbac/fixtures_gen",
|
||||
"rbac/roleprofile_gen", "rbac/userperm_gen"]),
|
||||
]
|
||||
for path_f, label, pat, suites_list in PROSE:
|
||||
try:
|
||||
txt = re.sub(r"\s+", " ", open(path_f, encoding="utf-8").read())
|
||||
except OSError as e:
|
||||
bad(f"Prose agrégée · {path_f} illisible : {e}"); continue
|
||||
try:
|
||||
exp_parts = sorted(auth[s] for s in suites_list)
|
||||
except KeyError as e:
|
||||
bad(f"Prose agrégée · {label} — suite {e} absente de plan.suites (câblage ?)")
|
||||
continue
|
||||
exp_total = sum(exp_parts)
|
||||
m = re.search(pat, txt)
|
||||
if m is None:
|
||||
bad(f"Prose agrégée · {path_f} · « {label} » — claim INTROUVABLE "
|
||||
f"(attendu total={exp_total} composants={exp_parts})")
|
||||
continue
|
||||
total = int(m.group(1))
|
||||
parts = sorted(int(x) for x in re.findall(r"\d+", m.group(2)))
|
||||
if total == exp_total and parts == exp_parts and total == sum(parts):
|
||||
good(f"Prose agrégée · {path_f} · {label} — "
|
||||
f"{total} ({'+'.join(map(str, parts))}) == source ({exp_total})")
|
||||
else:
|
||||
bad(f"Prose agrégée · {path_f} · {label} — dit total={total} "
|
||||
f"composants={parts} MAIS source dit total={exp_total} "
|
||||
f"composants={exp_parts} (interne_ok={total == sum(parts)}) "
|
||||
f"→ régénérer la fiche")
|
||||
|
||||
# e-CF : agrégat en prose à SUITE UNIQUE (pas de composants entre parenthèses).
|
||||
be_ecf = re.sub(r"\s+", " ", open("03_agents/erpnext_backend/AGENT.md",
|
||||
encoding="utf-8").read())
|
||||
ecf_exp = auth["fiscal/ecf_dgii"]
|
||||
mecf = re.search(r"\*\*e-CF (\d+) tests\*\*", be_ecf)
|
||||
if mecf is None:
|
||||
bad(f"Prose agrégée · e-CF — claim INTROUVABLE (attendu {ecf_exp})")
|
||||
elif int(mecf.group(1)) == ecf_exp:
|
||||
good(f"Prose agrégée · e-CF — {mecf.group(1)} == source ({ecf_exp})")
|
||||
else:
|
||||
bad(f"Prose agrégée · e-CF — dit {mecf.group(1)} MAIS source dit {ecf_exp} "
|
||||
f"(fiscal/ecf_dgii) → régénérer la fiche")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user