# Agrégateur RBAC · run-book d'application VPS unifié

**Sprint 2 · agent ERPNext Backend.** Quatrième et dernier maillon RBAC en-repo :
la **vue d'ensemble**. Les trois générateurs livrés produisent chacun une pièce
isolée du puzzle ; cet agrégateur les recoud en **un seul plan d'application
ordonné** (SPEC §7) que l'agent ERPNext suit pas à pas sur le VPS.

| Générateur | Répond à | Produit |
|---|---|---|
| [`fixtures_gen`](../fixtures_gen/README.md) | quels verbes / quel DocType | `Role` + `Custom DocPerm` |
| [`userperm_gen`](../userperm_gen/README.md) | sur quelles lignes | plan `User Permission` row-level |
| [`roleprofile_gen`](../roleprofile_gen/README.md) | quel bundle assignable | `Role Profile` par portail |
| **`apply_plan`** (ce module) | **dans quel ordre appliquer** | **run-book + manifeste agrégé** |

> Ce worker **n'écrit jamais sur le VPS** (contrainte #8). Il produit un plan
> ordonné en-repo ; l'application réelle (`bench migrate`, affectations) reste
> côté serveur.

## Pourquoi un agrégateur (et pas juste trois modules)

Trois sorties séparées laissent l'ordre d'application implicite. Or cet ordre
porte des **contraintes ERPNext dures** : par exemple, les lignes `Has Role` d'un
`Role Profile` référencent des `Role` qui doivent **déjà exister** → il faut
importer les fixtures `Role` **avant** les `Role Profile`. Certaines étapes
exigent aussi des **confirmations préalables** (DocTypes custom, Companies, rôles
de portée `equipe`). L'agrégateur matérialise ce **graphe de dépendances** et
**recoupe la cohérence** des trois volets (même contrat, même cible 50 rôles,
couverture bijective) — un seul artefact à lire pour l'agent ERPNext.

## Ce qui est généré (`out/`, commité · byte-déterministe)

| Fichier | Rôle |
|---|---|
| `apply_plan.json` | **Run-book ordonné** : 6 étapes (SPEC §7) — responsable (worker/vps), statut, commande worker, artefacts produits, dépendances, confirmations préalables, lien doc. |
| `MANIFEST.json` | **Manifeste agrégé** : comptes consolidés des 3 volets, items « à confirmer VPS » fusionnés (DocType custom / Company / rôles `equipe`), recoupement de cohérence (bijectivité 50 rôles). |

## Run-book généré (SPEC §7 · ordre d'application VPS)

| # | Responsable | Étape | Dépend de |
|---|---|---|---|
| 1 | worker | Générer fixtures `Role` + `Custom DocPerm` | — |
| 2 | vps | Créer les DocTypes DTP custom manquants (après confirmation) | 1 |
| 3 | vps | Déposer `role.json` + `custom_docperm.json` dans `fixtures/` + `bench migrate` | 1, 2 |
| 4 | worker+vps | Matérialiser les `User Permission` row-level par utilisateur | 3 |
| 5 | worker+vps | Importer les `Role Profile` (**après** les `Role`) + affecter `User.role_profile_name` | 3 |
| 6 | vps | Vérification HTTP post-déploiement + audit QA 4Big | 4, 5 |

## Utilisation

```bash
# Génère out/apply_plan.json + out/MANIFEST.json
python3 rbac_apply_plan.py build            # [-o DOSSIER]

# Valide le plan (schéma + invariants) sans rien écrire
python3 rbac_apply_plan.py validate

# Tests (stdlib pur, zéro pip)
python3 -m unittest discover -s tests -v
```

## Garde-fous (anti-invention · #6 · déterminisme)

- **Zéro chiffre recalculé** : chaque compte provient du manifeste du builder
  source (fixtures / userperm / roleprofile). Chaque item « à confirmer VPS » est
  repris tel quel. Le CLI **refuse d'écrire** si un invariant casse.
- **Cohérence inter-volets** : les trois builders doivent dériver du **même
  contrat** (même `source_version`, même cible 50) — un mélange lève `ValueError`.
- **Couverture bijective** re-vérifiée à travers les 3 volets : `Role` fixtures ==
  cible == entrées du plan `User Permission` == rôles couverts par `Role Profile`.
- **Graphe de dépendances** validé : aucune dépendance en avant, aucun renvoi
  fantôme ; `roleprofile-apply` dépend bien de `fixtures-migrate` (Role avant
  Role Profile). Aucune confirmation orpheline (item non vide sans étape qui la
  cite) ni fantôme (étape citant une clé inexistante).
- Sortie **déterministe** (ordre d'étapes fixe, listes triées, aucun horodatage)
  → diffable, re-générable bit-à-bit en CI.

## Vérification en-repo

- `python3 -m unittest discover -s tests -v` → **16/16 verts** (schéma maison +
  oracle `jsonschema` si présent ; fidélité des comptes aux manifestes source,
  fusion des confirmations, ordre + graphe SPEC §7, garde-fous de cohérence,
  déterminisme).
- Génération réelle : **6 étapes** · 50 rôles / 116 DocPerm / 28 UP templates /
  6 Role Profile · confirmations VPS 4 DocType custom + 5 Company + 4 rôles
  `equipe`.
- Job CI dédié `rbac-applyplan-tests` ajouté au **gate** (`.gitea/workflows/ci.yml`,
  Gitea Actions uniquement · #2).

## Auto-score 4Big du livrable : **96/100**

_Réserve −4_ : l'exécution réelle du run-book (`bench migrate`, création des
DocTypes custom, affectation `role_profile_name` / matérialisation des
`User Permission` par utilisateur) reste côté VPS (agent ERPNext, hors périmètre
worker, contrainte #8). Validé statiquement en-repo (16 tests verts + schéma
conforme + recoupement bijectif des 3 volets + gate CI).
