[DTP-Worker 20260801_230453] Sprint 8 · buffer · QA/Audit 5D : gate d'IDENTITÉ du VERDICT GLOBAL — le LABEL de conclusion de l'audit (PASS / PASS_WITH_OPEN_ITEMS / FAIL), la HEADLINE du livrable, était HORS de tout gate : le bloc audit_5d existant recompute la ventilation chiffrée (13 PASS · 0 FAIL · 4 à confirmer), les open items, les 5 dimensions et les livrables audités — jamais le label lui-même. Or il est data-derived (audit_report.json[verdict] == MANIFEST.json[verdict], byte-gatés), recalculé par builder.py (FAIL si ≥1 FAIL · sinon PASS_WITH_OPEN_ITEMS si ≥1 A_CONFIRMER · sinon PASS) et transcrit à la main à 2 endroits du README (la RÈGLE l.39 + le VERDICT COURANT l.49). Nouveau bloc : on MIROITE la règle et on exige (a) HONNÊTETÉ interne audit_report.verdict == verdict(triplet) — un PASS forcé pendant que 4 params restent a_confirmer (CŒUR #6 : ne jamais forcer un PASS) est mordu ; (b) MANIFEST.verdict == audit_report.verdict ; (c) label « Verdict courant » README == artefact ; (d) la règle « Verdict global » énumère EXACTEMENT le codomaine {FAIL, PASS_WITH_OPEN_ITEMS, PASS} du générateur. 5 morsures vérifiées (2 README · 2 artefact + M3 cascade a+b+c · 1 disparition), 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2272,6 +2272,94 @@ if a5_ctl is not None:
|
||||
bad(f"Audit 5D · {A5D_FI} — dit {'/'.join(m.groups())} MAIS artefact dit "
|
||||
f"{a5_ctl}/{a5_dim} ({a5_src}) → régénérer la fiche")
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Audit 5D · IDENTITÉ du VERDICT GLOBAL — le LABEL de conclusion (`PASS` /
|
||||
# `PASS_WITH_OPEN_ITEMS` / `FAIL`), pas seulement sa ventilation chiffrée.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le bloc de ventilation ci-dessus recompute le TRIPLET chiffré (pass/fail/à
|
||||
# confirmer) mais reste AVEUGLE au LABEL de verdict lui-même — la HEADLINE de tout
|
||||
# l'audit. Ce label est data-derived (out/audit_report.json[verdict] == out/
|
||||
# MANIFEST.json[verdict], byte-gatés par check_artifacts ⇒ faisant autorité),
|
||||
# recalculé par le générateur (qalib/builder.py) via la règle EXACTE : `FAIL` si
|
||||
# ≥1 FAIL · sinon `PASS_WITH_OPEN_ITEMS` si ≥1 A_CONFIRMER · sinon `PASS`. Le README
|
||||
# le transcrit À LA MAIN à DEUX endroits — la RÈGLE (« Verdict global : `FAIL` si …
|
||||
# sinon `PASS_WITH_OPEN_ITEMS` … sinon `PASS` ») ET le VERDICT COURANT
|
||||
# (« ## Verdict courant \n `PASS_WITH_OPEN_ITEMS` — **13 PASS · 0 FAIL · 4 à
|
||||
# confirmer** »). AUCUN gate ne le vérifiait : le triplet peut coller à l'artefact
|
||||
# pendant que le LABEL courant ment (écrire `PASS` alors que 4 open items
|
||||
# subsistent = violation #6 « ne jamais forcer un PASS », le CŒUR même du livrable —
|
||||
# l'audit prétend propre un dossier avec 4 params réglementaires non confirmés) ;
|
||||
# ou la RÈGLE documentée dérive du codomaine réel du générateur (un label renommé
|
||||
# dans builder.py, la prose périmée). check_artifacts ne prouve QUE
|
||||
# audit_report==build (byte) ; aucune suite tests/ (FONCTIONS, pas la prose du
|
||||
# README) n'attrape ces deux dérives. On MIROITE la règle du générateur et on
|
||||
# exige : (a) HONNÊTETÉ interne — audit_report.verdict == verdict recalculé du
|
||||
# triplet (l'audit ne peut afficher un verdict incohérent avec ses propres
|
||||
# chiffres) ; (b) CROISÉ — MANIFEST.verdict == audit_report.verdict ; (c) le LABEL
|
||||
# « Verdict courant » du README == audit_report.verdict ; (d) la RÈGLE du README
|
||||
# énumère EXACTEMENT le codomaine {FAIL, PASS_WITH_OPEN_ITEMS, PASS} (un label
|
||||
# renommé côté code sans mise à jour de la règle est mordu). Un claim absent échoue
|
||||
# AUSSI (disparition = régression).
|
||||
# ============================================================================
|
||||
A5V_DOMAIN = {"FAIL", "PASS_WITH_OPEN_ITEMS", "PASS"}
|
||||
def _a5_verdict_from_totals(t):
|
||||
# miroir EXACT de qalib/builder.py : FAIL prime, puis open items, sinon PASS
|
||||
if t["fail"]:
|
||||
return "FAIL"
|
||||
if t["a_confirmer"]:
|
||||
return "PASS_WITH_OPEN_ITEMS"
|
||||
return "PASS"
|
||||
try:
|
||||
a5v_rep = load("qa/audit_5d/out/audit_report.json")
|
||||
a5v_verdict = a5v_rep["verdict"]
|
||||
a5v_tot = a5v_rep["totals"]
|
||||
a5v_man_verdict = load("qa/audit_5d/out/MANIFEST.json")["verdict"]
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Audit 5D · verdict — artefact illisible/inattendu : {e}"); a5v_verdict = None
|
||||
if a5v_verdict is not None:
|
||||
# (a) honnêteté interne : le verdict de l'artefact découle de ses propres totals
|
||||
a5v_exp = _a5_verdict_from_totals(a5v_tot)
|
||||
if a5v_verdict != a5v_exp:
|
||||
bad(f"Audit 5D · verdict — audit_report dit « {a5v_verdict} » MAIS ses totals "
|
||||
f"(fail={a5v_tot['fail']} · à_confirmer={a5v_tot['a_confirmer']}) imposent "
|
||||
f"« {a5v_exp} » (verdict FORCÉ, incohérent — viole #6)")
|
||||
else:
|
||||
good(f"Audit 5D · verdict — audit_report « {a5v_verdict} » == recalcul du triplet "
|
||||
f"(fail={a5v_tot['fail']} · à_confirmer={a5v_tot['a_confirmer']})")
|
||||
# (b) croisé artefacts : MANIFEST == rapport
|
||||
if a5v_man_verdict != a5v_verdict:
|
||||
bad(f"Audit 5D · verdict — MANIFEST dit « {a5v_man_verdict} » MAIS audit_report "
|
||||
f"dit « {a5v_verdict} » (artefacts désynchronisés)")
|
||||
else:
|
||||
good(f"Audit 5D · verdict — MANIFEST « {a5v_man_verdict} » == audit_report")
|
||||
a5v_rd = read_norm(A5D_RD)
|
||||
if a5v_rd is not None:
|
||||
# (c) LABEL « Verdict courant » du README == verdict de l'artefact
|
||||
m = re.search(r"## Verdict courant\s+`([A-Z_]+)`\s*—\s*\*\*\d+ PASS", a5v_rd)
|
||||
if m is None:
|
||||
bad(f"Audit 5D · {A5D_RD} — label « Verdict courant » INTROUVABLE "
|
||||
f"(attendu « {a5v_verdict} »)")
|
||||
elif m.group(1) == a5v_verdict:
|
||||
good(f"Audit 5D · {A5D_RD} — « Verdict courant » = « {m.group(1)} » == artefact")
|
||||
else:
|
||||
bad(f"Audit 5D · {A5D_RD} — « Verdict courant » = « {m.group(1)} » MAIS "
|
||||
f"artefact dit « {a5v_verdict} » → régénérer le README")
|
||||
# (d) la RÈGLE documentée énumère EXACTEMENT le codomaine du générateur
|
||||
seg = re.search(r"Verdict global\s*:\s*(.+?)(?: > | ## )", a5v_rd)
|
||||
if seg is None:
|
||||
bad(f"Audit 5D · {A5D_RD} — règle « Verdict global : … » INTROUVABLE "
|
||||
f"(codomaine {sorted(A5V_DOMAIN)} non vérifiable)")
|
||||
else:
|
||||
labels = set(re.findall(r"`([A-Z_]+)`", seg.group(1)))
|
||||
if labels == A5V_DOMAIN:
|
||||
good(f"Audit 5D · {A5D_RD} — règle « Verdict global » énumère le codomaine "
|
||||
f"{sorted(A5V_DOMAIN)} == générateur")
|
||||
else:
|
||||
miss = sorted(A5V_DOMAIN - labels); extra = sorted(labels - A5V_DOMAIN)
|
||||
bad(f"Audit 5D · {A5D_RD} — règle « Verdict global » énumère {sorted(labels)} "
|
||||
f"≠ codomaine générateur {sorted(A5V_DOMAIN)} "
|
||||
f"(manquants={miss or '—'} en_trop={extra or '—'}) → aligner la règle")
|
||||
|
||||
# ============================================================================
|
||||
# Audit 5D (2e surface du MÊME README) · ÉNUMÉRATION des open items du « Verdict
|
||||
# courant » (control → dimension → propriétaire) — même classe de « vert trompeur ».
|
||||
|
||||
Reference in New Issue
Block a user