[DTP-Worker] Sprint 2 · Générateur Role Profile par portail (RBAC 50 rôles → 6 bundles assignables ERPNext v15)

- roleprofile_gen/ : profilelib (frappe Role Profile + Has Role natif v15,
  builder déterministe), roleprofile.schema.json, CLI build/validate refusant
  d'écrire si invariant cassé, 11 tests stdlib, README, .gitignore (out/).
- 6 profils couvrant les 50 rôles de façon bijective (5 portails métier +
  console technique plateforme). Aucun DocType custom (que du natif).
- Anti-invention #6 : rôles 100 % issus du contrat, nom de profil = convention
  déterministe dérivée de la clé portail.
- CI : job rbac-roleprofile-tests ajouté au gate (.gitea/workflows/ci.yml).
- Doc : SPEC §7 ét.5 + encart livré. Daily report session 8.
- Régression : 83 tests verts (72 + 11). Gate CI local vert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-30 04:04:04 +00:00
parent b1b011ab81
commit 70e022ccd6
11 changed files with 879 additions and 2 deletions
@@ -0,0 +1,92 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://oto-enterprise-os-dtp/rbac/roleprofile.schema.json",
"title": "Bundle Role Profile généré depuis rbac_50_roles.json",
"description": "Contrat de sortie de roleprofile_gen.py. Validé par le validateur maison Publiciste (zéro pip) dans le job CI rbac-roleprofile-tests. Un Role Profile ERPNext v15 natif par portail distinct du contrat ; chaque profil bundle les rôles (Has Role) de son portail. Couverture bijective des 50 rôles.",
"type": "object",
"additionalProperties": false,
"required": ["manifest", "role_profile"],
"properties": {
"manifest": {
"type": "object",
"additionalProperties": false,
"required": [
"generated_from",
"source_version",
"cible_rbac_roles",
"counts",
"profiles"
],
"properties": {
"generated_from": { "type": "string", "const": "rbac_50_roles.json" },
"source_version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
"cible_rbac_roles": { "type": "integer", "const": 50 },
"counts": {
"type": "object",
"additionalProperties": false,
"required": [
"role_profiles",
"portails_metier",
"portails_techniques",
"roles_couverts"
],
"properties": {
"role_profiles": { "type": "integer", "minimum": 1 },
"portails_metier": { "type": "integer", "minimum": 0 },
"portails_techniques": { "type": "integer", "minimum": 0 },
"roles_couverts": { "type": "integer", "minimum": 50, "maximum": 50 }
}
},
"profiles": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/definitions/profile_meta" }
}
}
},
"role_profile": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/definitions/role_profile_fixture" }
}
},
"definitions": {
"profile_meta": {
"type": "object",
"additionalProperties": false,
"required": ["portail", "role_profile", "metier", "nb_roles"],
"properties": {
"portail": { "type": "string", "minLength": 1 },
"role_profile": { "type": "string", "pattern": "^OTO Portail " },
"metier": { "type": "boolean" },
"nb_roles": { "type": "integer", "minimum": 1 }
}
},
"role_profile_fixture": {
"type": "object",
"additionalProperties": false,
"required": ["doctype", "name", "role_profile", "roles"],
"properties": {
"doctype": { "type": "string", "const": "Role Profile" },
"name": { "type": "string", "pattern": "^OTO Portail " },
"role_profile": { "type": "string", "pattern": "^OTO Portail " },
"roles": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/definitions/has_role_row" }
}
}
},
"has_role_row": {
"type": "object",
"additionalProperties": false,
"required": ["doctype", "parentfield", "parenttype", "role"],
"properties": {
"doctype": { "type": "string", "const": "Has Role" },
"parentfield": { "type": "string", "const": "roles" },
"parenttype": { "type": "string", "const": "Role Profile" },
"role": { "type": "string", "pattern": "^OTO " }
}
}
}
}