[DTP-Worker] Sprint 8 · buffer · Legal/CONFOTUR : IDENTITÉ des ENTITÉS porteuses du DocType CONFOTUR Application ANCRÉE sur CLAUDE.md §Entités — le champ Select entite_porteuse (le MENU DÉROULANT qui fixe QUELLE entité juridique porte chaque dossier d'incitation touristique) offre pour options les 7 entités canoniques du mandat (WAF·WA SRL·AC Arias Cuevas·Consortium ECR DR·Helios RD·Ploutos·9060 QC), DÉRIVÉES via options_source de confotur_spec.json[entites] et BYTE-GATÉES pour la REPRODUCTIBILITÉ par check_artifacts (l'artefact se reconstruit depuis le spec) mais JAMAIS ANCRÉES à CLAUDE.md §Entités, leur source faisant autorité. Les blocs CONFOTUR amont ne gatent que le COMPTE de champs/rôles et la cross-cohérence des PERMISSIONS, aveugles à l'IDENTITÉ de cette liste. Piège #6 : RENOMMER une entité dans CLAUDE.md §Entités (9060 QC→9061 QC) ou dans confotur_spec.json[entites] puis régénérer laisse le DocType offrir un menu qui CONTREDIT/omet une entité canonique pendant que la byte-gate reste VERTE (l'artefact se reconstruit fidèlement… depuis un spec dérivé) — « vert trompeur » de la MÊME classe que la marque SEO org (§Entités) et les tokens branding (#4). L'agent ERPNext Backend importerait un DocType dont le déroulant liste une entité FANTÔME ou en omet une (la restriction même que reqd pose). Aucune suite tests/ (FONCTIONS de génération, jamais l'ancre à CLAUDE.md) ne l'attrape. Gate ajouté (bloc « Legal · CONFOTUR — IDENTITÉ des ENTITÉS porteuses ») : les 7 entités re-dérivées des tokens en gras de la section « ## Entités » de CLAUDE.md, puis (a) options de l'artefact == CLAUDE.md ORDRE exact · (b) spec[entites] == CLAUDE.md l'INPUT ancré · (c) le spec DÉCLARE l'ancrage (entites_source cite CLAUDE.md + chaque entité) · (d) wiring intact options_source == "entites" · (e) README cite l'ancre CLAUDE.md + nomme chaque entité (nom complet OU 1er token · abréviation « AC » tolérée · #6). 7 morsures vérifiées (CLAUDE.md rename → 4 bites a/b/c/e simultanées, l'ancre est vive · artefact drop option · artefact reorder · spec entites renommé · spec source sans CLAUDE.md · spec options_source hardcoded · README retire une entité), restauré vert, 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4011,6 +4011,129 @@ if ci_lines is not None:
|
||||
bad("Job CI fiches · AUCUN token `*-tests` trouvé dans les fiches — la "
|
||||
"disparition de la colonne « Job CI » est elle-même une régression.")
|
||||
|
||||
# ============================================================================
|
||||
# Legal · CONFOTUR — IDENTITÉ des ENTITÉS PORTEUSES ancrée sur CLAUDE.md §Entités
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le champ Select `entite_porteuse` du DocType CONFOTUR Application — le menu
|
||||
# déroulant qui fixe QUELLE entité juridique porte chaque dossier d'incitation —
|
||||
# offre pour options les 7 entités canoniques du mandat (WAF · WA SRL · AC Arias
|
||||
# Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060 QC). Ces options sont
|
||||
# DÉRIVÉES via `options_source` de confotur_spec.json[entites] et byte-gatées pour
|
||||
# la REPRODUCTIBILITÉ par check_artifacts (l'artefact se reconstruit depuis le
|
||||
# spec) — mais JAMAIS ANCRÉES à CLAUDE.md §Entités, leur source faisant autorité.
|
||||
# Les blocs CONFOTUR amont gatent le COMPTE de champs/rôles et la cross-cohérence
|
||||
# des PERMISSIONS, aveugles à l'identité de cette liste d'entités.
|
||||
# Piège #6 : RENOMMER une entité dans CLAUDE.md §Entités (9060 QC→9061 QC) ou dans
|
||||
# confotur_spec.json[entites] puis régénérer laisse le DocType offrir un menu qui
|
||||
# CONTREDIT/omet une entité canonique du mandat pendant que check_artifacts reste
|
||||
# VERT (l'artefact se reconstruit… depuis un spec dérivé) — « vert trompeur » de
|
||||
# la MÊME classe que la marque SEO org (§Entités) et les tokens branding (#4).
|
||||
# Aucune suite tests/ (FONCTIONS de génération) ne l'attrape → l'agent ERPNext
|
||||
# Backend importerait un DocType dont le déroulant liste une entité fantôme ou en
|
||||
# omet une (la restriction même que le champ `reqd` pose).
|
||||
# GATE : (a) options de l'artefact == entités CLAUDE.md §Entités (ORDRE exact) ;
|
||||
# (b) spec[entites] == CLAUDE.md (l'INPUT ancré) ; (c) le spec DÉCLARE l'ancrage
|
||||
# (entites_source cite CLAUDE.md + chaque entité) ; (d) wiring intact
|
||||
# (options_source == "entites" · le déroulant DÉRIVE de la liste, pas d'un dur) ;
|
||||
# (e) README cite l'ancre CLAUDE.md + nomme chaque entité (nom complet OU 1er
|
||||
# token — l'abréviation « AC » de « AC Arias Cuevas » est un choix éditorial
|
||||
# toléré · #6). Un claim absent échoue AUSSI (traçabilité #6).
|
||||
# ============================================================================
|
||||
try:
|
||||
_cm_ce = open("CLAUDE.md", encoding="utf-8").read()
|
||||
_sec_ce = re.search(r"^## Entités\s*\n(.*?)(?=^## )", _cm_ce, re.S | re.M)
|
||||
if not _sec_ce:
|
||||
raise ValueError("section « ## Entités » introuvable")
|
||||
cm_ents = [_nfc(x.strip()) for x in re.findall(r"\*\*([^*]+?)\*\*", _sec_ce.group(1))]
|
||||
if not cm_ents:
|
||||
raise ValueError("aucune entité en gras dans §Entités")
|
||||
if len(cm_ents) != len(set(cm_ents)):
|
||||
raise ValueError(f"entités non uniques : {cm_ents}")
|
||||
except (OSError, ValueError) as e:
|
||||
bad(f"Confotur entités · CLAUDE.md §Entités illisible/inattendu : {e}"); cm_ents = None
|
||||
try:
|
||||
conf_spec_ce = load("legal/confotur/confotur_spec.json")
|
||||
conf_dt_ce = load("legal/confotur/out/doctype_confotur_application.json")
|
||||
ep_art = [f for f in conf_dt_ce.get("fields", []) if f.get("fieldname") == "entite_porteuse"]
|
||||
ep_spec = None
|
||||
for _grp in conf_spec_ce.get("field_groups", []):
|
||||
for _f in _grp.get("fields", []):
|
||||
if _f.get("fieldname") == "entite_porteuse":
|
||||
ep_spec = _f
|
||||
spec_ents = [_nfc(str(x).strip()) for x in conf_spec_ce.get("entites", [])]
|
||||
ents_src = _nfc(str(conf_spec_ce.get("entites_source", "")))
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Confotur entités · artefact/spec illisible/inattendu : {e}"); cm_ents = None
|
||||
if cm_ents is not None:
|
||||
# (a) ANCRAGE — options du Select de l'artefact == entités CLAUDE.md (ordre exact)
|
||||
if len(ep_art) != 1:
|
||||
bad(f"Confotur entités · champ `entite_porteuse` non-singleton dans l'artefact "
|
||||
f"({len(ep_art)}) → identité du déroulant indéterminable")
|
||||
else:
|
||||
art_opts = [_nfc(o.strip()) for o in str(ep_art[0].get("options", "")).split("\n") if o.strip()]
|
||||
if art_opts == cm_ents:
|
||||
good(f"Confotur entités · (a) options DocType `entite_porteuse` "
|
||||
f"({len(art_opts)}) == CLAUDE.md §Entités (ordre exact)")
|
||||
else:
|
||||
bad(f"Confotur entités · (a) options DocType {art_opts} ≠ CLAUDE.md §Entités "
|
||||
f"{cm_ents} → menu périmé (régénérer depuis le spec / aligner CLAUDE.md)")
|
||||
# (b) l'INPUT ancré — spec[entites] == CLAUDE.md (ordre exact)
|
||||
if spec_ents == cm_ents:
|
||||
good(f"Confotur entités · (b) spec[entites] == CLAUDE.md §Entités")
|
||||
else:
|
||||
bad(f"Confotur entités · (b) spec[entites] {spec_ents} ≠ CLAUDE.md §Entités "
|
||||
f"{cm_ents} → l'input du générateur DÉRIVE du mandat")
|
||||
# (c) le spec DÉCLARE l'ancrage : entites_source cite CLAUDE.md + chaque entité
|
||||
if "CLAUDE.md" not in ents_src:
|
||||
bad(f"Confotur entités · (c) `entites_source` NE CITE PAS CLAUDE.md "
|
||||
f"(ancrage non déclaré) : {ents_src!r}")
|
||||
else:
|
||||
_miss_src = [e for e in cm_ents if e not in ents_src]
|
||||
if _miss_src:
|
||||
bad(f"Confotur entités · (c) `entites_source` cite CLAUDE.md mais OMET "
|
||||
f"{_miss_src} → déclaration d'ancrage périmée")
|
||||
else:
|
||||
good(f"Confotur entités · (c) spec DÉCLARE l'ancrage (entites_source cite "
|
||||
f"CLAUDE.md + les {len(cm_ents)} entités)")
|
||||
# (d) wiring intact — le champ dérive ses options de la liste `entites`, pas d'un dur
|
||||
if ep_spec is None:
|
||||
bad("Confotur entités · (d) champ `entite_porteuse` ABSENT du spec (field_groups)")
|
||||
elif ep_spec.get("options_source") != "entites":
|
||||
bad(f"Confotur entités · (d) `entite_porteuse.options_source` = "
|
||||
f"{ep_spec.get('options_source')!r} ≠ \"entites\" → déroulant DÉCROCHÉ de la "
|
||||
f"liste ancrée (options potentiellement écrites en dur)")
|
||||
else:
|
||||
good("Confotur entités · (d) wiring intact (options_source == \"entites\")")
|
||||
# (e) README cite l'ancre CLAUDE.md + nomme chaque entité (nom complet OU 1er token)
|
||||
CONF_RD3 = f"{D}/legal/confotur/README.md"
|
||||
try:
|
||||
conf_rd3 = re.sub(r"\s+", " ", open(CONF_RD3, encoding="utf-8").read())
|
||||
except OSError as e:
|
||||
bad(f"Confotur entités · {CONF_RD3} illisible : {e}"); conf_rd3 = None
|
||||
if conf_rd3 is not None:
|
||||
_m_ep = re.search(r"`entite_porteuse`.{0,240}?9060 QC", conf_rd3)
|
||||
if not _m_ep:
|
||||
bad(f"Confotur entités · (e) ligne `entite_porteuse` INTROUVABLE dans "
|
||||
f"{CONF_RD3} (claim absent = régression #6)")
|
||||
else:
|
||||
_epline = _m_ep.group(0)
|
||||
if "CLAUDE.md" not in _epline:
|
||||
bad(f"Confotur entités · (e) ligne `entite_porteuse` ne cite pas l'ancre "
|
||||
f"« CLAUDE.md »")
|
||||
else:
|
||||
_miss_rd = []
|
||||
for _e in cm_ents:
|
||||
_first = _e.split()[0]
|
||||
if _e in _epline or re.search(r"(?<![\w-])" + re.escape(_first) + r"(?![\w-])", _epline):
|
||||
continue
|
||||
_miss_rd.append(_e)
|
||||
if _miss_rd:
|
||||
bad(f"Confotur entités · (e) README omet {_miss_rd} de la ligne "
|
||||
f"`entite_porteuse` (nom complet ni 1er token)")
|
||||
else:
|
||||
good(f"Confotur entités · (e) README cite CLAUDE.md + nomme les "
|
||||
f"{len(cm_ents)} entités")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user