- 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
15 KiB
Audit — Ce qui manque pour une appli 100% fonctionnelle
- Date : 2026-06-10
- Demandeur : Dom
- Auteur : Claude (audit read-only par 4 sous-agents d'exploration + contre-vérifications manuelles)
- Périmètre : agent client Windows (Léa), chaîne d'apprentissage, capacité de replay, maturité produit/fleet
- Statut findings : les fichier:ligne proviennent d'agents d'exploration. Les 3 contradictions majeures ont été re-vérifiées à la main (voir annexe). Avant d'engager du code sur un item, revalider le point au cas par cas (méthode habituelle).
- Avis croisé Qwen : reçu 2026-06-10 23:30 (
inbox_claude/2026-06-10_2330_qwen-to-claude_AVIS-GAPS-APPLI-100PCT.md) — intégré en Addendum (§7).
1. Diagnostic central
L'appli aujourd'hui, honnêtement qualifiée : un record-and-replay supervisé robuste, avec une couche sémantique réelle au grounding, mais dont la boucle d'apprentissage n'est pas fermée et dont les filets de sécurité sont écrits mais débranchés.
Trois promesses produit non tenues dans le code qui tourne :
- La boucle d'apprentissage est ouverte — Shadow observe et construit des workflows, mais ils n'arrivent jamais dans VWB (import jamais déclenché).
- L'exécution ne se vérifie pas elle-même — verify, healing, recovery : tout existe, tout est désactivé ou non branché.
- Pas de généralisation — un workflow appris ne survit pas à un changement de poste/résolution ; FAISS est construit au training mais jamais consulté au replay.
Point structurel transverse : deux chemins d'exécution aux capacités différentes :
visual_workflow_builder/backend/api_v3/execute.py(exécution locale VWB, Legacy + ORA)agent_v0/server_v1/replay_engine.py→ agent Windows Léa (chemin POC)
Certains manques n'existent que sur l'un des deux. t2a_decision et le templating profond {{var.field.sub}} sont implémentés sur le chemin Léa (replay_engine.py:1922 et :2017) mais absents du chemin local. Cette asymétrie a piégé jusqu'aux agents d'audit eux-mêmes — c'est un coût de maintenance et un risque d'erreur permanent.
2. Axe agent client Windows (Léa) — ~80% fonctionnel, 1 bug critique
Ce qui marche (vérifié wired)
- Capture complète : clics/double-clics, clavier+buffer texte, scroll, multi-écrans, DPI awareness, floutage sensible, dédup pHash.
- Résilience réseau : buffer SQLite persistant, retry 3×, backoff 1→30s, heartbeat 5s.
- Purge captures après ACK serveur (
PURGE_AFTER_ACK=1défaut). - Enroll Bearer token + machine_id ; détection dialogues système UAC/CredUI/SmartScreen fail-closed (pause supervisée).
- Rétention logs 180 j (conforme Règlement IA art. 12).
Gaps
| # | Gap | Sévérité | Preuve | Type |
|---|---|---|---|---|
| A1 | Timeout HTTP client 5s : étape serveur > 5s (extract_text, t2a) → client coupe, action déjà sortie de la queue → perdue silencieusement. Incident documenté 8 mai (4 actions perdues, pause step 18). | 🔴 BLOQUANT POC | agent_v1/core/executor.py:1786 ; docs/REPLAY_BLOCAGE_NOTES_MEDICALES_2026-05-08.md |
Bug |
| A2 | Watchdog _retry_pending absent côté serveur : actions perdues jamais republiées. |
🔴 BLOQUANT POC | network/streamer.py:99-105 (constat) |
Non implémenté |
| A3 | Écran verrouillé non détecté : agent capture noir, tape dans le vide. | 🟠 Important | — | Non implémenté |
| A4 | RecoveryEngine client : code complet, jamais appelé. | 🟡 Nice-to-have | agent_v1/core/recovery.py |
Écrit non branché |
| A5 | Long-polling HTTP fragile par construction (vs SSE/WebSocket). | 🟡 Post-POC | main.py:287-366 |
Architecture |
Note : la suspicion « appel Ollama de commentaire d'action orphelin côté client » ne se confirme pas côté agent_v1 (OLLAMA_HOST défini dans config mais aucun appel client). Le commentaire d'action est côté serveur.
3. Axe chaîne d'apprentissage — marche jusqu'au dernier mètre, puis s'arrête
Ce qui marche (vérifié wired)
- Chaîne Shadow complète : streaming → LiveSessionManager →
_worker_queue.txt→run_worker.py→ ScreenAnalyzer → CLIP → FAISS indexation → GraphBuilder DBSCAN → workflow JSON dansdata/training/workflows/{machine_id}/. - Apprentissage post-replay : ReplayLearner (JSONL
data/learning/replay_results/) + TargetMemoryStore (SQLitedata/learning/target_memory.db), consultés avant résolution, alimentés après succès/échec.
Gaps
| # | Gap | Sévérité | Preuve | Type |
|---|---|---|---|---|
| L1 | Workflows Shadow jamais importés dans VWB : import_core_workflow() existe (visual_workflow_builder/backend/api/workflows.py:622) mais aucun appel automatique post-finalize. La boucle d'apprentissage produit des fichiers que personne ne voit. |
🔴 BLOQUANT promesse produit | grep import_core_workflow depuis server_v1 = 0 hit |
Écrit non branché |
| L2 | ShadowLearningHook jamais importé (état avril 2026 inchangé). | 🟠 Important | core/grounding/shadow_learning_hook.py |
Écrit non branché |
| L3 | FAISS construit mais jamais interrogé au replay — la mémoire sémantique ne sert pas à la résolution. | 🟠 Important | core/embedding/faiss_manager.py |
Écrit non branché |
| L4 | Pas de généralisation cross-résolution/cross-poste : workflows cloisonnés par machine_id, ancres dépendantes du poste source. | 🟠 Important | core/models/workflow_graph.py |
Non implémenté |
| L5 | Copilot : inexistant (aucun moteur de suggestion). Autonomous : AutonomousPlanner isolé du replay engine. Le cycle Shadow→Copilot→Autonomous est aujourd'hui Shadow→(rien)→exécution déclenchée manuellement. | 🔴 BLOQUANT promesse produit | agent_chat/autonomous_planner.py, agent_v0/server_v1/execution_plan_runner.py |
Squelettes |
| L6 | Recapture anchor VWB : pas de revalidation/régénération PNG post-modification (bug connu mai 2026). | 🟠 Important | visual_workflow_builder/backend/services/anchor_image_service.py |
Non implémenté |
4. Axe replay/exécution — ça clique bien, mais ça ne sait pas si ça a marché
Ce qui marche (vérifié wired)
- Cascade de résolution active : template matching → CLIP → OCR/UI-TARS → VLM.
- DialogHandler branché (détection popups pré-step).
- Pause supervisée avec choix utilisateur (skip/static/coords, timeout 120s).
- Chemin Léa :
t2a_decision(replay_engine.py:1922, handlers :2045+), templating profond{{var.field.sub}}(path.split('.'):2017), extract_text.
Gaps
| # | Gap | Sévérité | Preuve | Type |
|---|---|---|---|---|
| R1 | verify_level='none' en dur : aucune vérification post-action que le clic a produit l'effet attendu (seul pHash d'attente d'écran). Contraire au principe « vérif avant + après chaque clic ». |
🟠 Important (🔴 avant clinique) | execute.py:1545 |
Branché désactivé |
| R2 | VLM pre-check if False: en dur : pas de validation que l'élément trouvé = l'élément attendu. |
🟠 Important | core/execution/observe_reason_act.py:1707 |
Branché désactivé |
| R3 | Healing engine implémenté, jamais appelé. | 🟠 Important | core/healing/healing_engine.py:21-150 |
Écrit non branché |
| R4 | Aucune reprise après crash : crash au step N → redémarrage à 0, pas de checkpoint. | 🔴 BLOQUANT clinique | execute.py:1732 (thread daemon sans checkpoint) |
Non implémenté |
| R5 | OCR-DIRECT « centre de ligne » : substring d'une ligne docTR → coordonnées du centre de la ligne entière. Sur une barre d'onglets, Imagerie/Notes/Synthèse ≈ mêmes coords. Latent et sournois. | 🟠 Important | agent_v0/server_v1/resolve_engine.py:1447-1527 |
Bug |
| R6 | Timeout VWB→serveur 30s vs étapes longues (t2a/Ollama lent) → 504. | 🟠 Important | server_client.py:207 |
Bug config |
| R7 | Reporting d'exécution pauvre : méthode de grounding utilisée et raison d'échec non tracées. | 🟠 Important | ExecutionStep DB | Incomplet |
| R8 | Popup détecté mais gestion échouée → continue (log seul), pas de pause. | 🟡 Nice-to-have | execute.py:283 |
Incomplet |
| R9 | 3 systèmes de grounding morts (code zombie) : fast_detector, smart_matcher, template_matcher standalone. |
🟡 Ménage | core/grounding/ |
Poids mort |
| R10 | TitleVerifier aveugle en VM (crop EasyOCR 45px illisible). | 🟡 Connu | core/grounding/title_verifier.py:34-67 |
Limitation |
5. Axe maturité produit / fleet — OK pour 5 TIM supervisés, pas au-delà
Ce qui marche (vérifié)
- Fleet : enroll/uninstall/revoke SQLite (
agent_registry.py),_guard_agent_registry_access, last_seen heartbeat. - Sessions concurrentes thread-safe ; uploads images rate-limités sans sérialisation.
- Healthcheck multi-composants + timer systemd (API, dashboard, worker heartbeat, disque).
- Export ZIP workflows ; dashboard HTTP Basic fail-closed.
Gaps
| # | Gap | Sévérité | Preuve | Type |
|---|---|---|---|---|
| P1 | DETTE-006/010 — grounding Qwen3-VL instable (smart_resize non déterministe, config checkpoint factor 28 vs 32). LE risque technique du calendrier POC. |
🔴 BLOQUANT POC | docs/MIGRATION_VLM_PLAN_2026-05-09.md, DETTE_TECHNIQUE.md |
En cours |
| P2 | 1 seul replay simultané (verrou global _replay_lock). Acceptable POC séquentiel, bloquant au-delà. |
🟠 Important post-POC | api_stream.py |
Limitation |
| P3 | Opérabilité non-dev : pas d'onglet « état systèmes », pas de monitoring GPU/Ollama, erreurs JSON brut. Acceptable seulement avec Dom en SSH derrière. | 🟠 Important | web_dashboard/app.py |
Incomplet |
| P4 | Export ZIP sans restore en masse ; backup exporte les JSON, pas la DB (DETTE-015 : symlink tient pour le POC). | 🟠 Important | core/system/backup_exporter.py:58-160 |
Incomplet |
| P5 | Multi-users/RBAC : 1 user statique. Accepté POC (lié DETTE-016). | 🟡 Post-POC | web_dashboard/app.py:67 |
Accepté |
| P6 | Pas de rotation des logs serveurs (logs/*.log) — artifact_retention ne couvre que les données. |
🟡 Nice-to-have | core/system/artifact_retention.py |
Incomplet |
| P7 | DETTE-013 : tests unit non exécutables sans RPA_API_TOKEN (sys.exit à l'import). |
🟠 Important dev | agent_v0/server_v1/api_stream.py:135 |
Bug |
| P8 | Ménage pré-POC (~9-10 j-h, MENAGE_PRE_POC_2026-05-29.md) non engagé ; ~21 modules core/ orphelins documentés. |
🟡 Planifié | docs/POC/ | Dette |
6. Priorisation proposée
Horizon 1 — avant le M2 live (jours) : fiabiliser la chaîne qui existe
- A1 Timeout client 5s → 30s (1 constante) + A2 watchdog
_retry_pendingserveur — le duo qui a tué la session du 8 mai. - P1 Trancher DETTE-006/010 (calibration grounding Qwen3-VL sur DGX) avant le bench J+6.
- A3 Détection écran verrouillé.
Horizon 2 — avant la clinique (semaines) : les filets de sécurité
- R1/R2 Réactiver verify (au moins post-condition légère) + pre-check.
- R5 Fix OCR centre-de-ligne (span du substring, pas centre de ligne).
- R4 Reprise sur crash (checkpoint step) + R7 tracer la méthode de résolution.
- R6 Timeout VWB 30s → adapté aux étapes longues (ou polling asynchrone).
Horizon 3 — la promesse produit (post-POC) : fermer la boucle
- L1 Pont auto Shadow→VWB (
import_core_workflowpost-finalize) — LA pièce qui transforme l'outil en produit apprenant. - L3 FAISS consulté au replay + L4 début de généralisation cross-poste.
- L5 Copilot (moteur de suggestion) puis branchement AutonomousPlanner.
- Unifier les deux chemins d'exécution (execute.py local vs replay_engine.py Léa).
- P2 Replays parallèles, P3 opérabilité TIM, P4 restore, RBAC.
7. Addendum — Avis croisé Qwen (historien/QG, 2026-06-10 23:30)
Convergences avec l'audit code
- DETTE-006/010 = les deux vrais risques démo (« si le grounding dérive, les clics ratent. Démo morte. ») — aligné avec P1/Horizon 1.
- Monitoring/alerting productif absent (P3) : « si un worker crashe à 3h du matin sur un TIM, personne ne le saura ».
- Écarts doc vs réalité confirmés par son registre : ContinuousLearner/Shadow hook orphelins (L2), cascade YOLO et OmniParser neutralisées (DETTE-004), ~1900 lignes de code mort jamais câblé (autonomous_planner, seeclick…) — cohérent avec L5/R9.
Apports nouveaux de Qwen (absents de l'audit code)
- Multi-TIM jamais testé > 1 agent simultané : le Fleet existe, mais routage session, isolation mémoire et contention GPU sous charge réelle sont inconnus. Mon audit avait noté le verrou replay global (P2) ; Qwen élargit : c'est toute la concurrence multi-agents qui n'a aucune preuve d'exécution.
- Le pipeline complet record → replay → compétence n'a jamais tourné en conditions réelles : M2 live n'a pas encore eu lieu. « Le premier vrai test sera devant le client » si on ne fait pas M2 avant.
- Incidents récurrents de son registre : worker zombie 5 jours (résolu par watchdog N3), tunnel Ollama instable (stabilisé systemd), UI-TARS 500 non détecté (toujours 0 test dédié), OOM VRAM GB10 (fixé).
- DETTE-015 jugée fragile : le symlink a déjà cassé une fois (P0-1) ; peut resurgir si cwd change.
Point de tension à arbitrer en M2 (pas tranché)
Qwen affirme : « si le serveur redémarre, les agents Windows tombent — pas de reconnexion automatique ». Mon audit client a trouvé buffer SQLite persistant + retry + backoff + health-check 30s (streamer.py). Les deux peuvent être vrais : le transport se reconnecte, mais la session/replay en cours ne reprend probablement pas après un restart serveur. À vérifier explicitement pendant M2 (test : restart serveur en cours de session).
Verdict Qwen
- 1 TIM en démo contrôlée : prêt (sous réserve DETTE-006/010).
- 5 TIM réels en clinique : pas prêt — le gap n'est pas dans le code métier (OCR, VLM, grounding) mais dans l'infra multi-utilisateur : sessions, isolation, monitoring, résilience.
Ce verdict est compatible avec la priorisation §6 et la renforce : l'Horizon 2 doit inclure un test de charge multi-agents (2-3 agents simultanés minimum) avant la clinique, en plus des filets de sécurité.
Annexe — Contradictions inter-agents résolues (contre-vérifiées à la main)
| Affirmation agent d'audit | Verdict après vérification |
|---|---|
| « t2a_decision non implémenté » | FAUX sur le chemin Léa : implémenté agent_v0/server_v1/replay_engine.py:1922 + handlers :2045+. Vrai uniquement pour le chemin local execute.py. |
« {{var.field.sub}} ne marche pas » |
FAUX sur le chemin Léa : path.split('.') replay_engine.py:2017. Vrai uniquement chemin local. |
| « Le chemin replay vers Léa est démis / Léa n'existe plus » | FAUX : pont learned_workflow_bridge.py côté VWB + polling /replay/next côté client actifs. Les deux chemins coexistent — c'est l'asymétrie connue. |
Leçon : tout audit ou modification doit d'abord identifier sur quel chemin d'exécution il porte.