- 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
20 KiB
Plan d'action — la suite (post-livraison DGX clinique)
Date: 2026-06-23Auteur: Claude (mandat Dom)Statut: vivant — à mettre à jour au fil des validationsPortée: chapeaute les plans existants (ne les remplace pas), dédoublonne et priorise les actions encore ouvertes après la livraison clinique du jour.
Contexte : le DGX part définitivement en clinique aujourd'hui (
192.168.1.178), puis travail 100 % à distance (VPN Stormshield + scp). Le jour J lui-même est couvert parMEMO_JOUR_J_LIVRAISON_DGX_CLINIQUE_2026-06-23.md— non répété ici.
0. Deux clarifications de cadrage (lire avant le reste)
-
Les gaps « pré-clinique » sont clos. L'audit (
AUDIT_GAPS_APPLI_100PCT_2026-06-10) et le postmortem (POSTMORTEM_PANNE_SECTEUR_DGX_2026-06-20) listaient des gaps durs (OVMF G2, IP statique G1, reconnexion Léa G4, reboot 11 services). LeMEMO_JOUR_J+TABLEAU_ACTIONS_DOM_PRECLINIQUE_2026-06-21confirment qu'ils sont résolus (watchdog OVMF LIVE testé,.178réservé DHCP, installateur autoportant). → sortis de ce plan. -
Accès distant = multi-VPN par site (correction Dom 23/06). Pas « WireGuard caduc » : WireGuard = notre VPN (labo/éditeur, reste valide) ; Stormshield = le VPN de la clinique (côté client) ; d'autres clients auront d'autres VPN. ⇒ l'accès distant devient une fonctionnalité paramétrée par fiche site (
INSTALLATION_MULTI_SITE), avec SSH cert-only + RDP comme transport commun au-dessus du VPN propre à chaque site. LePLAN_ACCES_DISTANT_SSH_CERT_DGX_2026-06-20(WireGuard) n'est pas suspendu — il devient un profil d'accès parmi d'autres.
Tableau de bord — sous-projets (labo d'abord, WIP ≤ 3 actifs)
Chaque feature F# = un sous-projet (objectif, branche, prérequis, statut, done). Backlog priorisé ; on n'ouvre que 2-3 sous-projets actifs à la fois (réactivité = focus). Merge prod = supervisé Dom. Codex orchestre par sous-projet à son retour (28/06).
Prérequis socle (avant parallélisme) : merger fix/dashboard-complete-installer (HEAD d686c3ac2) sur poc-dgx (1d6efdb1b) → base git propre ; ménage code mort (Qwen) cadré.
| SP | Feature | Statut | Prérequis bloquant |
|---|---|---|---|
| SP-0 | Socle git (merge branche + base propre) | ✅ fait (23/06 — FF 1d6efdb1b→d686c3ac2, local) |
— (parallélisme débloqué) |
| SP-1 | F14/U-B Anchors (compound) | ✅ code FAIT, commit 2cabc6cb7 (br. sp1/anchors-compound), validé données réelles + persistance |
ré-import en place entravé par U-A (import = doublon) → SP-4 |
| SP-2 | F2 Rejeu intelligent (R1→R7) | 🟢 débloqué (Q-F2-1 ✅ ; point d'entrée tracé = agent_chat 5004 / SemanticMatcher) | gros chantier R1→R7, à cadrer |
| SP-3 | F8 Exécution native (durcir + sandbox) | 🔴 bloqué | Q-F8-1..4 + vérif sécurité /execute/instruction |
| SP-4 | F1/U-A Consolidation fragmentation (T3) | 🟢 débloqué (Q-F1-1 ✅ signature trajectoire, source=DB Q-F9-1 ✅) | — (active quand on veut) |
| SP-5 | F6/U-C Mutualisation/fédération | 🟠 décidé, à coder | dépend SP-1 (anchors) |
| SP-Q | F13 Ménage code mort | 🔵 en cours (Qwen, read-only) | — |
| — | F3 F4 F5 F7 F9 F10 F11 F12 | ⚪ backlog priorisé | — |
Cadrage 3 sous-projets (23/06, 3 agents read-only) — interfaces communes & séquencement :
- 3 interfaces partagées à poser UNE fois : (1) signature de trajectoire (SP-4 = propriétaire) ; (2) index embeddings/FAISS partagé (
core/embeddings/shared_index.py, consommé par SP-2-R3 et la sélection skill↔tâche) ; (3) guardmachine_idcentralisé dansstream_processor.py(SP-4 ∩ SP-2-R6, ~L3197/3284). - Phase 0 (fondation, série, petit) : signature de trajectoire + accès index partagé.
- Phase 1 (parallèle, WIP=2, faible collision) : SP-4 (propriétaire
stream_processor.py) ∥ compétences (propriétairecore/competences/). - Phase 2 : SP-2 rejeu (le plus intriqué : touche
stream_processoret FAISS) — après stabilisation Phase 0 + SP-4. - ⚠️ Endpoints compétences
verdict/promoteEXISTENT déjà (api/lea_competences.py, blueprintapp.py:277, testtests/unit/test_lea_competence_verdict_api.py) → chantier compétences = auto-déclenchement (hook) + sélection intelligente seulement. - 1 branche par sous-projet, merge supervisé Dom.
QG Qwen (23/06, 18:30) — GO avec 4 ajustements (intégrés) :
- Marqueurs de propriété dans
api_stream.py(commentaires par range d'endpoints SP-4 vs compétences) — seul point de contact Phase 1, éviter conflits silencieux. Pas de refactor. - Fallback R2/R3 obligatoire (SP-2) : chaque nouveau chemin de résolution retombe sur les coords figées si la cascade intelligente échoue. Le rejeu enrichit, ne remplace pas. Non-négociable démo.
machine_idguard intouché en Phase 1 : le lifting du silo (stream_processor~L3197/3284) est entièrement Phase 2 / SP-2-R6. → lève la collision SP-4 ↔ SP-2.- Dead import
ExternalDecisionClient(api_stream.py:L7285, module absent, inoffensif via try/except) → à nettoyer dans le ménage code mort (catégorie C), pas dans SP-4.
Axe central — Rejeu intelligent des actions apprises 🎯
C'est le cœur produit (« Léa apprend, comprend, rejoue en exploitant ce qu'elle a appris » — pas du record-and-replay). Vérification runtime du 2026-06-23 (à reconfirmer fichier:ligne avant toute modif — méthode projet) :
État réel de la chaîne apprentissage → rejeu
| Maillon | État | Preuve (à reconfirmer) | Constat |
|---|---|---|---|
| Import auto Shadow → workflow rejouable | ❌ débranché | finalize api_stream ~2430-2466 : enqueue worker VLM, pas de conversion/import auto ; ShadowLearningHook jamais appelé |
Ce qui est appris n'est pas rendu rejouable sans geste manuel |
| Rejeu consulte le fonds appris (TargetMemoryStore) | ❌ débranché | build_replay_from_raw_events (~1841-2200) ne consulte rien ; TargetResolver.lookup() (~3263) jamais appelé par replay-session |
Le rejeu rejoue des coords/anchors figés, pas la cible apprise |
| Lecture FAISS / GlobalFAISSIndex au rejeu | ❌ write-only | workflow_replay.py accepte faiss_manager en param mais ne l'utilise pas ; aucun .search() au rejeu |
Index écrit, jamais lu au rejeu (cohérent fédération dormante) |
| verify post-condition (état UI après action) | ⚠️ absent | safety_checks + pause supervisée OK si mode supervisé (api_stream ~4299-4367) ; pas de verify_screen post-action |
Pas de boucle de feedback succès/échec |
Templating {{var.field.sub}} au rejeu |
✅ marche | _resolve_runtime_vars() replay_engine ~2027-2041, appelé ~4293 |
Données récupérées (T2A, extract) réinjectées en temps réel — acquis, ne pas refaire |
Filtre machine_id (cross-session) |
✅ actif = silo | stream_processor ~3197-3200 et ~3285 |
Apprentissage siloté par poste ; rejeu direct non filtré |
Verdict : le rejeu est aujourd'hui « brut » (events → coords), pas « intelligent ». L'apprentissage tourne (ShadowLearning, TargetMemory, FAISS) mais en silos jamais wirés au rejeu. Le templating des données récupérées, lui, fonctionne déjà.
Chaîne cible (ce vers quoi on va)
Capture/Shadow → finalize → [R1] import auto en workflow rejouable
→ au rejeu, chaque action résolue par : cascade UI (OCR/template/YOLO/VLM)
+ [R2] fonds appris (TargetMemoryStore) + [R3] FAISS anchors (similarité)
→ [✅] réinjection des données récupérées (templating)
→ [R4] verify post-condition (échec = pause supervisée, pas stop = apprentissage)
→ [R5] le résultat du rejeu réécrit le fonds (boucle)
→ [R6] mutualisation : lever silo machine_id + brancher fédération
Chantiers rejeu (ordonnés, du plus structurant au plus fin)
| ID | Chantier | Dépend de |
|---|---|---|
| R1 | Brancher l'import auto d'une session apprise en workflow rejouable post-finalize | décision « provider Léa runtime » |
| R2 | Faire consulter le fonds appris au rejeu : câbler TargetResolver.lookup() / TargetMemoryStore dans le chemin replay-session (résoudre par cible apprise, pas coords figées) |
R1 |
| R3 | Lire FAISS au rejeu : utiliser le faiss_manager déjà passé à workflow_replay.py comme fallback de résolution par similarité d'anchor |
R2 |
| R4 | verify post-condition : vérif état UI après action ; échec → pause supervisée (cf. failure-is-learning) | — |
| R5 | Boucle d'apprentissage : succès/échec/correction humaine du rejeu réécrivent le fonds (TargetMemory + FAISS) | R2, R4 |
| R6 | Mutualisation : lever le filtre machine_id + brancher la fédération (GlobalFAISSIndex.search() jamais appelé) |
décisions produit (silo vs fédéré, PII) |
| R7 | Bugs rejeu résiduels : reprise sur crash (R4 audit), OCR span/centre-de-ligne (R5 audit) | — |
⚠️ Tout cet axe touche le chemin runtime de la démo. Méthode imposée : chirurgie itérative supervisée — un maillon = un test ≤ 2 min = GO Dom, jamais de batch, démo Urgence_aiva_demo intacte à chaque étape. Reconfirmer le wiring au runtime avant chaque modif (imports lazy).
Feature — Unification Léa ↔ VWB (anchors) 🔗
Manque corrigé (signalé Dom 23/06). Chantier dédié : PLAN_CHANTIER_UNIFICATION_LEA_VWB_2026-06-17.md. Cœur = les anchors, le pont entre ce que VWB capture au recording et ce que Léa récupère au rejeu. Symptôme T3 « Léa ne trouve pas le bloc-notes » = fragmentation de l'apprentissage, pas silo.
Cycle de vie cible de l'anchor : capture (VWB recording / Shadow) → persistance (visual_anchors) → propagation au workflow → récupération au rejeu (résolution visuelle).
| ID | Sous-chantier | État | Risque |
|---|---|---|---|
| U-B | Anchors — propager anchor_image_base64 aux substeps compound (b8b963059 n'a fait que les actions simples ; les compound, majoritaires côté Léa, restent anchor_id=NULL → « Ancre requise » sans image) + ré-importer les workflows. learned_workflow_bridge.py _convert_compound_substep ~L279 |
prêt — fix ciblé bloquant | faible/additif |
| U-A | Consolidation fragmentation : workflow_id = signature stable de trajectoire + create-or-update (fusion + agrégation d'observations) — débloque T3 |
design décidé | moyen (touche build/persist démo) |
| U-C | Fonds commun = F6 (décidé cross + intra) : lever filtre machine_id (stream_processor ~L3197/L3284) + brancher fédération anonymisée (GlobalFAISSIndex.search() jamais appelé) |
décision prise, à coder | moyen-élevé |
| U-D | Asymétrie grounding : VWB recording = UI-DETR-1 ; replay Léa = cascade OCR/template/VLM → unifier le chemin de résolution | sujet ouvert post-démo | à trancher |
Lien fort avec F2 (rejeu intelligent) : la récupération des anchors au rejeu (U-B) est le substrat de R2/R3 — sans anchors propagés/retrouvés, le rejeu ne peut pas résoudre par la vision et retombe sur des coords figées.
⚠️ Implication de la décision F6 = cross-clinique : un pack fédéré anonymisé n'emporte ni coordonnées ni templates (PII) → seule la re-résolution visuelle par anchors/FAISS permet de le rejouer ailleurs. Donc F6 cross-clinique entraîne quasi-mécaniquement le principe « rejeu intelligent » (Q-F2-1) : il devient un prérequis, pas une option. (Décision induite Q-F14-1 au registre.)
Ordre interne (du chantier) : confirmer provider Léa (Q-F2-2) → U-B anchors (gain visuel immédiat, faible risque) → U-A consolidation → U-C fédération.
Prép SP-1 / U-B (vérif runtime 23/06, read-only) :
- Gap confirmé :
learned_workflow_bridge.py:_convert_compound_substep(L279-321) ne pose jamais_anchor_image_base64; la branche action simple (L226-233) le fait. → substeps compound =anchor_id NULL. - Source dispo : dans le JSON core, l'ancre du compound est à
target.context_hints.anchor_image_base64(pastarget.anchor_image_base64).target(parent) est déjà passé à_convert_compound_substep. - Fix (additif, ~1 endroit) : dans la boucle compound (L169-187), poser
_anchor_image_base64(même fallback que simple) sur le 1er substep cliquable uniquement. - Impact DB : 487/582 steps
anchor_id NULL(84 %) ; démoUrgence_aiva_demo= 8/18 manquants. - ⚠️ Caveat ré-import : le ré-import lit la source via
_load_core_workflow(workflow_id, machine_id)dansdata/training/workflows/{machine_id}/→ disponible par workflow ; la source de la démo n'est pas trouvée par nom → vérifier par core_workflow_id avant de compter sur le ré-import de la démo. - Test : ré-importer un workflow compound → un substep cliquable doit afficher son image via
GET /api/v3/anchor/<id>/thumbnail+StepNode.tsx. Risque : code = additif (faible) ; ré-import = étape sensible (backupworkflows.db+ par workflow + revérifier le replay). - FAIT 23/06 : fix commité (
2cabc6cb7, br.sp1/anchors-compound), TDD RED→GREEN, validé sur données réelles (source réelle : 2/2 clics compound désormais ancrés). Persistance confirmée :import_learned_workflow(L332)pop("_anchor_image_base64")→save_anchor_image→VisualAnchor+step.anchor_id(même chemin que les actions simples). Backup DB :instance/workflows.db.bak-sp1-2026-06-23. - ⚠️ Découverte :
import_learned_workflowcrée un nouveau workflow (generate_id, L301) — pas de mise à jour en place. Donc rafraîchir les anchors d'un workflow existant (ex. démo) par ré-import = doublon → dépend de U-A (create-or-update). Le fix U-B est correct en avant (tout nouvel import aura les anchors compound) ; le rafraîchissement des workflows déjà en base est porté par U-A (SP-4, décision Q-F1-1).
Feature — Exécution native agentique (computer-use zéro-shot) 🤖
Manque recadré (signalé Dom 23/06). Donner un objectif en langage naturel (« ouvre un navigateur et va sur YouTube ») et l'exécuter sans workflow appris, par planification + grounding visuel. Complément du rejeu appris (F2).
⚠️ Constat runtime (vérif 23/06) : ce n'est PAS absent — les briques existent et sont wirées. Le manque réel = durcissement + sandbox + validation humaine.
| ID | Brique | État runtime | Manque |
|---|---|---|---|
| F8.1 | Boucle ORA observe→reason(VLM)→act→verify→retry (core/execution/observe_reason_act.py:run_instruction) |
✅ wired via endpoint /execute/instruction (api_v3/execute.py:2033) |
durcissement |
| F8.2 | Planner NL→plan (agent_v0/server_v1/task_planner.py:understand() gemma4, mode _execute_free()) |
⚠️ présent, mode « free » peu mûr/peu testé | maturation + tests |
| F8.3 | Grounding cascade OCR→UI-TARS→VLM (= F3, partagé) | ✅ wired (input_handler.py:_grounding_ui_tars) |
— |
| F8.4 | Sandbox Worker (VM/Xvfb/VNC + kill-switch + validation humaine) | ❌ absent — exécution directe sur l'host, sans isolation ni pause | = le vrai manque (décision CUA P1) |
| F8.5 | Boucle vers l'apprentissage : un run natif réussi → capturé comme workflow appris (alimente F1) | ❌ absent | à câbler |
| F8.6 | Replanification dynamique si l'écran change radicalement (app crash…) | ❌ absent (ORA linéaire) | à ajouter |
🔴 Sécurité — vérifié 23/06 (audit read-only) : POST /api/v3/execute/instruction (VWB backend 5002, app.py:321 / execute.py:2033) lance la boucle ORA qui pilote directement l'écran X11 de l'host (pyautogui/xdotool, pas de sandbox, pas de pause humaine, pas de kill-switch) — cible de fait la VM Léa affichée. Auth = middleware Basic global (DASHBOARD_PASSWORD) mais loopback exempté ; sur le DGX clinique 5002 est atteignable sur le LAN (401 sans creds / 200 loopback), pas d'expo WAN. → Un acteur du LAN clinique avec les creds partagés (faibles, Medecin2026!) ou tout process local (loopback) peut déclencher une instruction agentique arbitraire sur l'host. Mitigation à décider (prod) : restreindre /execute/instruction à loopback / désactiver le mode « free » tant que F8.4 (sandbox) n'existe pas. Tant que F8.4 n'est pas en place, le mode « free » ne doit PAS être ouvert au-delà d'un environnement jetable — « JAMAIS l'hôte » + safety agent.
Articulation avec le mode appris : routage — pas de workflow appris pour le but → mode natif ; sinon rejeu (F2). Et le natif réussi devient appris (F8.5, la boucle se referme). Décisions → registre F8.
H1 — Stabilisation clinique (jour J → quelques jours, à distance)
| # | Action | Source |
|---|---|---|
| 1 | Aligner DGX↔local avant débranchement : git 5 commits behind (ec1fb81→d686c3ac2) ; backup workflows.db AVANT reset |
Qwen + tableau B′ |
| 2 | Ajouter RPA_SIGNING_KEY dans .env.local DGX (HMAC métadonnées FAISS, absent) |
KO-1 Qwen |
| 3 | Vérifier accès Stormshield depuis laptop = point de non-retour avant départ | MEMO §5 |
| 4 | Mot de passe (pas PIN) compte VM aivanov → RDP + presse-papier |
handoff |
| 5 | Confirmer profil Stormshield laptop avec PORQUET (atteint .178 ?) |
handoff |
| 6 | Apprentissage e2e sur 1 poste TIM réel avant de généraliser aux 5 | MEMO + audit |
H2 — Consolidation & dette (semaines suivantes, scp via VPN)
| # | Chantier | Pourquoi |
|---|---|---|
| 7 | Merger les branches : fix/dashboard-complete-installer non mergée sur poc-dgx ; clarifier topologie poc-dgx↔main |
base git propre avant tout ménage |
| 8 | Ménage code mort (mission Qwen en cours) → exécution par lots + QG | post-stabilisation |
| 9 | Test de charge multi-agents (2-3 puis 5 Léa simultanées) | 1 TIM démo ≠ 5 TIM réels |
| 10 | Bugs non-rejeu résiduels de l'audit (watchdog _retry_pending A2, écran verrouillé non détecté…) — re-vérifier lesquels sont encore ouverts |
audit gaps |
H3 — Produit & fond (après stabilisation, sur décisions Dom)
| # | Chantier | Bloqué par |
|---|---|---|
| 11 | Chantier B anchors VWB (propager anchor_image_base64 aux substeps compound) — fix ciblé, risque faible |
rien, prêt |
| 12 | CUA Sandbox Worker P1 (décision Dom 18/06 : VM/Xvfb/VNC + kill-switch, jamais l'hôte) | priorisation |
| 13 | Source vérité workflows (migration JSON→SQLAlchemy, sortir DETTE-015) | post-POC |
| 14 | Shadow → Copilot → Autonomous au runtime (aujourd'hui design) | R1-R6 + décisions |
Décisions qui reviennent à Dom (registre dédié)
→ Suivi vivant dans DECISIONS_PRODUIT_EN_ATTENTE_2026-06-23.md (Dom remplit à tête reposée).
Déjà tranché (23/06) :
- ✅ H3 en premier, décisions avant exécution (séquencement préservé).
- ✅ F6 = mutualisation cross-clinique ET intra-clinique → fédération anonymisée dans le périmètre + lever le silo
machine_identre postes. - ✅ F11 = accès multi-VPN par site (cf. §0.2).
Encore ouvert (bloque l'exécution des chantiers liés) :
- Principe « rejeu intelligent » (R2/R3) — le rejeu doit consulter le fonds appris ? Change l'archi du replay.
- Provider Léa au runtime (R1 + résolution).
- Critère de fusion des workflows (create-or-update).
- Source de vérité workflows (DB vs JSON) + métrique produit (24/79/37) — reco Claude consignée : DB = vérité, JSON = échange ; métrique = rejouables validés.
Séquencement imposé
Stabiliser (H1) → base git propre + merge (H2-7) → ménage code mort → axe rejeu R1→R7 (chirurgie supervisée) → fond produit (H3). On ne dégraisse pas, et on ne recâble pas le rejeu, sur une base mouvante.
Plans sources (référence, ne pas dupliquer)
MEMO_JOUR_J_LIVRAISON_DGX_CLINIQUE_2026-06-23 · TABLEAU_ACTIONS_DOM_PRECLINIQUE_2026-06-21 · PLAN_CHANTIER_UNIFICATION_LEA_VWB_2026-06-17 · PLAN_ACCES_DISTANT_SSH_CERT_DGX_2026-06-20 (volet WG suspendu) · PLAN_MIGRATION_WORKFLOWS_STORE_2026-06-09 · AUDIT_GAPS_APPLI_100PCT_2026-06-10 · CARTO_APPRENTISSAGE_FONDS_COMMUN_2026-06-16 · CHECKLIST_DGX_PRE_CLINIQUE · INSTALLATION_MULTI_SITE.