[DTP-Worker 20260802_013501] Sprint 8 · buffer · CRM Dossier Vente : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (5ᵉ instance du ledger)
5ᵉ instance de la classe « ledger d'invariants » (après Chat OTOIA · SEO · Fiscal e-CF · Legal CONFOTUR). Le compte « 12 invariants » de crm/dossier_vente/doctype_dossier_vente_gen.py était transcrit à la main à 7 endroits (4 chaînes du .py + 3 du README : commande, titre « ## Les 12 invariants », prose de score), aucun gaté — invisibles à check_artifacts (le .py n'est pas un out/*.json) et aux suites tests/ (qui testent des fonctions, pas la prose). Ajouter un contrôle # 13 · sans toucher les 7 chaînes ⇒ CI applique 13 contrôles pendant que docs+self-reports disent 12 en silence (vert trompeur · #6). Ledger PROPRE 1..12 (≈ CONFOTUR) : schéma compté à part (« schéma + N invariants »), pas de schéma-#1 non marqué, pas de split a/b ; détection de doublon nu conservée en filet. Le gate RECOMPUTE le ledger depuis le seul registre-source (# N · de _validate) et exige (a) ledger contigu 1..N ; (b) toute mention « N invariants » du .py == |ledger| ; (c) annonce du titre README == |ledger| ; (d) toute mention README == |ledger| ; (e) énumération numérotée de la section contiguë 1..N. 5 checks verts sur l'arbre propre. 7 morsures adversariales (M1 self-report .py · M2 titre README · M3 prose README · M4 ajout # 13 · = ledger 13 vs prose 12, mord .py+README+énumération = autorité au CODE · M5 gap # 7 · · M6 doublon nu # 5 · · M7 énumération 7→8). 6 gates re-verts exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7676,6 +7676,122 @@ if conf_dt_wd is not None:
|
||||
good(f"Confotur champs dérivés · (d) README `estado` DÉCLARE dériver des "
|
||||
f"états confotur_* du workflow vente")
|
||||
|
||||
# ============================================================================
|
||||
# CRM · Dossier Vente · IDENTITÉ du COMPTE D'INVARIANTS de `_validate` — 5ᵉ
|
||||
# instance de la classe « ledger d'invariants » (après Chat OTOIA · SEO · Fiscal
|
||||
# e-CF · Legal CONFOTUR), appliquée au générateur du DocType porteur.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le générateur doctype_dossier_vente_gen.py applique un COMPTE d'invariants de
|
||||
# cross-cohérence workflow ↔ DocType 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..12) qui ouvrent chaque
|
||||
# contrôle appliqué. Le nombre « 12 » est transcrit À LA MAIN à SEPT endroits
|
||||
# qu'AUCUN gate ne couvrait : QUATRE chaînes du .py (docstring module, docstring
|
||||
# `_validate`, message de succès CLI « … schéma + 12 invariants verts. », aide du
|
||||
# sous-parseur `validate`) + TROIS du README (bloc de commande « schéma + 12
|
||||
# invariants », titre de section « ## Les 12 invariants », prose de score « 12
|
||||
# invariants de cross-cohérence »). Ajouter un contrôle `# 13 ·` à `_validate` sans
|
||||
# toucher les chaînes ⇒ le README ET les self-reports du .py mentent en silence
|
||||
# pendant que la CI applique 13 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 Fiscal : ledger PROPRE 1..N (le schéma est compté À PART —
|
||||
# phrasé « schéma + N invariants » — donc PAS de schéma-#1 non marqué, pas de split
|
||||
# a/b) ; on garde la détection de doublon nu comme filet. 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| ; (c) le compte annoncé du
|
||||
# titre README « ## Les N invariants » == |ledger| ; (d) TOUTE mention « N
|
||||
# invariants » du README == |ledger| ; (e) l'ÉNUMÉRATION numérotée de la section
|
||||
# (« 1. … 2. … N. ») elle-même contiguë 1..N (item ni sauté ni dupliqué). Mention
|
||||
# ou registre absent = échec (disparition d'un chiffre = régression de traçabilité
|
||||
# #6).
|
||||
# ============================================================================
|
||||
DVV_GEN = f"{D}/crm/dossier_vente/doctype_dossier_vente_gen.py"
|
||||
DVV_RD = f"{D}/crm/dossier_vente/README.md"
|
||||
try:
|
||||
dvv_src = open(DVV_GEN, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Dossier Vente invariants · générateur {DVV_GEN} illisible : {e}"); dvv_src = None
|
||||
if dvv_src is not None:
|
||||
_mfn = re.search(r"def _validate\b", dvv_src)
|
||||
if _mfn is None:
|
||||
bad(f"Dossier Vente invariants · fonction `_validate` INTROUVABLE dans {DVV_GEN} "
|
||||
"(registre-source du ledger absent → impossible de re-dériver)")
|
||||
else:
|
||||
_bs = _mfn.end()
|
||||
_mn = re.search(r"\ndef ", dvv_src[_bs:])
|
||||
_body = dvv_src[_bs: _bs + _mn.start()] if _mn else dvv_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"Dossier Vente invariants · aucun marqueur `# N ·` dans `_validate` de "
|
||||
f"{DVV_GEN} (ledger vide → impossible de re-dériver)")
|
||||
elif _dup_bare:
|
||||
bad(f"Dossier Vente invariants · marqueur(s) `# N ·` dupliqué(s) sans suffixe "
|
||||
f"a/b dans `_validate` : {_dup_bare} — vrai doublon de registre")
|
||||
else:
|
||||
dvv_ninv = len(_top)
|
||||
if _top != list(range(1, dvv_ninv + 1)):
|
||||
bad(f"Dossier Vente invariants · ledger `_validate` NON contigu : {_top} "
|
||||
f"(attendu 1..{dvv_ninv}) — lacune/doublon dans les marqueurs `# N ·`")
|
||||
else:
|
||||
good(f"Dossier Vente invariants · ledger `_validate` contigu 1..{dvv_ninv} "
|
||||
f"({dvv_ninv} marqueurs `# N ·` · schéma compté à part)")
|
||||
# (b) self-reports du .py « N invariants ».
|
||||
dvv_py_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", dvv_src)})
|
||||
if not dvv_py_rep:
|
||||
bad(f"Dossier Vente invariants · aucune mention « N invariants » dans {DVV_GEN} "
|
||||
f"(self-report absent → attendu {dvv_ninv})")
|
||||
elif dvv_py_rep == [dvv_ninv]:
|
||||
good(f"Dossier Vente invariants · {DVV_GEN} — self-report(s) {dvv_py_rep} "
|
||||
f"== ledger ({dvv_ninv})")
|
||||
else:
|
||||
bad(f"Dossier Vente invariants · {DVV_GEN} — self-report(s) « N invariants » "
|
||||
f"{dvv_py_rep} MAIS ledger en compte {dvv_ninv} "
|
||||
"(une chaîne du générateur désynchronisée du registre)")
|
||||
# README : titre de section + toute mention + énumération numérotée.
|
||||
try:
|
||||
dvv_rd_raw = open(DVV_RD, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Dossier Vente invariants · {DVV_RD} illisible : {e}"); dvv_rd_raw = None
|
||||
if dvv_rd_raw is not None:
|
||||
_mh = re.search(r"##\s+Les\s+(\d+)\s+invariants", dvv_rd_raw)
|
||||
if _mh is None:
|
||||
bad(f"Dossier Vente invariants · {DVV_RD} — section « ## Les N invariants » "
|
||||
f"INTROUVABLE (attendu {dvv_ninv})")
|
||||
else:
|
||||
# (c) compte annoncé du titre.
|
||||
if int(_mh.group(1)) == dvv_ninv:
|
||||
good(f"Dossier Vente invariants · {DVV_RD} — annonce ({_mh.group(1)}) "
|
||||
f"== ledger ({dvv_ninv})")
|
||||
else:
|
||||
bad(f"Dossier Vente invariants · {DVV_RD} — annonce ({_mh.group(1)}) MAIS "
|
||||
f"ledger en compte {dvv_ninv} → régénérer le README")
|
||||
# (d) toute mention « N invariants » du README == ledger.
|
||||
dvv_rd_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", dvv_rd_raw)})
|
||||
if dvv_rd_rep == [dvv_ninv]:
|
||||
good(f"Dossier Vente invariants · {DVV_RD} — mention(s) {dvv_rd_rep} "
|
||||
f"== ledger (aucune dérive)")
|
||||
else:
|
||||
bad(f"Dossier Vente invariants · {DVV_RD} — mention(s) « N invariants » "
|
||||
f"{dvv_rd_rep} MAIS ledger en compte {dvv_ninv} → régénérer le README")
|
||||
# (e) énumération numérotée de la section == contiguë 1..N.
|
||||
_msec = re.search(r"##\s+Les\s+\d+\s+invariants.*?(?=\n##\s|\Z)",
|
||||
dvv_rd_raw, re.S)
|
||||
dvv_enum = ([int(x) for x in
|
||||
re.findall(r"(?<![\d.])(\d+)\.\s", _msec.group(0))]
|
||||
if _msec else [])
|
||||
if dvv_enum == list(range(1, dvv_ninv + 1)):
|
||||
good(f"Dossier Vente invariants · {DVV_RD} — énumération prose "
|
||||
f"contiguë 1..{dvv_ninv} == ledger")
|
||||
else:
|
||||
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")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user