[DTP-Worker 20260802_093532] Sprint 8 · buffer · Ledger d'invariants 9ᵉ instance : gate d'IDENTITÉ du COMPTE de _validate_bundle de crm/workflow_vente (dialecte # Invariant N ·, 3ᵉ oubli de la classe « close »)
Le générateur workflow_vente_gen.py (Livrable Sprint 4 l.50) porte un ledger de 9 invariants métier de graphe dans `_validate_bundle` (marqueurs `# Invariant N ·`, phrasé « schéma + 9 invariants »), avec le nombre « 9 » recopié à la main à 4 surfaces vives (gen l.223 + README l.78/114/125), aucune gatée. Prouvé ungate avant ajout (README 9→10 laissait check_readme_claims exit 0). Gate ajouté (a)(b)(d) sur le modèle Mobile : recompute le ledger depuis le SEUL registre-source `# Invariant N ·`, exige contiguïté 1..N + toute mention « N invariants » du .py et du README == |ledger|. Pas de gate (e) enum : la liste « ## Invariants vérifiés par le CLI » compte le schéma en item 1 et fusionne les invariants 8&9 → n'est pas le ledger (faux positif fragile évité). « 25 tests » exclu nativement par l'ancre « invariants » (aucun faux positif). 3 checks verts arbre propre · 7 morsures adversariales (dont M3 SILENT-GREEN `# Invariant 10 ·` ⇒ autorité=CODE, M7 no-false-positive) · unittest 25 OK · suite CI complète re-verte. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8650,6 +8650,119 @@ if mbi_src is not None:
|
||||
bad(f"Mobile invariants · {MBI_RD} — mention(s) « N invariants » "
|
||||
f"{mbi_rd_rep} MAIS ledger en compte {mbi_ninv} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# CRM · workflow_vente · IDENTITÉ du COMPTE D'INVARIANTS de `_validate_bundle` —
|
||||
# 9ᵉ instance de la classe « ledger d'invariants » (après Chat OTOIA · SEO ·
|
||||
# Fiscal e-CF · Legal CONFOTUR · CRM Dossier Vente · QA Audit 5D · CRM
|
||||
# Commissions · Mobile), appliquée au générateur du workflow de vente Frappe
|
||||
# (roadmap Sprint 4 l.50 « lead → visite → devis → réservation → contrat →
|
||||
# CONFOTUR »).
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le générateur workflow_vente_gen.py applique un COMPTE d'invariants métier de
|
||||
# graphe (unicité des états · transitions vers états déclarés · monotonie
|
||||
# doc_status · unicité (état,action) · atteignabilité depuis Lead · terminal de
|
||||
# succès · séparation des pouvoirs #6 · maîtres = états/actions · cohérence des
|
||||
# comptes du manifeste) 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_bundle` : les marqueurs `# Invariant N ·` (1..9) qui
|
||||
# ouvrent chaque contrôle. Le nombre « 9 » est transcrit À LA MAIN à QUATRE
|
||||
# endroits qu'AUCUN gate ne couvrait : UNE chaîne du .py (message de succès CLI
|
||||
# « … schéma + 9 invariants verts. ») + TROIS du README (bloc de commande
|
||||
# « schéma + 9 invariants métier », prose de vérification « 9 invariants de
|
||||
# graphe », prose d'auto-score « 9 invariants de graphe »). Ajouter un contrôle
|
||||
# `# Invariant 10 ·` à `_validate_bundle` sans toucher ces chaînes ⇒ le README ET
|
||||
# le self-report CLI mentent en silence pendant que la CI applique 10 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.
|
||||
# Prouvé ungate par mutation avant ajout (mémoire verify-uncovered-before-gating) :
|
||||
# « 9 invariants » → « 10 » dans le README laissait check_readme_claims exit 0.
|
||||
# PARTICULARITÉS vs les 8 blocs précédents :
|
||||
# · le REGISTRE-source utilise le style `# Invariant N ·` + la fonction
|
||||
# `_validate_bundle` (comme Mobile — les blocs ancrés sur le style nu `# N ·` /
|
||||
# `def _validate\b` ne le voient PAS) ;
|
||||
# · 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 le filet doublon nu ;
|
||||
# · le module porte un SECOND nombre distinct — « 25 tests » (la suite unittest,
|
||||
# déjà gatée par ailleurs) présent dans le README : l'ancre sur le mot
|
||||
# « invariants » l'exclut nativement (« 25 tests » ≠ mot « invariants ») ⇒ aucun
|
||||
# faux positif ;
|
||||
# · ce README N'A NI titre « ## Les N invariants » (son titre « ## Invariants
|
||||
# vérifiés par le CLI » ne porte AUCUN chiffre) NI énumération 1:1 avec le
|
||||
# ledger — sa liste « Invariants vérifiés par le CLI » compte le SCHÉMA en item 1
|
||||
# puis FUSIONNE les invariants 8 & 9 dans son item 9 (« Maîtres … ; comptes du
|
||||
# manifeste cohérents ») : elle N'EST PAS le ledger et la gater comme contiguë
|
||||
# == ledger serait un faux positif fragile (présentation éditoriale, #6). On
|
||||
# gate donc UNIQUEMENT (a)(b)(d), comme Audit 5D.
|
||||
# On RECOMPUTE le ledger depuis le SEUL registre-source (les `# Invariant N ·` de
|
||||
# `_validate_bundle`) 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).
|
||||
# ============================================================================
|
||||
WVI_GEN = f"{D}/crm/workflow_vente/workflow_vente_gen.py"
|
||||
WVI_RD = f"{D}/crm/workflow_vente/README.md"
|
||||
try:
|
||||
wvi_src = open(WVI_GEN, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"workflow_vente invariants · générateur {WVI_GEN} illisible : {e}"); wvi_src = None
|
||||
if wvi_src is not None:
|
||||
_mfn = re.search(r"def _validate_bundle\b", wvi_src)
|
||||
if _mfn is None:
|
||||
bad(f"workflow_vente invariants · fonction `_validate_bundle` INTROUVABLE dans "
|
||||
f"{WVI_GEN} (registre-source du ledger absent → impossible de re-dériver)")
|
||||
else:
|
||||
_bs = _mfn.end()
|
||||
_mn = re.search(r"\ndef ", wvi_src[_bs:])
|
||||
_body = wvi_src[_bs: _bs + _mn.start()] if _mn else wvi_src[_bs:]
|
||||
_pairs = re.findall(r"#\s*Invariant\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"workflow_vente invariants · aucun marqueur `# Invariant N ·` dans "
|
||||
f"`_validate_bundle` de {WVI_GEN} (ledger vide → impossible de re-dériver)")
|
||||
elif _dup_bare:
|
||||
bad(f"workflow_vente invariants · marqueur(s) `# Invariant N ·` dupliqué(s) sans "
|
||||
f"suffixe a/b dans `_validate_bundle` : {_dup_bare} — vrai doublon de registre")
|
||||
else:
|
||||
wvi_ninv = len(_top)
|
||||
if _top != list(range(1, wvi_ninv + 1)):
|
||||
bad(f"workflow_vente invariants · ledger `_validate_bundle` NON contigu : {_top} "
|
||||
f"(attendu 1..{wvi_ninv}) — lacune/doublon dans les marqueurs `# Invariant N ·`")
|
||||
else:
|
||||
good(f"workflow_vente invariants · ledger `_validate_bundle` contigu 1..{wvi_ninv} "
|
||||
f"({wvi_ninv} marqueurs `# Invariant N ·` · schéma compté à part)")
|
||||
# (b) self-report(s) du .py « N invariants » (le distinct « N tests » est
|
||||
# exclu nativement par l'ancre sur le mot « invariants »).
|
||||
wvi_py_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", wvi_src)})
|
||||
if not wvi_py_rep:
|
||||
bad(f"workflow_vente invariants · aucune mention « N invariants » dans {WVI_GEN} "
|
||||
f"(self-report absent → attendu {wvi_ninv})")
|
||||
elif wvi_py_rep == [wvi_ninv]:
|
||||
good(f"workflow_vente invariants · {WVI_GEN} — self-report(s) {wvi_py_rep} "
|
||||
f"== ledger ({wvi_ninv})")
|
||||
else:
|
||||
bad(f"workflow_vente invariants · {WVI_GEN} — self-report(s) « N invariants » "
|
||||
f"{wvi_py_rep} MAIS ledger en compte {wvi_ninv} "
|
||||
"(une chaîne du générateur désynchronisée du registre)")
|
||||
# (d) toute mention « N invariants » du README == ledger.
|
||||
try:
|
||||
wvi_rd_raw = open(WVI_RD, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"workflow_vente invariants · {WVI_RD} illisible : {e}"); wvi_rd_raw = None
|
||||
if wvi_rd_raw is not None:
|
||||
wvi_rd_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", wvi_rd_raw)})
|
||||
if not wvi_rd_rep:
|
||||
bad(f"workflow_vente invariants · aucune mention « N invariants » dans {WVI_RD} "
|
||||
f"(self-report README absent → attendu {wvi_ninv})")
|
||||
elif wvi_rd_rep == [wvi_ninv]:
|
||||
good(f"workflow_vente invariants · {WVI_RD} — mention(s) {wvi_rd_rep} "
|
||||
f"== ledger (aucune dérive)")
|
||||
else:
|
||||
bad(f"workflow_vente invariants · {WVI_RD} — mention(s) « N invariants » "
|
||||
f"{wvi_rd_rep} MAIS ledger en compte {wvi_ninv} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# DevOps deploy_runbook + QA acceptance · IDENTITÉ du COMPTE d'INVARIANTS de
|
||||
# `validate_bundle` — DEUXIÈME famille de « ledger d'invariants », distincte des
|
||||
|
||||
Reference in New Issue
Block a user