- 21 docs/*.md: audits, design notes, deployment plans, checklists, memos - Coordination: ROLES, runbooks (DGX reboot, Lea live), patches, registre, syntheses, systemd, QG template - Handoffs: 6 Codex handoff documents + README + template
8.8 KiB
Plan de chantier — Unification Léa + VWB (préparé le 2026-06-17 au soir)
Préparé par analyse multi-agents (3 agents read-only) + graphify, après le check UI post-reboot DGX. Rien n'a été modifié. Document de cadrage pour la reprise. Méthode imposée : chirurgie itérative supervisée (1 modif = 1 test = validation Dom). Pas de batch.
0. Diagnostic corrigé (important — deux hypothèses du soir invalidées)
Le check UI a fait remonter 3 symptômes : T3 (Léa « ne trouve pas » le bloc-notes), T5/anchors (images d'ancres absentes au VWB), et le sujet de fond fonds commun. L'analyse de code rectifie le diagnostic « à chaud » :
| Hypothèse du soir | Verdict après analyse code |
|---|---|
| T3 = silo machine_id (Léa-VM ne voit pas le savoir du .11) | FAUX au niveau sélection. Le SemanticMatcher ne filtre par aucune machine ; Léa tourne sur le DGX qui héberge les dossiers des deux postes → elle voit déjà tous les workflows. |
T3 = filtre is_production_ready |
FAUX. Aucun composant runtime (matcher, exécuteur, chat) ne lit is_production_ready/learning_state. |
| Vraie cause T3 | Fragmentation de l'apprentissage. 100 répétitions → ~20 workflows distincts nommés d'après les apps vues (« Bloc-notes, Explorateur et Terminal (2)(3)… »), aucun nommé franchement « ouvrir le bloc-notes » → le matching sémantique se dilue sur 20 quasi-doublons. (Le silo existe, mais au niveau renforcement cross-machine, pas sélection.) |
Chantier A — Consolidation de l'apprentissage (débloque T3) — PRIORITÉ HAUTE
Cause racine
Chaque session de streaming crée un workflow neuf, sans jamais chercher ni fusionner un existant.
- Nommage + suffixe
(2)(3):agent_v0/server_v1/stream_processor.py:4335-4344(_generate_workflow_name) — collision de nom → variante numérotée au lieu de renforcement. - Persistance directe sans dédup :
stream_processor.py:4417-4445(_persist_workflow), build:2966-3112(_build_workflow_from_session). - 1 observation / node : build toujours séquentiel (
graph_builder.py:345clusters={i:[i]}), doncobservation_count = 1(graph_builder.py:909). DBSCAN d'agrégation volontairement désactivé. - Aucune fonction de merge/dédup de workflows dans tout
core/. LeVariantManager(core/variants/variant_manager.py:266, seul code qui faitobservation_count += 1) n'est jamais appelé. is_production_readycalculé danscore/training/quality_validator.py:114,238-246(seuilmin_observations_per_node=3) — toujours False car 1 obs/node, mais sans effet runtime (label informatif uniquement).
Leviers (effort / risque / impact)
- A. Découpler « exécutable » de « production_ready » — faible / faible / moyen — quick-win sémantique.
- B. Fusion/dédup create-or-update à la persistance — élevé / moyen / fort — cause racine.
- C. Rebrancher l'agrégation des observations (
VariantManagerou_run_cross_session_learningqui réécritobservation_count) — moyen / moyen / fort. - D. Seuil
min_observationsconfigurable/contextuel — faible / moyen / faible — cosmétique seul. - E.
workflow_id= signature stable de la trajectoire (hash de la séquence d'actions) au lieu des apps vues — moyen / moyen / fort — supprime le(2)(3)à la racine, rend B trivial.
Reco : E + B (signature stable + create-or-update avec agrégation d'observations), D en complément, A en quick-win si on veut juste « rendre exécutable » vite.
Composants
stream_processor.py (L2966-3112, L4335-4344, L4417-4445, cross-session L3149-3268, list L4518) · graph_builder.py (L345, L909, L384-456) · quality_validator.py (L114, L238) · semantic_matcher.py (sélection) · variant_manager.py (L266, à rebrancher) · replay_learner.py:358 (consolidate = hints seulement, leurre).
Chantier B — Affichage anchors VWB (débloque T5) — FIX PRÉCIS, FAIBLE RISQUE
Cause racine
Le commit b8b963059 n'a corrigé que la moitié : l'import lit target.context_hints.anchor_image_base64 uniquement pour les actions simples.
services/learned_workflow_bridge.py: branche action simpleelseL226-233 (lit le base64, ajouté par le commit) ; les actions compound (majoritaires dans les workflows Léa) passent par_convert_compound_substepL279 qui ne lit jamais le base64 → substepsanchor_id=NULL→ frontend affiche « Ancre requise » sans image (frontend_v4/.../StepNode.tsx:113-119).- Aggravant état DGX : les workflows en base datent d'avril, 100% des steps
anchor_id=NULL→ ré-import nécessaire après fix.
Chaîne (pour mémoire)
import api_v3/learned_workflows.py:249 → convert learned_workflow_bridge.py:72 → save_anchor_image → table visual_anchors (db/models.py:163) → API GET /api/v3/anchor/<id>/thumbnail (api_v3/capture.py:356) → React StepNode.tsx (api.ts:136). Pas de mismatch URL/chemin.
Fix
Propager anchor_image_base64 aux substeps compound (passer target dans la boucle compound L169-187 / _convert_compound_substep, poser l'ancre sur le 1er substep cliquable — éviter de dupliquer sur N substeps). Risque faible/additif. Puis ré-importer les workflows cibles.
Chantier C — Fonds commun / mutualisation cross-poste — STRATÉGIQUE, DÉCISION PRODUIT D'ABORD
État
- Fédération
core/federation/entièrement débranchée au runtime :GlobalFAISSIndex.search()(faiss_global.py:199) jamais appelé ; endpoints export/import (api_stream.py:6277-6372) sans aucun déclencheur (ni cron, ni frontend). - Anonymisation = le maillon le plus mûr et prêt :
learning_pack.pyexclut machine_id/hostname/patient/nip/ipp (_clean_metadataL410,_SENSITIVE_METADATA_KEYSL61-66), hash SHA-256 (L388), export = embeddings 512d + signatures (pas de pixels/OCR brut). - Silo réel =
stream_processor._run_cross_session_learningL3197/L3284 (workflow_machine != machine_id → continue) : bloque le renforcement cross-machine. - Identité :
machine_idworkflows =hostname_os(exDESKTOP-58D5CAC_windows,agent_v1/config.py:34-37) ≠ token enrôlementcbd8f9f0…(agent_registry.py:107-111, sécurité parc). À ne pas confondre.
Options
- Intra-clinique brut (lever filtre cross-session L3197/L3284, charger toutes machines) — simple, non anonymisé → acceptable seulement intra-site.
- Cross-clinique anonymisé (brancher
search()global + déclencheur export/import + index global peuplé) — la vraie « fédération », effort moyen-élevé, seul canal sûr PII.
Décisions produit à trancher AVANT de coder (pour Dom)
- Quel point d'entrée Léa au runtime ? Deux providers concurrents :
agent_chat/app.py:678,906(port 5004, SemanticMatcher surdata/training/workflows/) vs chat serveurapi_stream.py:6623(_list_available_workflowsqui liste des sessions live, pas les workflows entraînés). Les logs DGX du soir montrent le 5004 + SemanticMatcher → probablement 5004, à confirmer formellement avant de coder A. - Critère de « même parcours » pour la fusion (levier E/B) : signature de trajectoire ? nom de base ? (workflows de 7 à 89 nodes pour « le même » parcours → alignement non trivial).
- Source de vérité workflows (DETTE-015) : DB VWB SQLite (5002) vs JSON
data/training/workflows/— la route/api/workflowsde Léa fusionne les deux avec une dédup fragile. Trancher la source canonique. - Niveau de mutualisation : intra-clinique brut (rapide, non anonymisé) vs fonds commun cross-clinique anonymisé (fédération). Implications réglementaires opposées.
- Re-exécutabilité des packs fédérés : l'export n'emporte que des squelettes anonymisés (pas les templates/coordonnées) → un chemin de re-résolution visuelle est nécessaire (cohérent avec le contrat 100% vision).
Ordre recommandé pour la reprise
- Confirmer le provider runtime de Léa (Q1) — 10 min, read-only, débloque tout le reste.
- Chantier B (anchors) — fix ciblé compound + ré-import. Faible risque, gain visuel immédiat (T5).
- Chantier A (consolidation) — E + B, chirurgie itérative. Débloque T3 (le bloc-notes « connu »).
- Chantier C (fonds commun) — décision produit (Q4) puis implémentation anonymisée. Le plus stratégique pour la proposition de valeur, mais le moins urgent pour une démo.
Garde-fous
- Tout changement au build/persist (Chantier A) touche le chemin qui alimente la démo Urgence → chirurgie itérative, 1 test par modif.
- Champ de mines
core/: vérifier le wiring runtime réel avant de rebrancher (VariantManager), pas seulement la présence du code. - Mutualisation cross-site = uniquement via
LearningPackanonymisé, jamais recopie de JSON bruts (PII : OCR, titres fenêtres patients).