[DTP-Worker] Sprint 8 · buffer · Faisabilité/fiches agents : les DEUX FICHES D'IDENTITÉ des rôles RBAC faisabilite-rendu-3d (03_agents/rendu/AGENT.md:34) et faisabilite-ifc-speckle (03_agents/ifc_speckle/AGENT.md:34) — la carte que l'agent ERPNext Backend lit pour SEEDER le rôle porteur des rendus / de l'export IFC→GLB — recopiaient EN PROSE, DEPUIS le contrat rbac_50_roles.json, les attributs data-derived du rôle (erpnext_role_name · nom_fr/modules côté rendu · portail · entite_principale · niveau · les DEUX permissions_cibles File R/W/create + Faisabilité R ou R/W) SANS AUCUN gate d'IDENTITÉ. Le README frontend/portails n'est gaté que sur des COMPTES de rôles par Workspace, aveugles à l'identité de CE rôle.
Même classe que la Fiche Mobile (session précédente) — surface 03_agents/*/AGENT.md transcrivant à la main les attributs d'un rôle depuis le contrat, gatée (si tant est) seulement sur des comptes agrégés. Gate ajouté (bloc « Fiches Faisabilité », config-table par fiche : fichier · role_id · attributs restitués) : par attribut recomputé du contrat, prose EXACTE exigée ; permissions rendues en ORDRE CANONIQUE (read→R · write→W · create/delete/… en toutes lettres — élargir/rétrécir change la chaîne, `R/W/create` ne satisfait plus `R/W/create/delete`) ; verbe inconnu ⇒ ROUGE ; claim absent échoue AUSSI. #6 (honnêteté) vérifié AVANT édition : la DESCRIPTION de ces deux fiches est une PARAPHRASE éditoriale (le contrat porte « (… — CLAUDE.md) » / « les modèles » que la fiche condense) — donc NON gatée verbatim (ne rien réécrire de correct pour un gate), seuls les attributs structurés (transcrits à l'exact) sont contraints. État courant : aucun attribut structuré périmé (12 recomputes == contrat). 11 morsures vérifiées (5 contrat + 6 fiche) : rendu Faisabilité +write (R→R/W) · rendu niveau 2→3 · rendu WA SRL→WAF · rendu rôle renommé · ifc Faisabilité R/W→R · fiche rendu File R/W/create→…/delete · fiche ifc R/W→R/W/create · fiche rendu module Faisabilité→Selling · fiche ifc WA SRL→WAF · fiche rendu nom_fr retiré (absent) · ligne contact ifc supprimée (INTROUVABLE) ; restauré vert, 7 gates re-verts. ci/README.md + activity log MAJ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+29
-1
File diff suppressed because one or more lines are too long
@@ -3308,6 +3308,103 @@ if mob_role is not None and mob_txt is not None:
|
||||
bad(f"Fiche Mobile · {MOB} — description « {_plain(md.group(1))} » "
|
||||
f"MAIS contrat « {exp_desc} » → description périmée, régénérer")
|
||||
|
||||
# ============================================================================
|
||||
# Fiches Faisabilité (03_agents/rendu/AGENT.md · 03_agents/ifc_speckle/AGENT.md) ·
|
||||
# les FICHES D'IDENTITÉ des rôles RBAC `faisabilite-rendu-3d` et
|
||||
# `faisabilite-ifc-speckle` — même classe de « vert trompeur » que la Fiche Mobile.
|
||||
# ----------------------------------------------------------------------------
|
||||
# La ligne « Contact in-repo (vérifié) » de chaque fiche (rendu:34 · ifc_speckle:34)
|
||||
# recopie À LA MAIN, DEPUIS le contrat rbac_50_roles.json (source de vérité RBAC,
|
||||
# schéma-validé · byte-gaté par les 3 MANIFEST recomputés), les attributs data-derived
|
||||
# du rôle : son `erpnext_role_name`, son `portail`, son `entite_principale`, son
|
||||
# `niveau`, ses `modules` (rendu), son `nom_fr` (rendu) et ses `permissions_cibles`
|
||||
# (deux doctypes chacune : `File` R/W/create + `Faisabilité` R ou R/W). C'est la carte
|
||||
# que l'agent ERPNext Backend lit pour seeder le rôle porteur des rendus / de l'export
|
||||
# IFC→GLB. AUCUN gate ne comparait ces valeurs au contrat : le README frontend/portails
|
||||
# n'est gaté que sur des COMPTES de rôles par Workspace, AVEUGLES à l'identité de CE
|
||||
# rôle. Éditer le contrat — élever le `scope_donnees`, changer le `niveau`,
|
||||
# l'`entite_principale`, renommer le rôle, ajouter/retirer un module ou élargir la
|
||||
# permission (`Faisabilité` R→R/W : le rôle Rendu 3D gagnerait l'écriture sur le
|
||||
# DocType Faisabilité qu'il ne doit que LIRE) — laisse la fiche périmée en silence
|
||||
# pendant que le contrat dit autre chose ⇒ l'agent câblerait le mauvais rôle
|
||||
# (l'invention même que #6 interdit). Aucune suite tests/ (qui teste des FONCTIONS de
|
||||
# résolution RBAC, pas la prose d'une fiche) n'attrape ce « vert trompeur ». On
|
||||
# RECOMPUTE chaque attribut RESTITUÉ par la fiche depuis le contrat (zéro duplication)
|
||||
# et on exige que la fiche l'énonce EXACTEMENT. Les verbes de permission sont rendus
|
||||
# dans un ORDRE CANONIQUE (read→R · write→W · create/delete/submit/… en toutes
|
||||
# lettres, ordre fixe) : élargir OU rétrécir une permission change la chaîne attendue
|
||||
# (`R/W` ne satisfait plus `R/W/create`, ni l'inverse) ; verbe inconnu ⇒ ROUGE
|
||||
# (étendre le gate). Un claim absent échoue AUSSI (traçabilité). NB : contrairement
|
||||
# à la Fiche Mobile, la DESCRIPTION de ces deux fiches est une PARAPHRASE éditoriale
|
||||
# (le contrat porte « (… — CLAUDE.md) » / « les modèles » que la fiche condense) —
|
||||
# elle n'est donc PAS gatée verbatim (ne rien réécrire de correct pour un gate · #6) ;
|
||||
# seuls les attributs STRUCTURÉS, eux transcrits à l'exact, sont contraints.
|
||||
# ============================================================================
|
||||
_CANON_VERB = [("read", "R"), ("write", "W"), ("create", "create"),
|
||||
("delete", "delete"), ("submit", "submit"), ("cancel", "cancel"),
|
||||
("amend", "amend"), ("print", "print"), ("report", "report")]
|
||||
FICHES_FAIS = [
|
||||
{"file": "03_agents/rendu/AGENT.md", "rid": "faisabilite-rendu-3d",
|
||||
"attrs": ["role", "nom_fr", "portail", "entite", "niveau", "modules", "perms"]},
|
||||
{"file": "03_agents/ifc_speckle/AGENT.md", "rid": "faisabilite-ifc-speckle",
|
||||
"attrs": ["role", "portail", "entite", "niveau", "perms"]},
|
||||
]
|
||||
try:
|
||||
_fais_roles = load("rbac/rbac_50_roles.json")["roles"]
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Fiches Faisabilité · rbac_50_roles.json illisible/inattendu : {e}")
|
||||
_fais_roles = None
|
||||
if _fais_roles is not None:
|
||||
for fc in FICHES_FAIS:
|
||||
rid, tag = fc["rid"], fc["file"].split("/")[1]
|
||||
cand = [r for r in _fais_roles if r.get("id") == rid]
|
||||
ftxt = read_norm(fc["file"])
|
||||
if ftxt is None:
|
||||
continue
|
||||
if len(cand) != 1:
|
||||
bad(f"Fiche {tag} · le contrat porte {len(cand)} rôle(s) `{rid}` "
|
||||
f"(attendu exactement 1) → contrat changé de forme, vérifier")
|
||||
continue
|
||||
r = cand[0]
|
||||
seg = re.search(rf"→ rôle `{re.escape(rid)}` \|(.+?)\|", ftxt)
|
||||
if seg is None:
|
||||
bad(f"Fiche {tag} · {fc['file']} — ligne de contact du rôle `{rid}` "
|
||||
f"INTROUVABLE (un claim absent est une régression · #6)")
|
||||
continue
|
||||
cell = seg.group(1)
|
||||
# permissions_cibles → chaîne « `Doctype` R/W/create + `Autre` R », ordre canon
|
||||
_perm_parts, _unknown = [], set()
|
||||
for p in (r.get("permissions_cibles") or []):
|
||||
acts = set(p.get("actions", []))
|
||||
_unknown |= acts - {k for k, _ in _CANON_VERB}
|
||||
sig = "/".join(tok for k, tok in _CANON_VERB if k in acts)
|
||||
_perm_parts.append(f"`{p.get('doctype')}` {sig}")
|
||||
perm_str = " + ".join(_perm_parts)
|
||||
if _unknown:
|
||||
bad(f"Fiche {tag} · rôle `{rid}` — verbe(s) inconnu(s) "
|
||||
f"{sorted(_unknown)} dans le contrat → étendre le gate")
|
||||
mods = r.get("modules") or []
|
||||
mod_pat = (rf"module `{re.escape(mods[0])}`(?![+`\w])" if len(mods) == 1
|
||||
else r"modules " + r"\+".join(f"`{re.escape(m)}`" for m in mods))
|
||||
_PATS = {
|
||||
"role": (rf"rôle ERPNext \*\*`{re.escape(r['erpnext_role_name'])}`\*\*",
|
||||
r["erpnext_role_name"]),
|
||||
"nom_fr": (rf"« {re.escape(r['nom_fr'])} »", r["nom_fr"]),
|
||||
"portail": (rf"portail {re.escape(r['portail'])}(?![\w])", r["portail"]),
|
||||
"entite": (rf"(?<![\w]){re.escape(r['entite_principale'])}(?![\w])",
|
||||
r["entite_principale"]),
|
||||
"niveau": (rf"niveau {r['niveau']}(?!\d)", str(r["niveau"])),
|
||||
"modules": (mod_pat, "+".join(mods)),
|
||||
"perms": (rf"perms {re.escape(perm_str)}(?![\w/])", perm_str),
|
||||
}
|
||||
for a in fc["attrs"]:
|
||||
pat, exp = _PATS[a]
|
||||
if re.search(pat, cell):
|
||||
good(f"Fiche {tag} · rôle `{rid}` — {a} « {exp} » == contrat")
|
||||
else:
|
||||
bad(f"Fiche {tag} · {fc['file']} — {a} : la fiche ne cite pas "
|
||||
f"« {exp} » du contrat rbac_50_roles.json (périmé ?) → régénérer")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user