[DTP-Worker 20260801_223450] Sprint 8 · buffer · SEO : gate d'IDENTITÉ du COMPTE d'INVARIANTS CI — le compte de contrôles de validation (annoncé « ## Garanties (N invariants) » du README + toutes ses mentions + les 5 self-reports du seo_gen.py : docstrings/aides/succès CLI) recomputé du SEUL registre-source = le LEDGER numéroté « # N · » de la fonction _validate (analogue du registre # Invariant N de Chat OTOIA · source d'autorité NON-artefact, invisible à check_artifacts). Exige : (a) ledger CONTIGU 1..N (comparaison LISTE triée ⇒ mord lacune ET doublon) ; (b) toute mention « N invariants » du .py == |ledger| ; (c) compte annoncé README == |ledger| ; (d) toute mention README == |ledger|. Prose du README RÉSUME (11 puces ≠ énumération 1:1) ⇒ gate du COMPTE, pas set-diff. 5 morsures vérifiées (2 README · 3 source ; doublon ledger attrapé là où l'approche set aurait raté), 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1550,6 +1550,101 @@ if seo_exp is not None:
|
||||
bad(f"SEO · {path_f} — dit {'/'.join(m.groups())} MAIS MANIFEST dit "
|
||||
f"{'/'.join(seo_exp)} ({seo_src}) → régénérer le doc")
|
||||
|
||||
# ============================================================================
|
||||
# SEO · IDENTITÉ du COMPTE d'INVARIANTS CI — le README annonce « ## Garanties
|
||||
# (N invariants) » et le générateur se SELF-REPORTE (« schéma + N invariants »)
|
||||
# ----------------------------------------------------------------------------
|
||||
# CLASSE DISTINCTE du bloc SEO ci-dessus (qui gate des COMPTES d'ARTEFACT —
|
||||
# mots-clés total + par langue, recomptés de MANIFEST.counts). Ici la donnée
|
||||
# gardée est le COMPTE D'INVARIANTS de validation, 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 la fonction `_validate` du générateur :
|
||||
# les marqueurs `# N ·` (1 · 2 · … · 15) qui ouvrent chaque contrôle appliqué —
|
||||
# exactement l'analogue du registre `# Invariant N` de Chat OTOIA. Ce compte est
|
||||
# transcrit À LA MAIN à SEPT endroits qu'AUCUN gate ne couvrait : le titre de
|
||||
# section du README (« ## Garanties (N invariants) ») + son bloc de commande
|
||||
# (« schéma de sortie + N invariants »), plus CINQ chaînes du .py lui-même
|
||||
# (docstring module, docstring `_validate`, aide `build`, message de succès CLI
|
||||
# « N invariants verts. », aide du sous-parseur `validate`). 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, jamais la prose) ni le
|
||||
# byte-gate d'artefact (aveugle au .py source) n'attrapent. On RECOMPUTE ici le
|
||||
# ledger depuis le SEUL registre-source (les `# N ·` de `_validate`) et on exige :
|
||||
# (a) le ledger est CONTIGU 1..N (lacune/doublon dans les marqueurs = défaut réel
|
||||
# du registre) ; (b) TOUTE mention « N invariants » du .py (self-reports) == |ledger|
|
||||
# (un seul chiffre désynchronisé mordu) ; (c) le compte annoncé du README == |ledger| ;
|
||||
# (d) TOUTE mention « N invariants » du README == |ledger|. La prose du README
|
||||
# RÉSUME les contrôles (≠ énumération numérotée 1:1) : on gate le COMPTE partout
|
||||
# où il apparaît, pas un set-diff de la liste. Section/compte absent = échec (la
|
||||
# disparition d'un chiffre est une régression de traçabilité · #6).
|
||||
# ============================================================================
|
||||
SEO_GEN = f"{D}/seo/seo_gen.py"
|
||||
try:
|
||||
seo_src = open(SEO_GEN, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"SEO invariants · générateur {SEO_GEN} illisible : {e}"); seo_src = None
|
||||
if seo_src is not None:
|
||||
# (a) registre-source = marqueurs `# N ·` du LEDGER de `_validate` (autorité).
|
||||
_mfn = re.search(r"def _validate\b", seo_src)
|
||||
if _mfn is None:
|
||||
bad(f"SEO invariants · fonction `_validate` INTROUVABLE dans {SEO_GEN} "
|
||||
"(registre-source du ledger absent → impossible de re-dériver)")
|
||||
else:
|
||||
_bs = _mfn.end()
|
||||
_mn = re.search(r"\ndef ", seo_src[_bs:])
|
||||
_body = seo_src[_bs: _bs + _mn.start()] if _mn else seo_src[_bs:]
|
||||
seo_led = [int(n) for n in re.findall(r"#\s*(\d+)\s*·", _body)]
|
||||
if not seo_led:
|
||||
bad(f"SEO invariants · aucun marqueur `# N ·` dans `_validate` de {SEO_GEN} "
|
||||
"(ledger vide → impossible de re-dériver)")
|
||||
else:
|
||||
seo_ninv = len(seo_led)
|
||||
if sorted(seo_led) != list(range(1, seo_ninv + 1)):
|
||||
bad(f"SEO invariants · ledger `_validate` NON contigu : {seo_led} "
|
||||
f"(attendu 1..{seo_ninv}) — lacune/doublon dans les marqueurs `# N ·`")
|
||||
else:
|
||||
good(f"SEO invariants · ledger `_validate` contigu 1..{seo_ninv} "
|
||||
f"({seo_ninv} marqueurs `# N ·`)")
|
||||
# (b) self-reports du .py « N invariants » (docstrings + aides + succès CLI).
|
||||
seo_py_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", seo_src)})
|
||||
if not seo_py_rep:
|
||||
bad(f"SEO invariants · aucune mention « N invariants » dans {SEO_GEN} "
|
||||
f"(self-report absent → attendu {seo_ninv})")
|
||||
elif seo_py_rep == [seo_ninv]:
|
||||
good(f"SEO invariants · {SEO_GEN} — self-report(s) {seo_py_rep} "
|
||||
f"== ledger ({seo_ninv})")
|
||||
else:
|
||||
bad(f"SEO invariants · {SEO_GEN} — self-report(s) « N invariants » "
|
||||
f"{seo_py_rep} MAIS ledger en compte {seo_ninv} "
|
||||
"(une chaîne du générateur désynchronisée du registre)")
|
||||
# README : titre de section + toute mention « N invariants ».
|
||||
try:
|
||||
seo_rd_raw = open(SEO_RD, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"SEO invariants · {SEO_RD} illisible : {e}"); seo_rd_raw = None
|
||||
if seo_rd_raw is not None:
|
||||
_mh = re.search(r"##\s+Garanties\s+\((\d+)\s+invariants\)", seo_rd_raw)
|
||||
if _mh is None:
|
||||
bad(f"SEO invariants · {SEO_RD} — section « ## Garanties (N invariants) » "
|
||||
f"INTROUVABLE (attendu {seo_ninv})")
|
||||
else:
|
||||
# (c) compte annoncé.
|
||||
if int(_mh.group(1)) == seo_ninv:
|
||||
good(f"SEO invariants · {SEO_RD} — annonce ({_mh.group(1)}) "
|
||||
f"== ledger ({seo_ninv})")
|
||||
else:
|
||||
bad(f"SEO invariants · {SEO_RD} — annonce ({_mh.group(1)}) MAIS "
|
||||
f"ledger en compte {seo_ninv} → régénérer le README")
|
||||
# (d) toute mention « N invariants » du README == ledger.
|
||||
seo_rd_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", seo_rd_raw)})
|
||||
if seo_rd_rep == [seo_ninv]:
|
||||
good(f"SEO invariants · {SEO_RD} — mention(s) {seo_rd_rep} "
|
||||
f"== ledger (aucune dérive)")
|
||||
else:
|
||||
bad(f"SEO invariants · {SEO_RD} — mention(s) « N invariants » "
|
||||
f"{seo_rd_rep} MAIS ledger en compte {seo_ninv} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# CRM · pipeline vente (états / transitions) — même classe de « vert trompeur »,
|
||||
# transcrit À LA MAIN dans DEUX README de module.
|
||||
|
||||
Reference in New Issue
Block a user