# Préparation validation réelle — flux `finalize → proposition utilisateur → replay-session` **Date** : 2026-05-20 **Mission** : Claude 5 (lecture seule) **Périmètre** : `agent_v0/deploy/windows_client/**`, `agent_v0/agent_v1/**`, scripts SCP, docs déploiement **Branche** : `backup/post-demo-2026-05-19` (HEAD `5ea4960e6`, 5 fichiers uncommitted + 1 untracked) ## Constat Le câblage agent-side du contrat enrichi `/finalize` est **déjà fait et présent dans la branche locale** : - `streamer.py:96-103, 627-635` — attribut `_on_finalize_result`, setter `set_on_finalize_result`, invocation après `resp.ok` (catch d'erreur du callback en log warning). - `main.py:31` — import `dispatch_finalize_result`. - `main.py:222` — wire `self.streamer.set_on_finalize_result(self._on_finalize_result)` dans `start_session`. - `main.py:445-448` — `_on_finalize_result(payload)` dispatche via `dispatch_finalize_result(self.ui, payload, replay_name)` avec `replay_name = self._last_recording_name`. - `finalize_contract.py` (untracked) — module pur, `dispatch_finalize_result(ui, payload, replay_name)` route selon `replay_launch.status` (`started`/`failed`) et `replay_ready + replay_request`. - `smart_tray.py:577-599` — `offer_finalize_replay(replay_request, replay_name)` : notification toast + dialog `_ask_consent` Article 14, puis `_launch_replay_request(replay_request, replay_name)` si accepté. Le contrat attendu côté serveur s'appelle **`replay_launch`** (status `started`/`failed`), pas `launch_replay` comme supposé en mission 4. Les 3 clés consommées : `replay_ready: bool`, `replay_request: dict` (doit contenir `session_id`), `replay_launch: dict {status}`. **Synchro vers Léa Windows** : aucun script SCP automatique présent dans le repo. Le canal réel est un `sshpass scp` manuel fichier par fichier vers `dom@192.168.1.11:C:/rpa_vision/agent_v1/` (cf. `memory/feedback_scp_auto_modif_client_windows.md` + handoff 2026-05-16). Le dossier `agent_v0/deploy/windows_client/` existe mais sert au **setup initial** (setup.bat), pas au déploiement incrémental — ses fichiers miroirs sont **obsolètes de 2 à 7 semaines** par rapport à la source. Le `BUILD_DEPLOY_GUIDE.md` (24 novembre 2025) parle de l'ancien `agent_v0/` mode upload chiffré — **obsolète** pour le flux agent_v1 streaming actuel. ## Fichiers à déployer ### Tableau fichier / déploiement / motif | Fichier source | Doit être déployé ? | Pourquoi | |---|---|---| | `agent_v0/agent_v1/main.py` | ✅ **OUI** | Lignes 31 (import), 222 (wire), 445-448 (`_on_finalize_result`) ajoutées — sans ce fichier, le callback n'est pas wired | | `agent_v0/agent_v1/network/streamer.py` | ✅ **OUI** | Lignes 96-103 + 627-635 ajoutées — sans ce fichier, le payload `/finalize` est jeté dans un log comme avant | | `agent_v0/agent_v1/ui/smart_tray.py` | ✅ **OUI** | Méthode `offer_finalize_replay` (577-599) ajoutée — sans elle, `finalize_contract.py` log "UI indisponible" et abandonne | | `agent_v0/agent_v1/finalize_contract.py` | ✅ **OUI (NOUVEAU FICHIER)** | Untracked en git, n'existe nulle part sur Léa Windows → **`ImportError` au démarrage de l'agent** si oublié (import ligne `main.py:31`) | | `agent_v0/agent_v1/vision/capturer.py` | ✅ **OUI** | Fix monitor missions 2/2b (garde dims aberrantes + fallback) — pertinent pour la fiabilité de la session enregistrée qui alimente le `replay_request` | | `agent_v0/agent_v1/core/executor.py` | 🟡 Si modifié séparément | Apparaît dans `git status` (modifié dans session antérieure), à vérifier avec `git diff` avant SCP pour ne pas pousser un changement non validé | | `agent_v0/agent_v1/ui/smart_tray.py` (méthode `_launch_replay_request`) | ✅ Inclus dans smart_tray.py | Référencée par `offer_finalize_replay`, ligne 507 | | `agent_v0/agent_v1/config.py` | ⚫ Non | Pas modifié par les missions récentes | | `agent_v0/agent_v1/ui/chat_window.py` | ⚫ Non | Pas modifié par mission 4 | | `agent_v0/agent_v1/ui/shared_state.py` | ⚫ Non | Pas modifié | | `agent_v0/agent_v1/network/persistent_buffer.py` | ⚫ Non | Pas modifié | | `agent_v0/agent_v1/network/feedback_bus.py` | ⚫ Non | Pas modifié | | `agent_v0/lea_ui/server_client.py` | ⚫ Non | Pas modifié, et probablement non utilisé au runtime actuel (main a son propre poll loop) | | `agent_v0/deploy/windows_client/**` | ⚫ Non | Miroir obsolète, sert au setup initial, ne fait pas partie du canal de déploiement incrémental | ### La synchro actuelle couvre-t-elle ? | Fichier | Couvert par synchro auto ? | Statut | |---|---|---| | `agent_v0/agent_v1/network/streamer.py` | ❌ NON | Aucun script auto — SCP manuel obligatoire | | `agent_v0/agent_v1/ui/smart_tray.py` | ❌ NON | Aucun script auto — SCP manuel obligatoire | | `agent_v0/agent_v1/main.py` | ❌ NON | Aucun script auto — SCP manuel obligatoire | | `agent_v0/agent_v1/finalize_contract.py` | ❌ NON (cas critique) | Fichier NEUF, untracked git, **absent partout sur Léa** → ImportError garanti si oublié | **Conclusion synchro** : il faut SCP les 4 fichiers manuellement. Le risque principal est d'oublier `finalize_contract.py` (nouveau) car il n'apparaîtra pas dans un diff incrémental classique — le réflexe "je SCP les fichiers modifiés" ne le couvre pas. **Doit être SCP en premier.** ### Commandes SCP de référence ```bash SSHPASS='loli' sshpass -e scp -o StrictHostKeyChecking=no \ /home/dom/ai/rpa_vision_v3/agent_v0/agent_v1/finalize_contract.py \ dom@192.168.1.11:C:/rpa_vision/agent_v1/finalize_contract.py SSHPASS='loli' sshpass -e scp -o StrictHostKeyChecking=no \ /home/dom/ai/rpa_vision_v3/agent_v0/agent_v1/network/streamer.py \ dom@192.168.1.11:C:/rpa_vision/agent_v1/network/streamer.py SSHPASS='loli' sshpass -e scp -o StrictHostKeyChecking=no \ /home/dom/ai/rpa_vision_v3/agent_v0/agent_v1/ui/smart_tray.py \ dom@192.168.1.11:C:/rpa_vision/agent_v1/ui/smart_tray.py SSHPASS='loli' sshpass -e scp -o StrictHostKeyChecking=no \ /home/dom/ai/rpa_vision_v3/agent_v0/agent_v1/main.py \ dom@192.168.1.11:C:/rpa_vision/agent_v1/main.py SSHPASS='loli' sshpass -e scp -o StrictHostKeyChecking=no \ /home/dom/ai/rpa_vision_v3/agent_v0/agent_v1/vision/capturer.py \ dom@192.168.1.11:C:/rpa_vision/agent_v1/vision/capturer.py ``` ## Checklist smoke ### Pré-requis machine (à valider une fois) | Item | Vérification | |---|---| | Léa Windows installée à `C:\rpa_vision\` | `dir C:\rpa_vision\agent_v1\main.py` doit répondre | | Venv `.venv` ou `.venv_v1_win` présent avec dépendances | `C:\rpa_vision\.venv\Scripts\python.exe -c "import pystray, pynput, mss, plyer; print('ok')"` | | Token API exporté côté Windows | `echo %RPA_API_TOKEN%` (ou variable session) — sinon `set RPA_API_TOKEN=` | | Serveur Linux accessible | depuis Windows : `curl -H "Authorization: Bearer %RPA_API_TOKEN%" http://192.168.1.40:5005/health` | | `rpa-streaming` actif côté Linux | `systemctl --user status rpa-streaming` | | VWB backend actif (port 5002) | optionnel — la mission 5 ne le requiert pas | | Léa Windows actuellement quittée | tray icône absente | ### Déploiement (≈ 1 min) 1. **SCP `finalize_contract.py` EN PREMIER** (fichier neuf, sinon ImportError au prochain `python run_agent_v1.py`). 2. SCP `streamer.py`, `smart_tray.py`, `main.py`, `capturer.py` (ordre indifférent). 3. Côté Léa Windows : ouvrir le dossier `C:\rpa_vision\agent_v1\` et confirmer les 5 fichiers ont la date du jour. 4. Lancer Léa : double-clic raccourci ou `C:\rpa_vision\.venv\Scripts\pythonw.exe run_agent_v1.py`. 5. Vérifier qu'aucune fenêtre console n'apparaît avec une stack trace `ImportError: finalize_contract`. Si oui → relancer avec `python.exe` (pas `pythonw.exe`) pour voir l'erreur, ou re-SCP `finalize_contract.py`. 6. Vérifier l'icône systray apparaît (cercle gris/orange selon connexion serveur). ### Test nominal (≈ 2 min) | # | Action utilisateur | Logs Léa Windows attendus | Logs serveur Linux attendus | Comportement attendu | |---|---|---|---|---| | 1 | Clic droit tray → "Apprenez-moi une tâche" | `agent_v0.agent_v1.ui.shared_state Enregistrement demarre : ...` + `agent_v1.network.streamer Streamer pour sess_... démarré` | `[REGISTER] session sess_... machine=desktop-58d5cac` | Notif "C'est parti !" | | 2 | Saisir nom "test_finalize_replay", confirmer | idem | idem | Tray icône passe rouge | | 3 | Faire 3-5 clics dans une app simple (calculatrice, notepad) | `Action capturée : mouse_click` × N | événements + screenshots reçus dans `/event` `/image` | Compteur "X étapes mémorisées" augmente dans le menu tray | | 4 | Clic droit tray → "C'est terminé" | `Enregistrement arrete : test_finalize_replay (N actions)` puis `Streamer ... arrêté` puis **`Session finalisée: {...replay_ready: true, replay_request: {session_id: sess_..., ...}, replay_launch: {status: ...}}`** | `[FINALIZE] sess_... → workflow construit, replay_request retourné` | Notif "Merci ! J'ai bien mémorisé vos N actions." | | 5 | (auto) | `agent_v1.ui.smart_tray.offer_finalize_replay : proposition test 'test_finalize_replay'` | rien | Notif toast "J'ai compris la tâche 'test_finalize_replay'. Voulez-vous la tester ?" | | 6 | Dialog tkinter Yes/No s'ouvre, cliquer **Oui** | `agent_v1.ui.smart_tray Replay demarre pour workflow ...` | `[REPLAY_START] replay_id=replay_..., workflow=...` puis actions enqueues | Tray icône passe bleu (replay actif), notification Article 50 "Le système d'intelligence artificielle exécute la tâche..." | | 7 | Observer Léa rejouer les clics | `Replay action received : click ...` × N puis `Replay terminé — retour en mode capture` | actions dispatchées via `/replay/next`, résultats via `/replay/result` | Les clics sont rejoués à l'écran. Tray icône revient vert. Notif "C'est fait !" | **Critère succès global** : 7 étapes en ≤ 5 min, sans intervention manuelle entre 4 et 7 autre que le dialog `_ask_consent`. ### Test d'échec (variantes à essayer après le nominal) | Variante | Action | Comportement attendu | |---|---|---| | Refus utilisateur | Étape 6 : cliquer **Non** | `_launch_replay_request` non appelé, aucun POST `/replay/start`, log Léa silencieux après `offer_finalize_replay`, retour mode capture sans replay | | Serveur ne renvoie pas `replay_ready` | Couper le câblage côté serveur pour qu'il renvoie juste `{}` | Léa log `Session finalisée: {}`, **aucune notif**, pas de dialog. Comportement = backward-compat parfaite | | Serveur renvoie `replay_launch.status=started` | Test serveur déjà lance le replay | Log Léa `Replay direct déjà lancé par le serveur après finalize`, dialog **NON affiché** (logique `dispatch_finalize_result:20-22`) | | Serveur renvoie `replay_launch.status=failed` | Idem | Log warning `Auto-replay serveur échoué après finalize, proposition manuelle`, dialog **affiché** (fallback) | | `finalize_contract.py` absent | Ne pas SCP ce fichier, relancer Léa | `ImportError: cannot import name 'dispatch_finalize_result'` au démarrage, agent ne démarre pas | | `offer_finalize_replay` absent (smart_tray ancien) | Ne pas SCP smart_tray.py, relancer | Log `UI indisponible pour proposer un test immédiat`, pas de dialog, replay non lancé | | Network coupé pendant finalize | Débrancher Wi-Fi pile au "C'est terminé" | Log Léa `Finalisation échouée: `, callback non invoqué, aucune notif. Session reste localement, retry implicite au prochain run (buffer SQLite) | | Replay impossible (workflow vide ou bug) | Faire 0 clic pendant l'enregistrement avant Stop | Selon serveur : si `replay_ready=false`, dispatch silencieux ligne 24. Pas de dialog, comportement attendu = ne pas proposer un test inutile | ### Signes d'échec à surveiller - **Stack trace au démarrage Léa** → `finalize_contract.py` manquant ou syntax error - **Aucun log `Session finalisée:` après "C'est terminé"** → streamer n'arrive pas à appeler `/finalize` (réseau ? auth ? port ?) - **Log `Session finalisée: {...}` MAIS pas de notif Léa** → soit le payload ne contient pas `replay_ready/replay_request`, soit `offer_finalize_replay` n'est pas sur smart_tray (ancien fichier) - **Notif affichée mais pas de dialog Yes/No** → bug tkinter (rare) ou thread bloqué (vérifier `_ask_consent` qui crée son propre Tk()) - **Dialog accepté mais aucun POST `/replay/start` côté serveur** → `_launch_replay_request` mal câblé ou `replay_request` invalide (session_id absent) ## Risques / points ouverts 1. **`finalize_contract.py` jamais committé** — `git status` le marque untracked. Risque : disparition silencieuse au prochain `git stash` ou `git checkout`. À committer avant la session de test. 2. **Le SCP des 5 fichiers n'est pas atomique** — si Léa est relancée entre le SCP de 2 fichiers, état incohérent possible. Mitigation : SCP les 5 fichiers d'abord, **puis** relancer Léa, jamais l'inverse. 3. **Le miroir `agent_v0/deploy/windows_client/agent_v1/` est obsolète de 7 semaines** — risque de confusion si quelqu'un essaie de redéployer un poste neuf via `setup.bat`. Hors scope mission 5 mais à signaler pour le prochain client. 4. **`_last_recording_name` peut être vide si `start_session` est appelé avec une chaîne vide** — `_on_finalize_result` fallback sur "la tâche que vous venez d'enregistrer", testé dans `finalize_contract.py:38`. OK mais l'UX dégrade. 5. **Le contrat serveur n'est pas encore validé** — la mission concerne le côté agent. Vérifier côté serveur que `/finalize` retourne bien `{replay_ready, replay_request, replay_launch}` AVANT de smoke-tester. Si le serveur retourne encore l'ancien payload, le test nominal s'arrêtera après étape 4 sans crash mais sans proposition (variante 2 du test d'échec). 6. **Token API `RPA_API_TOKEN` côté Windows** — si la variable n'est pas définie dans la session Windows actuelle, le `register` et le `finalize` retourneront 401 silencieusement (cf. `streamer.py:573-600`). Sympôme : log Léa `Enregistrement session échoué: 401`. 7. **Pas de test d'idempotence** — si l'utilisateur enchaîne 2 enregistrements rapidement et accepte le test du premier, le `_replay_active` pourrait masquer un second `offer_finalize_replay`. À surveiller mais hors scope ici. 8. **Article 14 / Article 50** — le dialog `_ask_consent` couvre l'Article 14 (contrôle humain avant lancement auto). La notification Article 50 (transparence "le système IA exécute...") est déjà câblée dans `_launch_replay_request` réutilisé. Conforme. ## Méthode d'audit - Lecture intégrale : `finalize_contract.py` (40 lignes), `LISEZMOI.txt` deploy, `setup.bat`. - Lecture déjà réalisée (audits précédents 2026-05-19/20) : `streamer.py`, `main.py`, `smart_tray.py` complets, `vision/capturer.py`. - Lecture ciblée : `BUILD_DEPLOY_GUIDE.md` (constate obsolescence), `feedback_scp_auto_modif_client_windows.md`, handoff 2026-05-16 (commandes SCP de référence). - Grep : consumers `dispatch_finalize_result`, `offer_finalize_replay`, `_launch_replay_request`, `_last_recording_name`. - Diff dates source vs miroir deploy/windows_client pour valider obsolescence. - `git diff agent_v0/agent_v1/network/streamer.py` pour confirmer la nature exacte des modifs. - **Aucune modification de code, aucune action VWB, aucune modif de script** — conformément aux interdits.