Files
rpa_vision_v3/docs/PLAN_ACTION_SUITE_2026-06-23.md
Dom 6907ecc82f docs: track design docs, plans, audits, coordination infrastructure, handoffs
- 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
2026-07-02 13:29:58 +02:00

20 KiB
Raw Blame History

Plan d'action — la suite (post-livraison DGX clinique)

  • Date: 2026-06-23
  • Auteur: Claude (mandat Dom)
  • Statut: vivant — à mettre à jour au fil des validations
  • Porté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 par MEMO_JOUR_J_LIVRAISON_DGX_CLINIQUE_2026-06-23.md — non répété ici.


0. Deux clarifications de cadrage (lire avant le reste)

  1. 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). Le MEMO_JOUR_J + TABLEAU_ACTIONS_DOM_PRECLINIQUE_2026-06-21 confirment qu'ils sont résolus (watchdog OVMF LIVE testé, .178 réservé DHCP, installateur autoportant). → sortis de ce plan.

  2. 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. Le PLAN_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 1d6efdb1bd686c3ac2, 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) guard machine_id centralisé dans stream_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étaire core/competences/).
  • Phase 2 : SP-2 rejeu (le plus intriqué : touche stream_processor et FAISS) — après stabilisation Phase 0 + SP-4.
  • ⚠️ Endpoints compétences verdict/promote EXISTENT déjà (api/lea_competences.py, blueprint app.py:277, test tests/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) :

  1. 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.
  2. 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.
  3. machine_id guard 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.
  4. 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 workflowré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 (pas target.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émo Urgence_aiva_demo = 8/18 manquants.
  • ⚠️ Caveat ré-import : le ré-import lit la source via _load_core_workflow(workflow_id, machine_id) dans data/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 (backup workflows.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_imageVisualAnchor + step.anchor_id (même chemin que les actions simples). Backup DB : instance/workflows.db.bak-sp1-2026-06-23.
  • ⚠️ Découverte : import_learned_workflow cré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 (ec1fb81d686c3ac2) ; 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-dgxmain 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_id entre postes.
  • F11 = accès multi-VPN par site (cf. §0.2).

Encore ouvert (bloque l'exécution des chantiers liés) :

  1. Principe « rejeu intelligent » (R2/R3) — le rejeu doit consulter le fonds appris ? Change l'archi du replay.
  2. Provider Léa au runtime (R1 + résolution).
  3. Critère de fusion des workflows (create-or-update).
  4. 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 mortaxe 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.