feat(navigate): jalon partiel D1 — compile navigate + coercion coords sûre

Ferme Gap C : _edge_to_normalized_actions produit désormais une action navigate
(handler serveur atteignable). Ajoute _coerce_action_coords : cast x_pct/y_pct en
float APRÈS résolution des templates, JAMAIS de fallback (0,0) — template non
résolu / valeur invalide → pause_for_human (safety_level=high). Non-régression
prouvée sur mouse_click classiques (idempotent sur floats).

⚠️ NE FERME PAS le write-only : Gap A (P1-B) non livré — aucun step click/type ne
déclare encore consommer navigate_login_coords. TestCompilerGapLiteralFloats
assert l'état ouvert. Boucle complète = chantier suivant (P1-B + test e2e edge→action).

21 tests verts, boot OK. Revue croisée Claude (GO jalon partiel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dom
2026-07-02 18:49:32 +02:00
parent fd9efdbbf5
commit 931cf13217
5 changed files with 841 additions and 6 deletions

View File

@@ -420,6 +420,7 @@ from .replay_engine import (
_edge_to_normalized_actions,
_substitute_variables,
_resolve_runtime_vars,
_coerce_action_coords,
_SERVER_SIDE_ACTION_TYPES,
_handle_extract_text_action,
_handle_extract_table_action,
@@ -4334,6 +4335,9 @@ async def get_next_action(session_id: str, machine_id: str = "default"):
if runtime_vars:
action = _resolve_runtime_vars(action, runtime_vars)
# Coercion coords: cast x_pct/y_pct en float après resolver
action = _coerce_action_coords(action)
type_ = action.get("type")
# pause_for_human : pause supervisée si safety_level/safety_checks ou mode supervised,