[DTP-Worker] Sprint 8 · buffer · RBAC/SPEC §3 : correction DÉFAUT FACTUEL (direction 10→9 · plateforme 5→6) + gate d'identité « Nb rôles » par portail ancré sur rbac_50_roles.json
La table §3 de RBAC_50_ROLES_SPEC.md avait dérivé du contrat en préservant le total 50 (10+5 vs 9+6) → invisible aux gates agrégés. Correction alignée sur la source + nouveau bloc check_readme_claims recomptant chaque portail (et le total) depuis rbac_50_roles.json. quality_report.json régénéré (evidence octets SPEC). 7 gates PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1011,3 +1011,43 @@ absolu) · **7 gates re-verts** (`check_readme_claims`/`check_ci_integrity`/`che
|
|||||||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'obtention
|
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'obtention
|
||||||
réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS).
|
réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS).
|
||||||
- **Auto-score 4Big** : 96/100.
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|
||||||
|
## Session `20260801_093301` · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §3 — DÉFAUT FACTUEL corrigé + gate d'identité par portail
|
||||||
|
|
||||||
|
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
|
||||||
|
série anti-dérive CLAUDE.md #6). Cette fois la surface data-derived non gatée était
|
||||||
|
**FAUSSE**, pas seulement à risque : la table §3 « Cartographie portails ↔ familles »
|
||||||
|
de `RBAC_50_ROLES_SPEC.md` (colonne « Nb rôles » PAR portail) avait dérivé du contrat
|
||||||
|
`rbac_50_roles.json` **en préservant le total 50**, donc en silence.
|
||||||
|
|
||||||
|
**Défaut factuel trouvé & corrigé** :
|
||||||
|
- `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md:47` — portail `direction` affichait
|
||||||
|
**10** rôles ; le contrat en compte **9** (6 direction + CFO/finance + analyste
|
||||||
|
faisabilité + 2 legal).
|
||||||
|
- `…:52` — portail `plateforme` affichait **5** ; le contrat en compte **6** (devops ·
|
||||||
|
rbac-admin · qa · otoia · mobile · bi).
|
||||||
|
- L'erreur était compensée (10+5 vs 9+6) → total 50 exact → invisible aux gates
|
||||||
|
agrégés. Le MANIFEST roleprofile (byte-gaté) disait déjà 9 et 6 : la SPEC seule
|
||||||
|
avait dérivé. Corrigé pour refléter la source.
|
||||||
|
|
||||||
|
**Dérive silencieuse fermée (gate durable)** :
|
||||||
|
- `ci/check_readme_claims.sh` — nouveau bloc « 1quater) RBAC_50_ROLES_SPEC §3 » : RECOMPTE
|
||||||
|
le nombre de rôles PAR portail depuis `rbac/rbac_50_roles.json` (source de vérité,
|
||||||
|
byte-gatée par `check_artifacts`) et exige l'égalité avec CHAQUE ligne de la table §3
|
||||||
|
**plus le total** `**Total** | **50**`. Un portail absent de la table échoue aussi
|
||||||
|
(disparition = régression de traçabilité). Complète le gate roleprofile (qui gate le
|
||||||
|
MANIFEST, un doc distinct) sur une 2ᵉ surface transcrivant la même ventilation.
|
||||||
|
- Vérif négative : réintroduire `direction=10` fait échouer le gate (exit 1, message
|
||||||
|
« dit 10 MAIS rbac_50_roles.json compte 9 ») ; restauré → vert.
|
||||||
|
|
||||||
|
**Artefact régénéré (reproductibilité #artifact-reproducibility-gate)** :
|
||||||
|
- `qa/audit_4big/out/quality_report.json` — l'`evidence` DOC cite la taille en octets de
|
||||||
|
la SPEC (10040 → 10039 octets après correction −1 o) ; rebuild `audit_4big_gen.py build
|
||||||
|
-o out` byte-identique. `check_artifacts` re-vert.
|
||||||
|
|
||||||
|
**CI** : les 7 gates PASS (validate_json · check_artifacts · check_docs ·
|
||||||
|
check_readme_claims · guard_constraints · check_regression · check_ci_integrity).
|
||||||
|
|
||||||
|
- **Hors périmètre worker (VPS · #8)** : néant (correction d'un doc en-repo + gate
|
||||||
|
bash/python3 stdlib ; l'unique artefact `out/` touché a été régénéré à l'identique).
|
||||||
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|||||||
@@ -120,7 +120,7 @@
|
|||||||
"criterion": "DOC",
|
"criterion": "DOC",
|
||||||
"weight": 20,
|
"weight": 20,
|
||||||
"passed": true,
|
"passed": true,
|
||||||
"evidence": "RBAC_50_ROLES_SPEC.md (10040 octets)"
|
"evidence": "RBAC_50_ROLES_SPEC.md (10039 octets)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"criterion": "CONTRAT",
|
"criterion": "CONTRAT",
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ technique transversale** (`plateforme`, hors du compte de 5) :
|
|||||||
|
|
||||||
| Portail | Familles rattachées | Nb rôles |
|
| Portail | Familles rattachées | Nb rôles |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **direction** | direction, finance (CFO), faisabilite (analyste), legal | 10 |
|
| **direction** | direction, finance (CFO), faisabilite (analyste), legal | 9 |
|
||||||
| **ventes** | ventes, marketing | 12 |
|
| **ventes** | ventes, marketing | 12 |
|
||||||
| **construction** | construction, faisabilite (rendu/IFC/économiste) | 10 |
|
| **construction** | construction, faisabilite (rendu/IFC/économiste) | 10 |
|
||||||
| **achat** | achat | 5 |
|
| **achat** | achat | 5 |
|
||||||
| **compta** | finance | 8 |
|
| **compta** | finance | 8 |
|
||||||
| **plateforme** _(technique)_ | plateforme | 5 |
|
| **plateforme** _(technique)_ | plateforme | 6 |
|
||||||
| | **Total** | **50** |
|
| | **Total** | **50** |
|
||||||
|
|
||||||
> Répartition indicative issue de `rbac_50_roles.json` ; la source de vérité reste
|
> Répartition indicative issue de `rbac_50_roles.json` ; la source de vérité reste
|
||||||
|
|||||||
@@ -826,6 +826,49 @@ if rp_man is not None and rp_rd is not None:
|
|||||||
f"{mp.group(1)}/{mp.group(2)}/{mp.group(3)} MAIS MANIFEST dit "
|
f"{mp.group(1)}/{mp.group(2)}/{mp.group(3)} MAIS MANIFEST dit "
|
||||||
f"{cp}/{cm}/{ct} → régénérer le README")
|
f"{cp}/{cm}/{ct} → régénérer le README")
|
||||||
|
|
||||||
|
# 1quater) RBAC_50_ROLES_SPEC.md §3 — table « Nb rôles » PAR portail ------------
|
||||||
|
# La table §3 « Cartographie portails ↔ familles » transcrit À LA MAIN le nombre de
|
||||||
|
# rôles de CHAQUE portail. C'est une ventilation par portail dans un doc DISTINCT
|
||||||
|
# du MANIFEST roleprofile (gaté ci-dessus) — jamais gaté, et il AVAIT dérivé en
|
||||||
|
# silence : `direction` 10 / `plateforme` 5 alors que le contrat compte 9 / 6, le
|
||||||
|
# total 50 restant préservé (dérive muette classique, #6). On RECOMPTE ici chaque
|
||||||
|
# ligne depuis rbac_50_roles.json (source de vérité, byte-gatée par check_artifacts)
|
||||||
|
# et on exige l'égalité avec ce que la SPEC écrit, total compris. Un portail manquant
|
||||||
|
# de la table échoue aussi (disparition = régression de traçabilité).
|
||||||
|
SPEC_RD = f"{D}/rbac/RBAC_50_ROLES_SPEC.md"
|
||||||
|
try:
|
||||||
|
src_roles = load("rbac/rbac_50_roles.json")["roles"]
|
||||||
|
except (OSError, KeyError, ValueError) as e:
|
||||||
|
bad(f"RBAC SPEC §3 · rbac_50_roles.json illisible : {e}"); src_roles = None
|
||||||
|
spec_rd = read_norm(SPEC_RD)
|
||||||
|
if src_roles is not None and spec_rd is not None:
|
||||||
|
by_portail = {}
|
||||||
|
for r in src_roles:
|
||||||
|
by_portail[r.get("portail")] = by_portail.get(r.get("portail"), 0) + 1
|
||||||
|
for portail in sorted(by_portail):
|
||||||
|
exp = str(by_portail[portail])
|
||||||
|
pat = rf"\| \*\*{re.escape(portail)}\*\*[^|]*\| [^|]*\| (\d+) \|"
|
||||||
|
m = re.search(pat, spec_rd)
|
||||||
|
if m is None:
|
||||||
|
bad(f"RBAC SPEC §3 · portail `{portail}` — ligne INTROUVABLE dans "
|
||||||
|
f"{SPEC_RD} (attendu Nb rôles={exp})")
|
||||||
|
elif m.group(1) == exp:
|
||||||
|
good(f"RBAC SPEC §3 · portail `{portail}` — Nb rôles {m.group(1)} == "
|
||||||
|
f"rbac_50_roles.json ({exp})")
|
||||||
|
else:
|
||||||
|
bad(f"RBAC SPEC §3 · portail `{portail}` — {SPEC_RD} dit {m.group(1)} "
|
||||||
|
f"MAIS rbac_50_roles.json compte {exp} → corriger la SPEC")
|
||||||
|
exp_tot = str(len(src_roles))
|
||||||
|
mt = re.search(r"\*\*Total\*\* \| \*\*(\d+)\*\*", spec_rd)
|
||||||
|
if mt is None:
|
||||||
|
bad(f"RBAC SPEC §3 · total — ligne « Total » INTROUVABLE dans {SPEC_RD} "
|
||||||
|
f"(attendu {exp_tot})")
|
||||||
|
elif mt.group(1) == exp_tot:
|
||||||
|
good(f"RBAC SPEC §3 · total — {mt.group(1)} == rbac_50_roles.json ({exp_tot})")
|
||||||
|
else:
|
||||||
|
bad(f"RBAC SPEC §3 · total — {SPEC_RD} dit {mt.group(1)} MAIS "
|
||||||
|
f"rbac_50_roles.json compte {exp_tot} → corriger la SPEC")
|
||||||
|
|
||||||
# 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