11 KiB
CR — Durcissement du setup auto Windows (gardes visuelles + skip pixel-change)
Date : 2026-05-22
Branche : backup/post-demo-2026-05-19
Périmètre : agent_v0/server_v1/replay_engine.py, agent_v0/agent_v1/core/executor.py, agent_v0/agent_v1/ui/chat_window.py
Statut : patch + tests implémentés et verts (104/104 sur le périmètre).
1. Constat live (run du 22 mai 2026 — replay_sess_76b7d067)
Sur sess_20260520T102916_066851, le trim neutral=True passe correctement (patch 20 mai). En revanche, le setup auto Windows enchaîne ses étapes sans aucune garde visuelle intermédiaire :
act_setup_sess_click_startfinit enposition_fallback(clic blind, sans résolution VLM).- Succès jugé sur simple changement d'écran (
_wait_for_screen_changedansexecutor.py:1313). type_searchetwait_resultspartent sans garde de fenêtre.click_app_resultdécouvre trop tard que la fenêtre attendue n'est pasRecherchermaisFenêtre de dépassement de capacité de la barre d'état système..- Pendant ce temps,
bloc-notesa déjà été tapé dans la mauvaise surface (le popup overflow), polluant l'état.
2. Cause racine
Deux trous combinés :
| # | Trou | Détail |
|---|---|---|
| A | Pas de pré/post-conditions visuelles entre les étapes du setup | verify_screen côté agent (executor.py:1196) ne faisait qu'un time.sleep et déléguait toute vérification au serveur — qui n'a pas de node CLIP pour ces étapes intermédiaires |
| B | Validation par simple pixel-change sur click_start |
executor.py:1313 considère un click _setup_phase valide dès qu'un seul pixel change. Or l'overflow popup change l'écran sans pour autant ouvrir le bon menu |
3. Patch minimal — nouveau contrat de contrôle visuel
3.1 Nouvelle chaîne setup (12 actions, 3 gardes intermédiaires + 1 finale)
1. click_start_menu clic visuel, fallback x/y
2. wait_start_menu 1000 ms
3. verify_start_menu_open GARDE 1 — titre ∈ {Rechercher, Search,
Cortana, Démarrer,
Start, SearchHost,
StartMenuExperienceHost}
4. click_search_box clic visuel (uniquement si search_mode = click_then_type)
5. wait_search_ready 500 ms
6. verify_search_box_active GARDE 2 — titre ∈ {<title_session_source>,
Rechercher, Search}
7. type_app_name frappe « Bloc-notes »
8. wait_search_results 1200 ms
9. verify_search_results_visible GARDE 3 — titre ∈ {Rechercher, Search,
Cortana, SearchHost,
StartMenuExperienceHost}
10. click_app_result clic visuel + expected_window_before
11. wait_app_launch 2000 ms (3000 pour Office)
12. verify_screen final CLIP node setup_initial (pré-existant)
3.2 Mécanisme des gardes (nouveau champ sur verify_screen)
Champ ajouté sur le contrat verify_screen : expected_window_title_contains: List[str]. Côté agent :
time.sleepd'attente (comportement legacy).- Si patterns présents :
get_active_window_info()→ comparaison substring case-insensitive avec les patterns. - Match positif → succès, on continue.
- Match négatif → bascule en mode apprentissage humain (
_capture_human_correction, 120 s).- Si l'utilisateur agit : warning
setup_guard_window_mismatch, success=True, la correction remonte au serveur. - Si timeout : success=False,
needs_human=True, pause supervisée.
- Si l'utilisateur agit : warning
Pas de changement côté serveur. Le node CLIP final (setup_initial) reste pour le verify post-launch.
3.3 Skip pixel-change pour _setup_phase
Dans executor.py, juste avant la branche _wait_for_screen_change :
is_setup_action = bool(action.get("_setup_phase"))
if needs_screen_check and hash_before and is_setup_action:
# Setup phase : pixel-change neutralisé, la garde verify_screen tranche
time.sleep(0.5)
elif needs_screen_check and hash_before:
# Comportement legacy pour les actions utilisateur
...
Conséquences :
click_start_menune peut plus être validé sur la seule ouverture du systray overflow popup.- Le verify_screen suivant détecte le mauvais titre fenêtre et déclenche immédiatement le mode apprentissage.
- Les actions utilisateur hors setup conservent strictement le comportement précédent (non-régression vérifiée).
3.4 Fix troncature bulle pause supervisée (livré au tour précédent)
Pour mémoire — déjà appliqué :
chat_window._compute_paused_bubble_height(reason_str): helper statique testable.- Calcul :
max(wrapped_lines, explicit_lines)avec cap à 12 lignes (vs 8 avant). - Scrollbar activée dès que cap atteint OU contenu ≥ 200 chars (vs > 280 chars avant).
- Les longs
reasonserveur listant plusieurs candidats (avec\n) ne sont plus tronqués silencieusement.
4. Fichiers modifiés
| Fichier | Modification | SCP Windows |
|---|---|---|
agent_v0/server_v1/replay_engine.py |
_generate_setup_actions : insertion de 3 actions verify_screen (verify_start_menu_open, verify_search_box_active, verify_search_results_visible) |
Non |
agent_v0/agent_v1/core/executor.py |
Helper statique _window_title_matches_any ; branche verify_screen étendue avec garde titre fenêtre + mode apprentissage ; skip _wait_for_screen_change pour _setup_phase=True |
Oui → C:/rpa_vision/agent_v1/core/executor.py |
agent_v0/agent_v1/ui/chat_window.py |
Helper statique _compute_paused_bubble_height ; cap relevé à 12 lignes, scrollbar dès cap atteint ou ≥ 200 chars (tour précédent) |
Oui → C:/rpa_vision/agent_v1/ui/chat_window.py |
⚠️ Le miroir agent_v0/deploy/windows_client/ est obsolète (setup initial uniquement). Canal d'incrémental réel = SCP manuel direct vers C:/rpa_vision/.
5. Tests ajoutés ou adaptés
| Fichier | Nature | Tests |
|---|---|---|
tests/unit/test_env_setup.py |
NEW classe TestSetupVisualGuards |
6 tests : insertion verify_start_menu_open, verify_search_box_active (mode click_then_type), absence en direct_typing, verify_search_results_visible toujours présent (les 2 modes), timeout ≤ 2 s sur toutes les gardes |
tests/unit/test_env_setup.py |
Adaptation de 5 tests existants | test_notepad_setup_visual (12 actions), test_skips_search_click_for_direct_typing, test_verify_screen_final_present_with_title, test_no_final_verify_without_title, test_full_pipeline_from_events (séquence canonique mise à jour) |
tests/unit/test_executor_verify_window_guard.py |
NEW fichier | 13 tests : helper _window_title_matches_any (7 cas) + routage garde (4 cas : match, mismatch+correction, mismatch+timeout, neutre sans patterns) + skip pixel-change _setup_phase (2 cas : setup skippe, hors-setup garde le comportement) |
tests/unit/test_chat_window_paused_dispatch.py |
Ajout classe TestPausedBubbleHeight (tour précédent) |
6 tests : empty, court, long single line, multi-lignes \n, cap atteint, seuil 200 chars |
tests/integration/test_replay_session_trim_neutral.py |
Inchangé (tour précédent) | 1 test bout-en-bout — toujours vert avec le nouveau setup |
Bilan tests sur le périmètre : 104 / 104 verts.
cd /home/dom/ai/rpa_vision_v3
source .venv/bin/activate
set -a && source .env.local && set +a
python -m pytest \
tests/unit/test_env_setup.py \
tests/unit/test_executor_verify_window_guard.py \
tests/unit/test_chat_window_paused_dispatch.py \
tests/unit/test_server_client_replay_controls.py \
tests/integration/test_replay_session_trim_neutral.py -v
6. Comportement attendu en live
Après SCP executor.py + chat_window.py et redémarrage Léa, sur un nouveau /replay-session de sess_20260520T102916_066851 :
| Scénario | Log Léa attendu | Issue |
|---|---|---|
click_start touche le vrai bouton Windows |
[LEA] verify_screen garde OK : 'Recherche' matche [...] |
Setup avance, frappe protégée |
click_start ouvre systray overflow popup |
Pixel-change observé MAIS log explicite Setup action … : validation pixel-change skippée (garde verify_screen ultérieure) puis [LEA] verify_screen garde KO : attendu un titre contenant [...], actuel 'Fenêtre de dépassement…' |
Mode apprentissage humain immédiat, aucune frappe à l'aveugle |
Focus perdu pendant wait_search_results (notification surgit) |
[LEA] verify_screen garde KO sur verify_search_results_visible |
Apprentissage humain avant click_app_result |
Bulle de pause avec un long reason |
Scrollbar visible | Plus de troncature |
7. Risques / limites
- Patterns FR+EN uniquement : couverture Windows 10/11 FR et EN. Sur OS exotique (DE, ES, ZH), il faudra étendre
expected_window_title_contains. Localisé dans_generate_setup_actions, extension triviale. - Skip pixel-change conditionné à
_setup_phase: seules les actions marquées_setup_phase=Trueperdent la validation pixel-change. Si une future contribution ajoute une action setup sans garde verify_screen derrière, on perdrait le filet. À surveiller / documenter dans la convention de génération. - Mode
direct_typing: couverture parverify_start_menu_open(avant frappe) +verify_search_results_visible(avant clic résultat). Pas deverify_search_box_activecar pas declick_search_boxà valider — testé explicitement. - Helper
_compute_paused_bubble_height: prend en compte les\nexplicites et la longueur ; cap 12 lignes. Compromis volontairement conservateur — afficher une scrollbar légèrement trop tôt vaut mieux que tronquer du contenu critique de pause.
8. Synthèse pour décision
- Avant ce patch : setup auto enchaînait click → wait → type → click_result sans contrôle entre, et un seul changement de pixel suffisait à valider la première étape. Constat live =
bloctapé dansFenêtre de dépassement…, click_result en erreur tardive,paused_need_help. - Après ce patch : 3 gardes verify_screen titre fenêtre + skip pixel-change setup → chaque transition critique est verrouillée. Mode apprentissage humain immédiat à la première dérive. Pixel-change ne décide plus de la validité d'une étape setup.
- Scope : 2 fichiers prod modifiés (≈ 90 lignes ajoutées dans
replay_engine.py, ≈ 75 dansexecutor.py), 2 fichiers test (≈ 350 lignes neuves + adaptations). Aucun changement côté serveur ni protocole. - SCP :
executor.pyetchat_window.pyà pousser versC:/rpa_vision/agent_v1/…avant relance Léa.replay_engine.pyreste côté serveur Linux. - Validation live à faire : lancer un
/replay-sessionsursess_20260520T102916_066851, vérifier la présence des 3 logsverify_screen garde OK(ou un mode apprentissage propre en cas de dérive).