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 unDossierdans le tenant du client (demande.tenant_schema), avec un lien retourdemande_id. - Dossiers Labo/Studio :
GET /api/dossiersne 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, commeapercu) ; 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ésdemande_idalimentent 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. Latraceest désormais genre-aware (partition par table + scatter, commecalculer_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 desDossierdu 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 undemande_id: pas de promotion fake → réel. La seule façon dont unDossierobtient undemande_idreste sa création par validation d'une photo de demande (_creer_dossier_calculable) ; aucun endpoint ne réaffectedemande_idaprè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.