[DTP-Worker] Sprint 8 · buffer · RBAC/SPEC §7 : gate d'IDENTITÉ des comptes du run-book d'application (28 UP templates · 6 Role Profiles / 5 métier · 50 rôles bijectifs) ancrés sur rbac_50_roles.json

Surface data-derived non gatée : la NOTE §7 «  … livré en-repo » de
RBAC_50_ROLES_SPEC.md restitue 3 comptes du run-book (28 templates Company /
6 profils (5 métier + plateforme technique) / 50 rôles bijectifs) sur une
surface DISTINCTE des READMEs userperm_gen/roleprofile_gen déjà gatés et du
§3 (per-portail). Nouveau bloc 1quinquies dans check_readme_claims.sh :
recompute depuis le contrat byte-gaté (|scope==entite|, |portails distincts|,
|métier hors plateforme|, |roles|), blockquote « > » neutralisé avant match.
6 morsures vérifiées (dont contrat entite→groupe = silent green), restauré
vert, 7 gates PASS. ci/README.md (récap + paragraphe détaillé) mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-01 10:11:39 +00:00
parent 7ce0f7b1e7
commit 7e8bb9a0de
3 changed files with 134 additions and 1 deletions
+23 -1
View File
File diff suppressed because one or more lines are too long
+56
View File
@@ -869,6 +869,62 @@ if src_roles is not None and spec_rd is not None:
bad(f"RBAC SPEC §3 · total — {SPEC_RD} dit {mt.group(1)} MAIS "
f"rbac_50_roles.json compte {exp_tot} → corriger la SPEC")
# 1quinquies) RBAC_50_ROLES_SPEC.md §7 — comptes du run-book d'application --------
# La NOTE §7 (« ✅ … livré en-repo ») restitue, sur une SURFACE DISTINCTE des READMEs
# userperm_gen / roleprofile_gen (déjà gatés), TROIS comptes data-derived du run-book
# d'application : « 28 templates `Company` (portée `entite`) », « 6 profils (5 portails
# métier + console technique `plateforme`) » et « 50 rôles de façon bijective ». Ces
# nombres sont recopiés À LA MAIN dans la SPEC ; le bloc §3 ne gate QUE la ventilation
# par portail, et les gates userperm/roleprofile ne regardent QUE leurs propres READMEs
# — AVEUGLES à cette note. BUMPER une portée `entite`→`groupe` dans le contrat (le
# nombre de `User Permission` templates change), AJOUTER/RETIRER un portail (le nombre
# de `Role Profile` change) ou casser la bijection laisse la note PÉRIMÉE en silence
# pendant que le contrat dit autre chose → l'agent ERPNext Backend lirait un run-book
# qui promet un décompte faux. On RECOMPUTE les 3 comptes depuis rbac_50_roles.json
# (source de vérité, byte-gatée par check_artifacts) : 28 == |scope==entite|, 6 ==
# |portails distincts| (= 1 Role Profile par portail), 5 == portails métier (hors
# `plateforme`, la console technique), 50 == |rôles|. Un claim absent échoue AUSSI.
if src_roles is not None and spec_rd is not None:
# La note §7 vit dans un blockquote (`> `) : les marqueurs survivent au collapse
# de read_norm et fragmentent la prose (« 28 > templates »). On les retire pour
# ce bloc (aucun `> ` littéral hors blockquote dans cette SPEC ; les flèches sont
# des `→`). Le §3 (tables sans blockquote) n'en a pas besoin.
spec7 = spec_rd.replace("> ", "")
up_entite = sum(1 for r in src_roles if r.get("scope_donnees") == "entite")
portails = {r.get("portail") for r in src_roles}
metier = portails - {"plateforme"}
if "plateforme" not in portails:
bad("RBAC SPEC §7 · portail technique `plateforme` ABSENT du contrat "
"rbac_50_roles.json → la note « console technique plateforme » est fausse")
exp_up = str(up_entite)
m_up = re.search(r"(\d+) templates `Company` \(portée `entite`\)", spec7)
if m_up is None:
bad(f"RBAC SPEC §7 · « N templates Company (portée entite) » INTROUVABLE "
f"dans {SPEC_RD} (attendu {exp_up})")
elif m_up.group(1) == exp_up:
good(f"RBAC SPEC §7 · UP templates {m_up.group(1)} == |scope==entite| "
f"rbac_50_roles.json ({exp_up})")
else:
bad(f"RBAC SPEC §7 · {SPEC_RD} dit {m_up.group(1)} templates Company MAIS "
f"rbac_50_roles.json compte {exp_up} rôles `entite` → corriger la SPEC")
exp_prof, exp_metier, exp_bij = str(len(portails)), str(len(metier)), str(len(src_roles))
m_pr = re.search(
r"\*\*(\d+) profils\*\* \((\d+) portails métier \+ console technique "
r"`plateforme`\) couvrant les \*\*(\d+) rôles de façon bijective\*\*", spec7)
if m_pr is None:
bad(f"RBAC SPEC §7 · « N profils (M portails métier …) couvrant les K rôles "
f"de façon bijective » INTROUVABLE dans {SPEC_RD} "
f"(attendu {exp_prof}/{exp_metier}/{exp_bij})")
elif m_pr.groups() == (exp_prof, exp_metier, exp_bij):
good(f"RBAC SPEC §7 · Role Profiles {m_pr.group(1)} (métier {m_pr.group(2)}) "
f"bijectifs sur {m_pr.group(3)} rôles == rbac_50_roles.json "
f"({exp_prof}/{exp_metier}/{exp_bij})")
else:
bad(f"RBAC SPEC §7 · {SPEC_RD} dit {'/'.join(m_pr.groups())} MAIS "
f"rbac_50_roles.json compte {exp_prof}/{exp_metier}/{exp_bij} "
f"(profils=portails distincts / métier=hors plateforme / rôles) "
f"→ corriger la SPEC")
# 2) apply_plan — ligne « Génération réelle » agrégeant les 3 volets ------------
AP_RD = f"{D}/rbac/apply_plan/README.md"
try: