[DTP-Worker] Sprint 8 · buffer · Domaine Portails (2e surface) : le CATALOGUE des DocTypes custom à CRÉER avant import (« CONFOTUR Application, Faisabilité, Publiciste Log ») était énuméré À LA MAIN dans le §« Hand-off → agent ERPNext Backend » du README frontend/portails sans AUCUN gate d'IDENTITÉ. Le bloc portails existant ne gate que les COMPTES par workspace (cartes/liens/rôles, ×2 docs) — jamais ce catalogue de hand-off. Ce catalogue est DATA-DERIVED : doctypes_custom_a_creer de frontend/portails/out/MANIFEST.json (byte-gaté par check_artifacts), lui-même l'UNION des doctypes_custom par workspace (flag custom du contrat rbac_50_roles.json, pour les seuls DocTypes CÂBLÉS dans un portail). C'est un ensemble PROPRE au module (sous-ensemble de celui de rbac/fixtures_gen : API Access, custom mais non câblé, en est ABSENT). PIÈGE : check_artifacts ne prouve QUE MANIFEST==build (byte-for-byte) et le build RECOMPUTE l'union — donc AJOUTER une carte visant un nouveau DocType custom au contrat (le MANIFEST rebâtit le catalogue) · en RENOMMER un · en RETIRER un du câblage laisse la consigne de hand-off périmée pendant que l'artefact dit autre chose : l'agent Backend créerait le mauvais jeu de DocTypes (liens pointant dans le vide — le risque même que la consigne veut prévenir) — « vert trompeur » qu'aucune suite tests/ (qui teste des FONCTIONS de mapping, pas la prose de hand-off) n'attrape → nouveau bloc recomputant le catalogue depuis MANIFEST (zéro duplication du contrat du générateur #6 · même patron que le catalogue custom de rbac/fixtures_gen) et exigeant que la prose l'énumère EXACTEMENT (absent ET en trop via set-diff · accents/casse normalisés via unicodedata). Cohérences croisées en bonus (mordent un MANIFEST INTERNEMENT incohérent) : catalogue NON VIDE, sans doublon et TRIÉ (byte-déterminisme) · égal à l'UNION des doctypes_custom par workspace (pas de DocType custom câblé mais oublié du hand-off, ni l'inverse). Un claim absent échoue AUSSI. 5 morsures vérifiées : prose renomme Faisabilité→Faisabilite2 (absents=[faisabilite] en trop=[faisabilite2]) · prose retire Publiciste Log (sous-ensemble) · énumération supprimée (INTROUVABLE) · MANIFEST retire du catalogue un DocType encore câblé (incohérence interne : câblés hors catalogue=[Publiciste Log] + prose en trop) · catalogue MANIFEST non trié (byte-déterminisme) ; restauré = green : [CONFOTUR Application,Faisabilité,Publiciste Log] == MANIFEST == union des workspaces · exit 0. État courant : aucun catalogue périmé (anti-invention #6, rien à réécrire) — le défaut est la surface ungated. ci/README.md (table + détail « 2e surface Portails ») mis à jour · 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2080,6 +2080,88 @@ if cat is not None:
|
||||
bad(f"CRM projets · README dit {mp.group(1)}..{mp.group(2)} MAIS catalogue "
|
||||
f"CLAUDE.md va de {cat_codes[0]} à {cat_codes[-1]} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# Portails frontend (2e surface) · CATALOGUE des DocTypes `custom` à CRÉER avant
|
||||
# import — la consigne de hand-off VPS énumérée À LA MAIN, sans gate d'IDENTITÉ.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le bloc portails ci-dessus recompute les COMPTES par workspace (cartes/liens/
|
||||
# rôles) et la table portail⇔workspace. Restait, dans le MÊME README, une surface
|
||||
# data-derived DISTINCTE et jamais gatée : le §« Hand-off → agent ERPNext Backend »
|
||||
# item 2 énumère « DocTypes custom à créer avant import : `CONFOTUR Application`,
|
||||
# `Faisabilité`, `Publiciste Log` (… le reste est natif v15) ». Ce catalogue est
|
||||
# DATA-DERIVED : c'est out/MANIFEST.json `doctypes_custom_a_creer` (byte-gaté par
|
||||
# check_artifacts) = l'UNION des `doctypes_custom` par workspace, eux-mêmes lus du
|
||||
# flag `custom` du contrat rbac_50_roles.json pour les seuls DocTypes visés par une
|
||||
# carte/un lien du portail. C'est un ensemble PROPRE au module portails (sous-
|
||||
# ensemble de celui de rbac/fixtures_gen : ici seuls comptent les DocTypes custom
|
||||
# effectivement CÂBLÉS dans un workspace — API Access, custom mais non câblé, en est
|
||||
# ABSENT). check_artifacts ne prouve QUE MANIFEST==build (byte-for-byte), et le
|
||||
# build RECOMPUTE l'union — donc AJOUTER une carte visant un nouveau DocType custom
|
||||
# au contrat (le MANIFEST rebâtit le catalogue) · en RENOMMER un · en RETIRER un du
|
||||
# câblage laisse la prose de hand-off périmée pendant que l'artefact dit autre
|
||||
# chose : l'agent Backend créerait le mauvais jeu de DocTypes (liens pointant dans
|
||||
# le vide — le risque même que la consigne veut prévenir) — « vert trompeur »
|
||||
# qu'aucune suite tests/ (qui teste des FONCTIONS de mapping, pas la prose de hand-
|
||||
# off) n'attrape. Même patron que le catalogue custom de rbac/fixtures_gen. On
|
||||
# RECOMPUTE le catalogue depuis MANIFEST (zéro duplication du contrat du générateur
|
||||
# #6) et on exige que la prose l'énumère EXACTEMENT (absent ET en trop via set-diff ·
|
||||
# accents/casse normalisés). Cohérences croisées en bonus (mordent un MANIFEST
|
||||
# INTERNEMENT incohérent) : catalogue NON VIDE, sans doublon et TRIÉ (byte-
|
||||
# déterminisme) · top-level == UNION des `doctypes_custom` par workspace (pas de
|
||||
# DocType custom câblé mais oublié du catalogue de hand-off, ni l'inverse). Un claim
|
||||
# absent échoue AUSSI (traçabilité).
|
||||
# ============================================================================
|
||||
PC_RD = f"{D}/frontend/portails/README.md"
|
||||
def _pc_acc(s):
|
||||
return "".join(c for c in unicodedata.normalize("NFKD", s)
|
||||
if unicodedata.category(c) != "Mn").strip().lower()
|
||||
try:
|
||||
pc_man = load("frontend/portails/out/MANIFEST.json")
|
||||
pc_cat = pc_man["doctypes_custom_a_creer"]
|
||||
pc_union = sorted({d for w in pc_man["workspaces"]
|
||||
for d in w.get("doctypes_custom", [])})
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Portails catalogue · MANIFEST illisible/inattendu : {e}"); pc_cat = None
|
||||
if pc_cat is not None:
|
||||
# bonus : le catalogue est un vrai ENSEMBLE trié non vide (byte-déterminisme)
|
||||
if not pc_cat:
|
||||
bad("Portails catalogue · doctypes_custom_a_creer VIDE → aucun DocType custom "
|
||||
"câblé dans un workspace ? (contrat/spec suspect)")
|
||||
elif pc_cat != sorted(set(pc_cat)):
|
||||
bad(f"Portails catalogue · doctypes_custom_a_creer {pc_cat} non trié/dédupliqué "
|
||||
f"→ générateur non déterministe (attendu {sorted(set(pc_cat))})")
|
||||
else:
|
||||
good(f"Portails catalogue · custom à créer {pc_cat} — ensemble trié, sans doublon")
|
||||
# bonus : cohérence interne — catalogue == union des doctypes_custom par workspace
|
||||
if sorted(set(pc_cat)) != pc_union:
|
||||
miss = sorted(set(pc_union) - set(pc_cat)); extra = sorted(set(pc_cat) - set(pc_union))
|
||||
detail = (f" · câblés hors catalogue={miss}" if miss else "") + \
|
||||
(f" · au catalogue mais non câblés={extra}" if extra else "")
|
||||
bad(f"Portails catalogue · doctypes_custom_a_creer {sorted(pc_cat)} ≠ UNION des "
|
||||
f"doctypes_custom par workspace {pc_union}{detail} → MANIFEST incohérent")
|
||||
else:
|
||||
good(f"Portails catalogue · catalogue == union des doctypes_custom par workspace "
|
||||
f"({pc_union})")
|
||||
pc_rd = read_norm(PC_RD)
|
||||
if pc_rd is not None:
|
||||
# README §Hand-off item 2 — énumération du catalogue custom avant « (issus »
|
||||
mp = re.search(r"custom à créer\*\* avant import[^:]*?: (.+?)\(issus", pc_rd)
|
||||
if mp is None:
|
||||
bad(f"Portails catalogue · {PC_RD} — énumération « DocTypes custom à créer … » "
|
||||
f"INTROUVABLE (attendu {sorted(pc_cat)})")
|
||||
else:
|
||||
got = {_pc_acc(t) for t in re.findall(r"`([^`]+)`", mp.group(1))}
|
||||
exp = {_pc_acc(x) for x in pc_cat}
|
||||
if got == exp:
|
||||
good(f"Portails catalogue · {PC_RD} — hand-off {sorted(pc_cat)} == "
|
||||
f"MANIFEST.doctypes_custom_a_creer")
|
||||
else:
|
||||
miss = sorted(exp - got); extra = sorted(got - exp)
|
||||
detail = (f" · absents={miss}" if miss else "") + \
|
||||
(f" · en trop={extra}" if extra else "")
|
||||
bad(f"Portails catalogue · {PC_RD} — hand-off énumère {sorted(got)} MAIS "
|
||||
f"MANIFEST {sorted(exp)}{detail} → régénérer le README")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user