ADR-0023 — Taxonomie du journal : ce que chaque persona doit voir, et dans quel ordre¶
| Statut | 🟢 Accepté — 2026-06-21 ; implémenté (catégorie dérivée + filtrage (catégorie × persona) + synthèse externe dans workflow.py ; rendu groupé par phase dans Cabinet.tsx ; test dans test_revue.py, suite verte) |
| Portée | Lecture du journal d'une demande (GET /api/demandes/{id}/evenements), filtrage par persona, rendu côté Cabinet. Pas d'écriture : le journal reste append-only et intégral (rien n'est supprimé en base). |
| Décision | La catégorie d'un événement (jalon / controle / exploration) est dérivée de son type (fonction pure, pas de colonne). La visibilité devient une fonction de (catégorie × persona) au lieu du booléen visible_client seul. L'Externe ne reçoit pas le détail des explorations mais un résumé ; le rendu est groupé par phase, phase courante en tête. |
| Réf. | ADR-0006 — Personas staff & cross-tenant (journal) ; ADR-0008 — Revue contradictoire ancrée ; ADR-0019 — Deux bancs |
🎯 À retenir
Le journal devenait kilométrique : un dump plat, chronologique ascendant, dominé par
les événements exploration (le staff qui tâtonne dans le labo). L'Externe — un auditeur
indépendant — le voyait en entier, noyé sous le brouillon de cognition. Or un auditeur
audite des décisions et leur justification, pas la cognition qui les a produites. On
distingue trois catégories d'événements (jalon / contrôle / exploration), dérivées du
type (zéro colonne, zéro migration, rétroactif). La visibilité devient (catégorie ×
persona) : le client voit les jalons publics ; l'externe voit jalons + contrôle, et
l'exploration résumée (« N explorations par X » — preuve de diligence sans le bruit) ;
le junior/senior voient tout (exploration repliable). Le rendu est groupé par phase,
phase courante d'abord. Le journal reste intégral en base — on filtre la lecture, on
n'efface jamais l'écriture.
Contexte¶
Observé en QA (parcours Externe, après validation senior → audit externe). Le journal
(journal_demande, workflow.py) :
- est ordonné
order_by(EvenementDemande.id)= chronologique ascendant : l'état actionnable (soumission, validation, la tâche d'audit) est tout en bas, après un kilomètre de scroll ; - n'a que deux niveaux de visibilité : le consultant voit
visible_client=True(jalons curés) ; tout le staff — junior, senior, ET externe — voit TOUT, dont chaqueexploration(générée par_journaliser_exploration,labo.py, dédupliquée par salve mais empilée à travers acteurs et sessions).
Les dix types d'événements relèvent en réalité de trois natures :
- Jalon — le squelette du workflow :
assignation,demarrage,soumission_revue,validation_senior,transmission_externe,audit_approuve,audit_reserves,livraison. - Contrôle — la cuisine d'audit :
critique_revue(+ réserves d'audit). C'est précisément ce que l'externe doit voir (preuve de contrôle, ADR-0008). - Exploration — la télémétrie opérationnelle :
exploration des hypothèses sur la photo. Le tâtonnement du staff dans le labo (ADR-0019, banc d'expérimentation).
Le défaut de fond : un booléen visible_client ne peut pas exprimer un besoin à trois
lentilles. Et la lentille de l'auditeur n'est pas « tout », c'est « les décisions et le
contrôle ».
Décision¶
- D-JOURNAL-1 — Catégorie DÉRIVÉE, pas stockée. La catégorie est une fonction pure du
type(categorie_evenement(type) -> "jalon" | "controle" | "exploration", table de correspondance). Aucune colonne, donc aucune migration, aucun backfill, et l'historique existant est catégorisé rétroactivement. (Sobriété : ne pas ajouter une colonne quand une dérivation suffit ; évite le piège de nullabilité_COLONNES_AJOUTEES.) - D-JOURNAL-2 — Visibilité = (catégorie × persona). Remplace le filtre booléen unique par une
matrice. Le
visible_clientexistant reste la source du sous-ensemble client des jalons (certains jalons sont publics, d'autres non — inchangé).
| Catégorie | Client (consultant) | Externe | Junior / Senior |
|---|---|---|---|
| Jalon | ✅ visible_client=True |
✅ | ✅ |
| Contrôle | ❌ | ✅ | ✅ |
| Exploration | ❌ | résumé seulement | ✅ (repliable côté UI) |
- D-JOURNAL-3 — Exploration RÉSUMÉE pour l'externe. L'externe ne reçoit pas les lignes d'exploration brutes (hors de son périmètre d'audit). Le serveur les remplace par une ligne de synthèse par acteur : « N explorations par X » — preuve que la diligence a eu lieu, sans le détail ligne à ligne. La preuve de non-arbitraire reste les justifications (texte + référentiel + pièces, ADR-0009), pas le log de tâtonnement. Pour junior/senior, l'exploration est repliée par défaut côté UI (rien de caché à l'équipe opérationnelle).
- D-JOURNAL-4 — Rendu GROUPÉ PAR PHASE, phase courante en tête. Le frontend regroupe les événements par phase du workflow (Étude → Revue → Audit → Livraison), la phase courante d'abord (l'état actionnable est immédiatement visible), l'ordre chronologique préservé à l'intérieur de chaque phase (lecture chaîne-de-custody locale). Remplace le dump plat ascendant.
- D-JOURNAL-5 — Intégral en base, filtré en lecture. On ne touche jamais à l'écriture : le journal reste append-only et complet (ADR-0006). Tout le filtrage/résumé est une transformation de lecture, par persona. Un audit ultérieur (ou un changement de politique) retrouve l'historique brut intact.
Conséquences¶
- Positif. L'externe lit une vue d'audit (décisions + contrôle), pas un flux opérationnel ; le journal cesse d'être kilométrique pour tous ; l'ordre cesse d'enterrer l'actionnable. Zéro migration (catégorie dérivée), rétroactif sur l'historique.
- Pas de perte d'information. Rien n'est effacé ; seule la lecture est cadrée par persona. L'externe garde la preuve de diligence (le résumé) sans le bruit.
- Limite assumée. Le mapping
type → catégorieest une table à tenir à jour quand un nouveau type d'événement apparaît (un type inconnu tombe par défaut enjalon— visible, jamais masqué par erreur : on préfère sur-montrer que cacher un événement d'audit).
Déclencheur de réexamen (pré-enregistré)¶
- Un nouveau persona reviewer (ex. un second audit) → réévaluer la matrice (catégorie × persona).
- L'externe réclame le détail d'exploration (cas d'audit approfondi) → ajouter un dépliement à la demande (le brut est en base, D-JOURNAL-5 le permet sans migration).
- Le résumé par phase devient lui-même long (très grosse étude) → pagination/repli par phase.