[DTP-Worker 20260802_020502] Sprint 8 · buffer · QA Audit 5D : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (6ᵉ instance du ledger)
RECOMPUTE le ledger d'invariants depuis le SEUL registre-source (les `# N ·` de `_validate` de audit_5d_gen.py) et gate les 6 transcriptions manuelles de « 15 invariants » (4 dans le .py, 2 dans le README), toutes invisibles à check_artifacts. Ledger PROPRE 1..15 (schéma compté à part). Exige : (a) ledger contigu 1..N (lacune/doublon nu mordu) ; (b) toute mention « N invariants » du .py == |ledger| ; (d) toute mention README == |ledger|. Le distinct « 17 contrôles » est exclu nativement par l'ancre sur le mot « invariants » (aucun faux positif). 6 morsures adversariales dont M3 (ajout `# 16 ·` mord .py+README, preuve que l'autorité est le CODE) et M6 (« 17 contrôles » reste invisible). 6 gates re-verts exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7792,6 +7792,104 @@ if dvv_src is not None:
|
||||
bad(f"Dossier Vente invariants · {DVV_RD} — énumération prose {dvv_enum} "
|
||||
f"≠ 1..{dvv_ninv} (item sauté/dupliqué) → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# QA · Audit 5D · IDENTITÉ du COMPTE D'INVARIANTS de `_validate` — 6ᵉ instance
|
||||
# de la classe « ledger d'invariants » (après Chat OTOIA · SEO · Fiscal e-CF ·
|
||||
# Legal CONFOTUR · CRM Dossier Vente), appliquée au générateur de l'audit QA.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le générateur audit_5d_gen.py applique un COMPTE d'invariants de cohérence
|
||||
# audit/contrôles dont la source d'autorité n'est PAS un artefact JSON (invisible
|
||||
# à check_artifacts qui byte-gate les out/*.json) mais le LEDGER NUMÉROTÉ de
|
||||
# `_validate` : les marqueurs `# N ·` (1..15) qui ouvrent chaque contrôle. Le
|
||||
# nombre « 15 » est transcrit À LA MAIN à SIX endroits qu'AUCUN gate ne couvrait :
|
||||
# QUATRE chaînes du .py (docstring sous-commande « valide schéma + 15 invariants »,
|
||||
# docstring `_validate`, message de succès CLI « … 15 invariants — verdict … »,
|
||||
# aide du sous-parseur `validate`) + DEUX du README (bloc de commande « schéma +
|
||||
# 15 invariants », prose d'architecture « CLI build/validate + **15 invariants** »).
|
||||
# Ajouter un contrôle `# 16 ·` à `_validate` sans toucher les chaînes ⇒ le README
|
||||
# ET les self-reports du .py mentent en silence pendant que la CI applique 16
|
||||
# contrôles — « vert trompeur » (#6) que ni les suites tests/ (FONCTIONS de
|
||||
# validation, jamais la prose) ni le byte-gate d'artefact (aveugle au .py source)
|
||||
# n'attrapent.
|
||||
# PARTICULARITÉ vs Dossier Vente : ledger PROPRE 1..N compté À PART du schéma
|
||||
# (phrasé « schéma + N invariants » — pas de schéma-#1 non marqué, pas de split
|
||||
# a/b ; on garde la détection de doublon nu comme filet). MAIS le module porte
|
||||
# un SECOND nombre distinct — « 17 contrôles » (le compte de fonctions-contrôle,
|
||||
# déjà gaté par ailleurs) — présent dans le .py ET le README : un `\d+ invariants`
|
||||
# ancré sur le mot « invariants » l'exclut nativement (« 17 contrôles » ≠ mot
|
||||
# « invariants »), donc AUCUN faux positif sur l'arbre propre. Contrairement à
|
||||
# Dossier Vente/Fiscal, ce README n'a NI titre « ## Les N invariants » NI
|
||||
# énumération numérotée du ledger → on ne gate que (a)(b)(d). On RECOMPUTE le
|
||||
# ledger depuis le SEUL registre-source (les `# N ·` de `_validate`) et on exige :
|
||||
# (a) ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre, mordu) ;
|
||||
# (b) TOUTE mention « N invariants » du .py == |ledger| ; (d) TOUTE mention « N
|
||||
# invariants » du README == |ledger|. Mention ou registre absent = échec
|
||||
# (disparition d'un chiffre = régression de traçabilité #6).
|
||||
# ============================================================================
|
||||
A5I_GEN = f"{D}/qa/audit_5d/audit_5d_gen.py"
|
||||
A5I_RD = f"{D}/qa/audit_5d/README.md"
|
||||
try:
|
||||
a5i_src = open(A5I_GEN, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Audit 5D invariants · générateur {A5I_GEN} illisible : {e}"); a5i_src = None
|
||||
if a5i_src is not None:
|
||||
_mfn = re.search(r"def _validate\b", a5i_src)
|
||||
if _mfn is None:
|
||||
bad(f"Audit 5D invariants · fonction `_validate` INTROUVABLE dans {A5I_GEN} "
|
||||
"(registre-source du ledger absent → impossible de re-dériver)")
|
||||
else:
|
||||
_bs = _mfn.end()
|
||||
_mn = re.search(r"\ndef ", a5i_src[_bs:])
|
||||
_body = a5i_src[_bs: _bs + _mn.start()] if _mn else a5i_src[_bs:]
|
||||
_pairs = re.findall(r"#\s*(\d+)([a-z]?)\s*·", _body)
|
||||
_dup_bare = sorted({n for n in {p[0] for p in _pairs}
|
||||
if [s for num, s in _pairs if num == n and s == ""]
|
||||
and sum(1 for num, _ in _pairs if num == n) > 1})
|
||||
_top = sorted({int(n) for n, _ in _pairs})
|
||||
if not _top:
|
||||
bad(f"Audit 5D invariants · aucun marqueur `# N ·` dans `_validate` de "
|
||||
f"{A5I_GEN} (ledger vide → impossible de re-dériver)")
|
||||
elif _dup_bare:
|
||||
bad(f"Audit 5D invariants · marqueur(s) `# N ·` dupliqué(s) sans suffixe "
|
||||
f"a/b dans `_validate` : {_dup_bare} — vrai doublon de registre")
|
||||
else:
|
||||
a5i_ninv = len(_top)
|
||||
if _top != list(range(1, a5i_ninv + 1)):
|
||||
bad(f"Audit 5D invariants · ledger `_validate` NON contigu : {_top} "
|
||||
f"(attendu 1..{a5i_ninv}) — lacune/doublon dans les marqueurs `# N ·`")
|
||||
else:
|
||||
good(f"Audit 5D invariants · ledger `_validate` contigu 1..{a5i_ninv} "
|
||||
f"({a5i_ninv} marqueurs `# N ·` · schéma compté à part)")
|
||||
# (b) self-reports du .py « N invariants » (le distinct « N contrôles »
|
||||
# est exclu nativement par l'ancre sur le mot « invariants »).
|
||||
a5i_py_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", a5i_src)})
|
||||
if not a5i_py_rep:
|
||||
bad(f"Audit 5D invariants · aucune mention « N invariants » dans {A5I_GEN} "
|
||||
f"(self-report absent → attendu {a5i_ninv})")
|
||||
elif a5i_py_rep == [a5i_ninv]:
|
||||
good(f"Audit 5D invariants · {A5I_GEN} — self-report(s) {a5i_py_rep} "
|
||||
f"== ledger ({a5i_ninv})")
|
||||
else:
|
||||
bad(f"Audit 5D invariants · {A5I_GEN} — self-report(s) « N invariants » "
|
||||
f"{a5i_py_rep} MAIS ledger en compte {a5i_ninv} "
|
||||
"(une chaîne du générateur désynchronisée du registre)")
|
||||
# (d) toute mention « N invariants » du README == ledger.
|
||||
try:
|
||||
a5i_rd_raw = open(A5I_RD, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Audit 5D invariants · {A5I_RD} illisible : {e}"); a5i_rd_raw = None
|
||||
if a5i_rd_raw is not None:
|
||||
a5i_rd_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", a5i_rd_raw)})
|
||||
if not a5i_rd_rep:
|
||||
bad(f"Audit 5D invariants · aucune mention « N invariants » dans {A5I_RD} "
|
||||
f"(self-report README absent → attendu {a5i_ninv})")
|
||||
elif a5i_rd_rep == [a5i_ninv]:
|
||||
good(f"Audit 5D invariants · {A5I_RD} — mention(s) {a5i_rd_rep} "
|
||||
f"== ledger (aucune dérive)")
|
||||
else:
|
||||
bad(f"Audit 5D invariants · {A5I_RD} — mention(s) « N invariants » "
|
||||
f"{a5i_rd_rep} MAIS ledger en compte {a5i_ninv} → régénérer le README")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user