[DTP-Worker 20260802_133549] Sprint 8 · buffer · roles_couverts (« 44 ») : enquête anti-dérive → NON-surface prouvée (3 manifestes importent la même fonction roles_by_portail sur l'unique contrat rbac_50_roles.json → transitivement mono-sourcé, un cross-gate serait redondant #5, contra le cross-check RBAC-50 qui re-dérive) + documentation du garde-fou dans ci/README.md pour prévenir un gate N+1 redondant

This commit is contained in:
Claude Code DTP Worker
2026-08-02 13:45:38 +00:00
parent 8dcedb092c
commit 576e78b6a9
2 changed files with 58 additions and 0 deletions
+38
View File
@@ -1371,3 +1371,41 @@ figés à 13 ⇒ les **5 surfaces head-docs+GAP mordent** « dit 13 MAIS 03_agen
(preuve que l'autorité est le git-index, pas la cohérence interne). Arbre restauré. **7 gates
re-joués → exit 0** : check_ci_integrity · check_artifacts · check_docs · check_readme_claims ·
check_regression · guard_constraints · validate_json. Aucune commande touchant au VPS.
## Sprint 8 · buffer · `roles_couverts` (« 44 ») — enquête anti-dérive → **non-surface prouvée** + documentation du garde-fou mono-source (évite un gate N+1 redondant #5)
**Contexte** — Série anti-dérive Sprint 8. Un balayage des comptes numériques a
exhumé un candidat de **cohérence croisée** : le total « **44 rôles couverts par les
5 portails** » apparaît, identique, dans **trois** manifestes byte-gatés
(`frontend/portails`, `frontend/chat_otoia`, `mobile/app_config` ·
`counts.roles_couverts=44`). Chaque « 44 » vif n'est ancré qu'à SON propre manifeste
(mobile README · `demo/scenarios/out/run_sheet.md`). Aucun `ci/*.sh` ne compare les
trois entre eux. Hypothèse initiale : surface non couverte, analogue au cross-check
RBAC « 50 » (`rbac/apply_plan/rbac_apply_plan.py:149` exige
`fixtures==userperm==roleprofile`).
**Enquête — l'analogie RBAC-50 ne tient PAS** (mémoire *fix-vs-gate-transitively-
protected-constant*). Le « 50 » EST cross-gaté car ses trois rendus **re-dérivent**
chacun leur décompte avec leur **propre** logique. Ici, au contraire, les trois
modules **importent la même** fonction de partition : `mobilelib/deps.py:77` et
`chatlib/deps.py:84` font tous deux `roles_by_portail = portails_builder.roles_by_portail`
(unique définition `frontend/portails/wslib/builder.py:36`), appliquée à l'**unique**
contrat `rbac/rbac_50_roles.json` (une seule copie de chaque dans `git ls-files`).
`roles_couverts` est donc **transitivement mono-sourcé** (code partagé + donnée
partagée) : les trois totaux **ne peuvent diverger** que si cette fonction ou ce
contrat change — auquel cas les trois changent ensemble et `check_artifacts`
(reproductibilité byte-à-byte) vérifie chaque rebuild. Un gate « 44==44==44 » serait
**redondant (#5)** et masquerait la vraie garantie (le partage de code), pas une
surface à protéger.
**Livraison — DOCUMENTATION du garde-fou, pas un gate.** Ajout d'un bloc sourcé
« **Non-surface documentée — `roles_couverts` N'EST PAS cross-gaté, à dessein** » dans
`ci/README.md` (entre le bloc mobile Expo et le bloc CONFOTUR) : cite les 3 imports
partagés + le contrat unique + contraste explicite avec le cross-check RBAC-50, pour
qu'une session future ne rajoute PAS le gate redondant. Chemins en backticks (non
link-checkés, cf. mémoire *backtick-path-escapes-check-docs*).
**Vérif** — 4 citations de lignes vérifiées exactes (`deps.py:77`, `deps.py:84`,
`wslib/builder.py:36`, `rbac_apply_plan.py:149`). **7 gates re-joués → exit 0** :
guard_constraints · validate_json · check_docs · check_artifacts · check_regression ·
check_ci_integrity · check_readme_claims. Aucune commande touchant au VPS.