[DTP-Worker 20260802_050513] Sprint 8 · buffer · RBAC SPEC §2 : ancrage de l'ÉNUMÉRATION des scope_donnees (ligne Row-level) sur l'enum de rbac.schema.json
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1497,6 +1497,79 @@ if src_roles is not None and spec_rd is not None:
|
||||
bad(f"RBAC SPEC §5 · (d) la ligne totalise {rhs} MAIS le contrat "
|
||||
f"compte {N5} rôles → SPEC périmée")
|
||||
|
||||
# 1octies) RBAC_50_ROLES_SPEC.md §2 — ligne « Row-level » : ENSEMBLE des `scope_donnees`
|
||||
# ancré sur l'ENUM du schéma ---------------------------------------------------
|
||||
# Le §2 « Modèle RBAC » ouvre par « trois niveaux, tous couverts PAR LE SCHÉMA » puis
|
||||
# sa 3ᵉ ligne de table (« **Row-level** (User Permission) | `scope_donnees` | … ») ÉNUMÈRE
|
||||
# À LA MAIN l'ensemble EXHAUSTIF des portées row-level : `own` · `equipe` · `entite`
|
||||
# (Company) · `groupe` (consolidé). Cet ensemble EST, par la phrase d'intro, l'`enum`
|
||||
# `scope_donnees` de `rbac.schema.json` (la DÉFINITION du modèle — pas le sous-ensemble
|
||||
# effectivement exercé par les 50 rôles, qui peut être plus petit). AUCUN gate ne le
|
||||
# touchait : §3 compte les rôles par portail, §4 les entités, §5 le résumé, le bloc
|
||||
# userperm gate la TABLE `scope → mécanisme` d'un AUTRE artefact (userperm_gen/out) —
|
||||
# tous AVEUGLES à l'énumération du §2. « Vert trompeur » : le schéma AJOUTE une portée
|
||||
# (`region` pour un besoin ops réel) ou en RETIRE une (`own` supprimé, schéma resserré)
|
||||
# et le §2 reste PÉRIMÉ en silence — il documente un modèle qui contredit le schéma
|
||||
# tout en se réclamant « couvert par le schéma ». Ni check_artifacts (la SPEC n'est pas
|
||||
# un out/*.json) ni tests/ (FONCTIONS de validation, jamais l'ancre SPEC↔schéma) ne
|
||||
# mordent. Même classe que §4 (colonne ancrée sur une source-de-vérité structurée).
|
||||
# GATE : (a) ENSEMBLE des jetons `scope` de la ligne §2 == `enum` scope_donnees du
|
||||
# schéma (set-diff absent ET fantôme) ; (b) ANCRAGE — la colonne du milieu NOMME bien
|
||||
# `scope_donnees` (sinon on gate la mauvaise ligne) ; (c) sanity — toute portée
|
||||
# RÉELLEMENT exercée par le contrat ∈ enum (une portée en usage jamais dans l'enum =
|
||||
# schéma incohérent). On NE gate PAS les gloses parenthétiques `(Company)`/`(consolidé)`
|
||||
# (éditoriales · #6). Ligne/section/enum absents = échec (traçabilité #6).
|
||||
if src_roles is not None and spec_rd is not None:
|
||||
try:
|
||||
_sch = load("rbac/rbac.schema.json")
|
||||
_enum = _sch["definitions"]["role"]["properties"]["scope_donnees"]["enum"]
|
||||
scope_enum = set(_enum)
|
||||
if not scope_enum:
|
||||
raise ValueError("enum scope_donnees vide")
|
||||
if len(scope_enum) != len(_enum):
|
||||
raise ValueError(f"enum scope_donnees non unique : {_enum}")
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"RBAC SPEC §2 · rbac.schema.json enum scope_donnees illisible : {e}")
|
||||
scope_enum = None
|
||||
_m2 = re.search(r"## 2\..*?(?=## 3\.)", spec_rd)
|
||||
if _m2 is None:
|
||||
bad(f"RBAC SPEC §2 · section « ## 2. » INTROUVABLE dans {SPEC_RD} "
|
||||
f"(claim absent = régression #6)")
|
||||
elif scope_enum is not None:
|
||||
# Ligne « Row-level » : capte la colonne « champ » (c2) et la colonne énum (c3)
|
||||
_row = re.search(r"\| \*\*Row-level\*\*[^|]*\|([^|]*)\|([^|]*)\|", _m2.group(0))
|
||||
if _row is None:
|
||||
bad(f"RBAC SPEC §2 · ligne « **Row-level** » INTROUVABLE dans {SPEC_RD} "
|
||||
f"(claim absent = régression #6)")
|
||||
else:
|
||||
c_field, c_enum = _row.group(1), _row.group(2)
|
||||
# (b) la colonne du milieu doit nommer le champ `scope_donnees`
|
||||
if "scope_donnees" not in c_field:
|
||||
bad(f"RBAC SPEC §2 · (b) colonne « champ » de la ligne Row-level ne "
|
||||
f"nomme pas `scope_donnees` (capté {c_field!r}) → ligne mal ancrée")
|
||||
else:
|
||||
good("RBAC SPEC §2 · (b) ligne Row-level ancrée sur le champ "
|
||||
"`scope_donnees`")
|
||||
spec_scopes = set(re.findall(r"`([a-z_]+)`", c_enum))
|
||||
_miss = sorted(scope_enum - spec_scopes) # dans l'enum, absents du §2
|
||||
_phan = sorted(spec_scopes - scope_enum) # listés au §2, hors enum
|
||||
if _miss or _phan:
|
||||
bad(f"RBAC SPEC §2 · (a) énumération `scope_donnees` ≠ enum du "
|
||||
f"schéma — manquants={_miss} · fantômes={_phan} → aligner la "
|
||||
f"SPEC sur rbac.schema.json (ou régénérer si le schéma a changé)")
|
||||
else:
|
||||
good(f"RBAC SPEC §2 · (a) {len(spec_scopes)} portées row-level "
|
||||
f"{sorted(spec_scopes)} == enum scope_donnees du schéma")
|
||||
# (c) sanity : toute portée exercée par le contrat ∈ enum
|
||||
_used = {r.get("scope_donnees") for r in src_roles}
|
||||
_illegal = sorted(_used - scope_enum)
|
||||
if _illegal:
|
||||
bad(f"RBAC SPEC §2 · (c) portées `scope_donnees` exercées par le contrat "
|
||||
f"HORS enum du schéma : {_illegal} → schéma/contrat incohérents")
|
||||
else:
|
||||
good(f"RBAC SPEC §2 · (c) les {len(_used)} portées exercées par le contrat "
|
||||
f"sont toutes dans l'enum du schéma")
|
||||
|
||||
# 2) apply_plan — ligne « Génération réelle » agrégeant les 3 volets ------------
|
||||
AP_RD = f"{D}/rbac/apply_plan/README.md"
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user