diff --git a/agent_v0/server_v1/replay_engine.py b/agent_v0/server_v1/replay_engine.py index 583ae3078..47a5be132 100644 --- a/agent_v0/server_v1/replay_engine.py +++ b/agent_v0/server_v1/replay_engine.py @@ -1432,14 +1432,38 @@ def _schedule_retry( # Stratégie de retry selon le numéro actions_to_insert = [] - if next_retry == 2: - # Retry 2 : injecter un wait de 2s avant l'action - wait_action = { + if next_retry == 1 and reason in ("verification_failed", "no_screen_change"): + # Auto-recovery par gesture : tenter Win+D ("Afficher le bureau") + # avant le 1er retry quand l'action n'a rien changé à l'écran. + # Hypothèse : une fenêtre/popup non bloquante obstrue la cible. + # Cf. gesture_catalog.win_minimize_all (agent_chat/gesture_catalog.py:84) + # et audit project-quality-guardian Cas #2 — self-healing par gesture + # archivé le 24/04 dans _archive/dead_code_20260424/, reconnecté ici + # côté serveur dans le pipeline de retry plutôt que dans la cascade + # ORALoop (qui n'est plus sur le chemin actif du replay). + actions_to_insert.append({ + "action_id": f"recovery_win_d_{uuid.uuid4().hex[:6]}", + "type": "key_combo", + "keys": ["super", "d"], + "_recovery_gesture": "win_minimize_all", + }) + # Petit wait après Win+D pour laisser l'OS terminer l'animation + actions_to_insert.append({ + "action_id": f"wait_recovery_{uuid.uuid4().hex[:6]}", + "type": "wait", + "duration_ms": 500, + }) + logger.info( + "Auto-recovery : injection Win+D avant retry %d/%d (raison: %s)", + next_retry, max_retries, reason, + ) + elif next_retry == 2: + # Retry 2 : injecter un wait de 2s avant l'action (loading possible) + actions_to_insert.append({ "action_id": f"wait_retry_{uuid.uuid4().hex[:6]}", "type": "wait", "duration_ms": 2000, - } - actions_to_insert.append(wait_action) + }) actions_to_insert.append(retry_action)