[DTP-Worker] Sprint 8 · buffer · RBAC/SPEC §4 : gate d'IDENTITÉ de la colonne « Entité » de la « Cartographie rôles ↔ entités » (7 entités nommées + Groupe consolidé) doublement ancré sur CLAUDE.md §Entités (descripteurs) ⨯ rbac_50_roles.json (ENSEMBLE des entite_principale)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1219,3 +1219,67 @@ schema.org/hreflang amont — zéro duplication #6) :
|
|||||||
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; l'injection réelle des
|
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; l'injection réelle des
|
||||||
balises hreflang/JSON-LD dans les pages reste côté agent Frontend/SEO / VPS).
|
balises hreflang/JSON-LD dans les pages reste côté agent Frontend/SEO / VPS).
|
||||||
- **Auto-score 4Big** : 96/100.
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|
||||||
|
## Session `20260801_113314` · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §4 — colonne « Entité » de la « Cartographie rôles ↔ entités » ancrée sur CLAUDE.md §Entités ⨯ rbac_50_roles.json
|
||||||
|
|
||||||
|
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
|
||||||
|
série anti-dérive CLAUDE.md #6, double classe « ANCRAGE à CLAUDE.md »
|
||||||
|
([[claude-md-constant-anchor-gate]]) **et** « identité d'ensemble recomputée du
|
||||||
|
contrat RBAC » [[rbac-spec-portail-count-gate]]). Même patron que les entités CONFOTUR
|
||||||
|
`entite_porteuse` (session 053228, §Entités) et les comptes SPEC §3/§7 — appliqué à
|
||||||
|
une **section encore ungated de la MÊME SPEC** : la table §4.
|
||||||
|
|
||||||
|
**Dérive silencieuse fermée** :
|
||||||
|
- `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` §4 « Cartographie rôles ↔ entités » —
|
||||||
|
la **colonne de tête** liste les entités porteuses (`entite_principale`) : les **7
|
||||||
|
entités NOMMÉES** du mandat (`WAF` (holding) · `WA SRL` · `AC Arias Cuevas` ·
|
||||||
|
`Consortium ECR DR` (paymaster RH) · `Helios RD` (marque publique) · `Ploutos`
|
||||||
|
(patrimoine/monétaire) · `9060 QC` (World Activities Cdn.)) + le périmètre consolidé
|
||||||
|
« **Groupe** (consolidé) ».
|
||||||
|
- **Deux sources faisant autorité** : (i) **CLAUDE.md §Entités** — l'orthographe + le
|
||||||
|
descripteur parenthétique de chaque entité nommée ; (ii) `rbac_50_roles.json`
|
||||||
|
(byte-gaté via les 3 MANIFEST) — l'ENSEMBLE des `entite_principale` réellement portées
|
||||||
|
par les 50 rôles (distinct = 11 Helios RD · 11 WA SRL · 10 AC Arias Cuevas · 8 Groupe ·
|
||||||
|
5 9060 QC · 3 WAF · 1 Ploutos · 1 Consortium ECR DR = 50).
|
||||||
|
- Piège : le §3 gate les comptes par **portail**, le §7 les comptes du run-book, les
|
||||||
|
blocs fiches l'`entite` d'**UN** rôle ; tous **aveugles à l'identité de CETTE liste**.
|
||||||
|
AJOUTER au contrat un rôle porté par une entité absente de §4 (menu périmé), RENOMMER
|
||||||
|
une entité dans CLAUDE.md §Entités (`9060 QC→9061 QC`) ou DÉRIVER un descripteur laisse
|
||||||
|
la SPEC contredire le mandat/le contrat EN SILENCE — « vert trompeur » qu'aucune suite
|
||||||
|
`tests/` (FONCTIONS RBAC, jamais la prose de la SPEC) n'attrape.
|
||||||
|
- **État courant** : aucune ligne périmée — §4 == distinct contrat (8 entités) · les 7
|
||||||
|
descripteurs == CLAUDE.md §Entités · aucune entité décorative (anti-invention #6, rien
|
||||||
|
à réécrire). Le défaut est la **surface ungated**.
|
||||||
|
|
||||||
|
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc `1sexies` après le §7, réutilise
|
||||||
|
`src_roles`/`spec_rd`/`SPEC_RD` déjà en scope · extraction fraîche de CLAUDE.md §Entités
|
||||||
|
— `_nfc` défini plus bas, non requis, entités ASCII) :
|
||||||
|
- **(a) ENSEMBLE** — entités de la colonne §4 (`| **Entité** (desc)? |`) == distinct
|
||||||
|
`entite_principale` du contrat (set-diff : `manquantes` = portées mais hors §4 ·
|
||||||
|
`fantômes` = listées mais jamais portées).
|
||||||
|
- **(b) ANCRAGE** — chaque entité NOMMÉE de CLAUDE.md §Entités (tokens en gras) est une
|
||||||
|
ligne §4 ET son descripteur parenthétique == CLAUDE.md **EXACT** (absence comprise :
|
||||||
|
`WA SRL`/`AC Arias Cuevas` = sans descripteur des deux côtés).
|
||||||
|
- **(c)** chaque entité nommée du mandat est PORTÉE ≥1× par le contrat (`cm_ents ⊆
|
||||||
|
contrat`, aucune décorative).
|
||||||
|
- Section `## 4.` INTROUVABLE = claim absent = régression #6.
|
||||||
|
|
||||||
|
**8 morsures vérifiées** (4 côté contrat/CLAUDE.md · 4 côté SPEC) : contrat +rôle porté
|
||||||
|
par entité `Fantasia` absente de §4 (`(a)` manquante) · contrat `Ploutos→Ploutos SRL`
|
||||||
|
sur tous les rôles (`(a)` fantôme+manquante · `(c)` `Ploutos` décorative) · CLAUDE.md
|
||||||
|
`9060 QC→9061 QC` (`(b)` absent · `(c)` décorative) · CLAUDE.md descripteur
|
||||||
|
`(holding)→(holding co)` (`(b)` mismatch) · SPEC §4 `AC Arias Cuevas→AC Arias` (`(a)`
|
||||||
|
fantôme+manquante · `(b)` absent) · SPEC §4 descripteur `marque publique→marque privée`
|
||||||
|
(`(b)` mismatch) · SPEC §4 retire la ligne « Groupe » (`(a)` manquante) · SPEC supprime
|
||||||
|
l'en-tête `## 4.` (INTROUVABLE) ; restauré = green : 9 ✓ (`(a)` 8 entités · `(b)` 7
|
||||||
|
descripteurs · `(c)` 7 portées) · exit 0. Working tree byte-restauré (`git checkout --`,
|
||||||
|
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts** (`check_readme_claims`/
|
||||||
|
`check_artifacts`/`check_docs`/`guard_constraints`/`check_regression`/
|
||||||
|
`check_ci_integrity`/`validate_json`).
|
||||||
|
|
||||||
|
- `ci/README.md` (clause de la ligne récap du pipeline + paragraphe détaillé « Colonne
|
||||||
|
Entité de la §4 ancrée sur CLAUDE.md §Entités ») mis à jour.
|
||||||
|
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||||||
|
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; le seed réel des
|
||||||
|
rôles/entités `bench` reste côté VPS).
|
||||||
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|||||||
+31
-1
File diff suppressed because one or more lines are too long
@@ -925,6 +925,85 @@ if src_roles is not None and spec_rd is not None:
|
|||||||
f"(profils=portails distincts / métier=hors plateforme / rôles) "
|
f"(profils=portails distincts / métier=hors plateforme / rôles) "
|
||||||
f"→ corriger la SPEC")
|
f"→ corriger la SPEC")
|
||||||
|
|
||||||
|
# 1sexies) RBAC_50_ROLES_SPEC.md §4 — colonne « Entité » ancrée sur CLAUDE.md §Entités
|
||||||
|
# La table §4 « Cartographie rôles ↔ entités » liste, en colonne de tête, les entités
|
||||||
|
# porteuses (`entite_principale`) du RBAC : les 7 entités NOMMÉES du mandat (WAF · WA
|
||||||
|
# SRL · AC Arias Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060 QC) + le
|
||||||
|
# périmètre consolidé « Groupe ». Cette colonne a DEUX sources faisant autorité — (i)
|
||||||
|
# CLAUDE.md §Entités (l'orthographe + le descripteur parenthétique de chaque entité
|
||||||
|
# nommée) et (ii) rbac_50_roles.json (l'ENSEMBLE des `entite_principale` réellement
|
||||||
|
# portées par les 50 rôles) — mais AUCUN gate ne la touchait : le §3 gate les comptes
|
||||||
|
# par PORTAIL, le §7 les comptes du run-book, les blocs fiches l'`entite` d'UN rôle ;
|
||||||
|
# tous AVEUGLES à l'identité de CETTE liste. AJOUTER au contrat un rôle porté par une
|
||||||
|
# entité absente de §4, RENOMMER une entité dans CLAUDE.md §Entités (9060 QC→9061 QC)
|
||||||
|
# ou DÉRIVER un descripteur laisse la SPEC contredire le mandat/le contrat EN SILENCE
|
||||||
|
# — « vert trompeur » de la MÊME classe que les entités CONFOTUR (§Entités) ou la
|
||||||
|
# marque SEO org, qu'aucune suite tests/ (FONCTIONS RBAC, jamais la prose de la SPEC)
|
||||||
|
# n'attrape.
|
||||||
|
# GATE : (a) ENSEMBLE des entités de §4 == distinct `entite_principale` du contrat
|
||||||
|
# (set-diff : ni fantôme ni manquante) ; (b) ANCRAGE — chaque entité NOMMÉE de
|
||||||
|
# CLAUDE.md §Entités est une ligne de §4 avec son descripteur parenthétique EXACT
|
||||||
|
# (absence comprise) ; (c) chaque entité nommée du mandat est PORTÉE ≥1× par le
|
||||||
|
# contrat (cm_ents ⊆ contrat). Un claim absent échoue AUSSI (traçabilité #6).
|
||||||
|
if src_roles is not None and spec_rd is not None:
|
||||||
|
try:
|
||||||
|
_cm4 = open("CLAUDE.md", encoding="utf-8").read()
|
||||||
|
_sec4 = re.search(r"^## Entités\s*\n(.*?)(?=^## )", _cm4, re.S | re.M)
|
||||||
|
if not _sec4:
|
||||||
|
raise ValueError("section « ## Entités » introuvable")
|
||||||
|
# (entité → descripteur parenthétique | None), dans l'ordre du mandat
|
||||||
|
cm4 = [(m.group(1).strip(), (m.group(2).strip() if m.group(2) else None))
|
||||||
|
for m in re.finditer(r"\*\*([^*]+?)\*\*(?:\s*\(([^)]+)\))?", _sec4.group(1))]
|
||||||
|
if not cm4:
|
||||||
|
raise ValueError("aucune entité en gras dans §Entités")
|
||||||
|
if len({e for e, _ in cm4}) != len(cm4):
|
||||||
|
raise ValueError(f"entités non uniques : {[e for e, _ in cm4]}")
|
||||||
|
except (OSError, ValueError) as e:
|
||||||
|
bad(f"RBAC SPEC §4 · CLAUDE.md §Entités illisible/inattendu : {e}"); cm4 = None
|
||||||
|
_m4 = re.search(r"## 4\..*?(?=## 5\.)", spec_rd)
|
||||||
|
if _m4 is None:
|
||||||
|
bad(f"RBAC SPEC §4 · section « ## 4. » INTROUVABLE dans {SPEC_RD} "
|
||||||
|
f"(claim absent = régression #6)"); sec4 = None
|
||||||
|
else:
|
||||||
|
sec4 = _m4.group(0)
|
||||||
|
if cm4 is not None and sec4 is not None:
|
||||||
|
# Colonne de tête : « | **Entité** (desc)? | » (le header non-gras et le
|
||||||
|
# séparateur ne matchent pas ; la colonne « Exemples de rôles » n'a pas de gras)
|
||||||
|
spec4 = [(m.group(1).strip(), (m.group(2).strip() if m.group(2) else None))
|
||||||
|
for m in re.finditer(r"\| \*\*([^*|]+?)\*\*(?: \(([^)|]+)\))? \|", sec4)]
|
||||||
|
spec4_names = {n for n, _ in spec4}
|
||||||
|
contract_ents = {r.get("entite_principale") for r in src_roles}
|
||||||
|
# (a) ENSEMBLE §4 == distinct entite_principale du contrat
|
||||||
|
_miss = sorted(contract_ents - spec4_names) # portées mais absentes de §4
|
||||||
|
_phan = sorted(spec4_names - contract_ents) # listées mais jamais portées
|
||||||
|
if _miss or _phan:
|
||||||
|
bad(f"RBAC SPEC §4 · (a) colonne Entité ≠ distinct `entite_principale` du "
|
||||||
|
f"contrat — manquantes={_miss} · fantômes={_phan} → corriger la SPEC "
|
||||||
|
f"(ou régénérer si le contrat a changé)")
|
||||||
|
else:
|
||||||
|
good(f"RBAC SPEC §4 · (a) {len(spec4_names)} entités == distinct "
|
||||||
|
f"`entite_principale` rbac_50_roles.json (ni fantôme ni manquante)")
|
||||||
|
# (b) ANCRAGE — chaque entité NOMMÉE de CLAUDE.md est une ligne §4 · descripteur exact
|
||||||
|
spec4_map = dict(spec4)
|
||||||
|
for _e, _d in cm4:
|
||||||
|
if _e not in spec4_map:
|
||||||
|
bad(f"RBAC SPEC §4 · (b) entité canonique `{_e}` (CLAUDE.md §Entités) "
|
||||||
|
f"ABSENTE de la colonne §4 → SPEC périmée")
|
||||||
|
elif spec4_map[_e] != _d:
|
||||||
|
bad(f"RBAC SPEC §4 · (b) `{_e}` — descripteur §4 {spec4_map[_e]!r} ≠ "
|
||||||
|
f"CLAUDE.md §Entités {_d!r} → aligner sur le mandat")
|
||||||
|
else:
|
||||||
|
good(f"RBAC SPEC §4 · (b) `{_e}` ({_d or 'sans descripteur'}) == "
|
||||||
|
f"CLAUDE.md §Entités")
|
||||||
|
# (c) chaque entité nommée du mandat est PORTÉE ≥1× par le contrat
|
||||||
|
_unused = sorted(e for e, _ in cm4 if e not in contract_ents)
|
||||||
|
if _unused:
|
||||||
|
bad(f"RBAC SPEC §4 · (c) entités du mandat JAMAIS portées par un rôle : "
|
||||||
|
f"{_unused} → entité décorative (contrat ou §Entités à revoir)")
|
||||||
|
else:
|
||||||
|
good(f"RBAC SPEC §4 · (c) les {len(cm4)} entités nommées du mandat sont "
|
||||||
|
f"portées ≥1× par le contrat")
|
||||||
|
|
||||||
# 2) apply_plan — ligne « Génération réelle » agrégeant les 3 volets ------------
|
# 2) apply_plan — ligne « Génération réelle » agrégeant les 3 volets ------------
|
||||||
AP_RD = f"{D}/rbac/apply_plan/README.md"
|
AP_RD = f"{D}/rbac/apply_plan/README.md"
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user