From 7ce0f7b1e733b2757133c33d1fc4d09cf105ed3c Mon Sep 17 00:00:00 2001 From: Claude Code DTP Worker Date: Sat, 1 Aug 2026 09:39:54 +0000 Subject: [PATCH] =?UTF-8?q?[DTP-Worker]=20Sprint=208=20=C2=B7=20buffer=20?= =?UTF-8?q?=C2=B7=20RBAC/SPEC=20=C2=A73=20:=20correction=20D=C3=89FAUT=20F?= =?UTF-8?q?ACTUEL=20(direction=2010=E2=86=929=20=C2=B7=20plateforme=205?= =?UTF-8?q?=E2=86=926)=20+=20gate=20d'identit=C3=A9=20=C2=AB=20Nb=20r?= =?UTF-8?q?=C3=B4les=20=C2=BB=20par=20portail=20ancr=C3=A9=20sur=20rbac=5F?= =?UTF-8?q?50=5Froles.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- 05_activity_log/2026-08-01.md | 40 +++++++++++++++++ .../qa/audit_4big/out/quality_report.json | 2 +- .../rbac/RBAC_50_ROLES_SPEC.md | 4 +- ci/check_readme_claims.sh | 43 +++++++++++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/05_activity_log/2026-08-01.md b/05_activity_log/2026-08-01.md index 8bbda95..a6e1095 100644 --- a/05_activity_log/2026-08-01.md +++ b/05_activity_log/2026-08-01.md @@ -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 réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS). - **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. diff --git a/05_deliverables_mvp/qa/audit_4big/out/quality_report.json b/05_deliverables_mvp/qa/audit_4big/out/quality_report.json index d228dde..137d66f 100644 --- a/05_deliverables_mvp/qa/audit_4big/out/quality_report.json +++ b/05_deliverables_mvp/qa/audit_4big/out/quality_report.json @@ -120,7 +120,7 @@ "criterion": "DOC", "weight": 20, "passed": true, - "evidence": "RBAC_50_ROLES_SPEC.md (10040 octets)" + "evidence": "RBAC_50_ROLES_SPEC.md (10039 octets)" }, { "criterion": "CONTRAT", diff --git a/05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md b/05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md index 5fcc142..3e8e5a9 100644 --- a/05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md +++ b/05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md @@ -44,12 +44,12 @@ technique transversale** (`plateforme`, hors du compte de 5) : | 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 | | **construction** | construction, faisabilite (rendu/IFC/économiste) | 10 | | **achat** | achat | 5 | | **compta** | finance | 8 | -| **plateforme** _(technique)_ | plateforme | 5 | +| **plateforme** _(technique)_ | plateforme | 6 | | | **Total** | **50** | > Répartition indicative issue de `rbac_50_roles.json` ; la source de vérité reste diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index a990ffc..11ec6d8 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -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"{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 ------------ AP_RD = f"{D}/rbac/apply_plan/README.md" try: