Aller au contenu

ADR-0019 — Deux bancs : étude (réel, journalisé) vs expérimentation (fake, labellisé)

Statut Accepté — 2026-06-19
Portée Séparation des deux lieux de calcul : Plan de travail de l'étude (cabinet/etude) vs Labo / Studio. Modèle Dossier, endpoints /api/dossiers et /api/demandes/{id}/apercu
Décision La donnée client réelle ne s'éprouve qu'au banc d'étude (cross-tenant scopé + journalisé) ; le banc d'expérimentation (Labo/Studio) reste sur de la donnée expérimentale, et accepte enfin l'upload de photos RH labellisées « Expérimental »
Réf. ADR-0005 — Tenant = groupe ; ADR-0006 — Personas staff cross-tenant ; ADR-0007 — Vues par persona & instigation ; ADR-0004 — Labo (versionnement) ; ADR-0018 — Observatoire (étage 3)

🎯 À retenir

Deux bancs, deux régimes de données. Le banc d'étude (Plan de travail) éprouve un jeu contre la vraie population du client, via un accès cross-tenant scopé par l'assignation et journalisé (ADR-0006). Le banc d'expérimentation (Labo, Studio) reste hors projet : données expérimentales, tenant cabinet, pour apprendre et jouer le moteur. On comble son vide actuel (coincé sur K1) par l'upload de photos RH expérimentales, et on rend visible une frontière qui existe déjà dans la donnée : un dossier est « Expérimental » ssi demande_id IS NULL (badge dérivé, pas de colonne). Invariant : un dossier expérimental ne peut jamais acquérir un demande_id.

Contexte

Observé en QA (parcours junior). Un actuaire junior crée et justifie un jeu d'hypothèses à l'Atelier, puis veut l'éprouver contre la population du client. Il va naturellement au Labo (sensibilités, forks « et si 3,5 % », pont OHADA) et au Studio. Mais :

  • le Labo affiche « Aucun dossier dans ce tenant » et ne propose que « Préparer le labo (cas-témoin K1) » ;
  • le Studio ne liste sous « Dossiers RH » que Dossier K1 (1 salarié).

La raison est structurelle, pas accidentelle. Il existe deux stocks de population, reliés par un pont à sens unique :

  • Photo de demande (DocumentRH) → à la validation, _creer_dossier_calculable (backend/app/demandes.py) crée automatiquement un Dossier dans le tenant du client (demande.tenant_schema), avec un lien retour demande_id.
  • Dossiers Labo/Studio : GET /api/dossiers ne renvoie que les dossiers du tenant de session. Or le junior (staff) siège dans le tenant cabinet (BFEV), jamais celui du client. Il voit donc K1 et le bac à sable cabinet, jamais la population d'Acme.

Le seul point de rencontre sanctionné entre un jeu (cabinet) et la population client, c'est le Plan de travail de l'étude, via POST /api/demandes/{id}/apercu : cet endpoint traverse le tenant (charge le dossier depuis le tenant client) mais sous garde d'assignation + journalisation (ADR-0006, garde-fou 2). Le design a délibérément cantonné l'accès aux données client au workflow d'étude, et gardé Labo/Studio comme bac à sable privé du cabinet.

Le vrai manque n'est donc pas « le dossier est inaccessible ». C'est double : (a) le Plan de travail fait un calcul unique, pas l'exploration du Labo (sensibilités/OHADA/forks) sur la vraie population ; (b) le banc d'expérimentation est coincé sur K1 — pas moyen pour le junior d'éprouver le moteur sur une population de son cru.

Décision

  • D-BANC-1 — Deux bancs, deux régimes de données. On acte explicitement : banc d'étude (Plan de travail) = donnée client réelle, cross-tenant scopé par l'assignation + journalisé, dans le mandat ; banc d'expérimentation (Labo, Studio) = données expérimentales, tenant cabinet, hors mandat. Le Labo/Studio ne servent jamais de point d'accès à la donnée client.
  • D-BANC-2 — L'exploration du réel vit au banc d'étude. Les capacités d'exploration (sensibilités, pont OHADA, brouillon/fork « et si X % ») sur la population client se font dans le Plan de travail, via une résolution scopée par l'assignation (demande_visible, comme apercu) ; l'accès cross-tenant est journalisé (une trace par salve, dédupliquée — ADR-0006). On n'introduit pas la donnée client dans le Labo/Studio. (Livré : sensibilités + pont OHADA + variante « et si X% » (brouillon). Étendu : waterfall / trace / tornado scopés demande_id alimentent la « Synthèse du calcul » du reviewer — vue globale Studio (DBO, OHADA, waterfall, tornado, détail par salarié) en TÊTE de la revue senior/externe, avant le détail des hypothèses. La DBO de TÊTE vient du résultat FIGÉ à la soumission (/workflow.resultat, genre/multi-aware), jamais d'un recalcul. La trace est désormais genre-aware (partition par table + scatter, comme calculer_dbo_jeu) : le détail par salarié d'un jeu genré est exact et somme à la DBO figée.)
  • D-BANC-3 — Upload expérimental au banc d'expérimentation. Le Labo/Studio acceptent l'upload de photos RH expérimentales (mêmes CSV, même pipeline ingestion.valider() — le junior apprend du même coup le comportement d'ingestion sur les anomalies). Elles créent des Dossier du tenant cabinet, demande_id = NULL.
  • D-BANC-4 — Le label « Expérimental » est DÉRIVÉ, pas stocké. Un dossier est expérimental ssi demande_id IS NULL (vérité déjà en base : NULL = créé hors demande). Badge affiché partout (Labo, Studio, sélecteurs). Pas de nouvelle colonne → on évite le piège _COLONNES_AJOUTEES (sinon 500 prod). K1 reste le « cas-témoin » (sous-cas notable du banc d'expérimentation).
  • D-BANC-5 — Invariant de non-promotion. Un dossier expérimental (demande_id NULL) ne doit jamais acquérir un demande_id : pas de promotion fake → réel. La seule façon dont un Dossier obtient un demande_id reste sa création par validation d'une photo de demande (_creer_dossier_calculable) ; aucun endpoint ne réaffecte demande_id après coup.
  • D-BANC-6 — Isolation par construction, label pour l'humain. L'isolation est structurelle : un dossier expérimental vit dans le tenant cabinet, sans demande_id, n'entre pas dans le workflow d'étude, n'est jamais figé, n'atteint jamais le client. Le rayon de souffle d'un faux dossier est donc cognitif, pas opérationnel. On n'ajoute pas de verrou dur supplémentaire : le badge (D-BANC-4) + l'invariant (D-BANC-5) suffisent. (Anti-sur-ingénierie, posé explicitement.)

Conséquences

  • Positif. Le junior obtient enfin un vrai bac à sable (au-delà de K1) pour se faire la main et tester le moteur, sans jamais toucher la donnée client. L'exploration sérieuse, elle, se fait sur la vraie population, tracée et dans le mandat. La frontière d'isolation (ADR-0006) reste intacte et devient explicite dans l'UI.
  • Deux chantiers de réalisation, dans cet ordre. (1) Upload expérimental + badge au Labo/Studio — le plus petit, débloque le junior tout de suite. (2) Exploration dans le Plan de travail (sensibilités/OHADA/fork sur apercu) — plus gros, suit.
  • Limite assumée (transitoire). Tant que le chantier (2) n'est pas livré, le junior éprouve le réel par le calcul unique du Plan de travail (pas encore sensibilités/forks sur la donnée client). C'est nommé, pas masqué.

Déclencheur de réexamen (pré-enregistré)

  • Besoin de comparer plusieurs clients / cross-tenant (ex. benchmark) → ne relève PAS de cet ADR : il rejoint le garde-fou 4 et l'observatoire (ADR-0018), nouvel ADR dédié.
  • Le label dérivé devient insuffisant (ex. on voudrait des dossiers expérimentaux dans un tenant client, ou un état « cas-témoin » distinct de « expérimental ») → introduire un flag explicite sur Dossier (avec entrée _COLONNES_AJOUTEES) et re-statuer.