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>
7.6 KiB
7.6 KiB
QG Review Framework — D1 NavigateCoords Patch
Auteur: Qwen
Date: 2026-07-02
Statut: EN ATTENTE patch Codex
Scope: Review du patch D1 (Option 1 — Compiler Injection) produit par Codex
Baseline test coverage (pré-patch)
| Fichier | Classes | Tests | Rôle |
|---|---|---|---|
test_navigate_handler_e2e.py |
4 | 8 | Handler mock — nominal, OCR miss, no screenshot, never-fail |
test_navigate_wiring.py |
4 | 5 | Import/wiring non-regression |
test_action_resolver.py |
6 | 10 | NavigateCoords, NavigateResult, grounded_to_coords, navigate_login |
test_coords_consumption_gap.py |
3 | 10 | GAP DOCUMENTATION — résolution viable, compiler gap, navigate→[] |
| Total | 17 | 33 |
Tests critiques à mettre à jour après D1 patch:
test_coords_consumption_gap.py::test_navigate_action_type_unknown— affirme actuellementactions == []; doit affirmerlen(actions) >= 1etactions[0]["type"] == "navigate"après D1test_coords_consumption_gap.py::TestCompilerGapLiteralFloats— 4 tests documentant le gap literal-floats; après D1, les tests coords_var doivent affirmer templates strings ARE produites quand coords_var présent
Point d'insertion exact D1:
- Fichier:
replay_engine.py - Entre
elif action_type == "llm_generate"(retourne[normalized]~L1949) etelse:clause (~L1953) - Navigate branch:
elif action_type == "navigate"→normalized["type"] = "navigate"+ parameters dict →return [normalized]
P1-C root cause:
_resolve_runtime_vars_in_str(L2025):return str(value)— tout {{var.field}} résolu devient string "0.35" pas float 0.35- Coercion helper
_coerce_action_coordsdoit agir APRÈS_resolve_runtime_vars(L4335), AVANTtype_ = action.get("type")(L4337)
Critères de review — Checklist
C1 : Branche navigate dans _edge_to_normalized_actions (Gap C)
| # | Critère | GO | NOGO |
|---|---|---|---|
| C1-1 | Branche elif action_type == "navigate" ajoutée entre llm_generate (L1949) et else (L1951) |
Present, position correcte | Absente ou mal positionnée |
| C1-2 | normalized["type"] = "navigate" |
Oui | Type incorrect |
| C1-3 | Parameters dict avec login_coords_var, password_coords_var, submit_coords_var |
Noms exacts, valeurs default | Noms divergent ou absents |
| C1-4 | Retourne [normalized] (1 action serveur-side) |
[normalized] |
[] ou autre |
| C1-5 | Test TR-1 : test_navigate_action_type_unknown mis à jour — affirme len(result) >= 1 et result[0]["type"] == "navigate" |
Test updated + passes | Test non mis à jour ou fails |
C2 : coords_var dans branches mouse_click / text_input (Gap A)
| # | Critère | GO | NOGO |
|---|---|---|---|
| C2-1 | coords_var = action_params.get("coords_var") check dans mouse_click |
Present | Absent |
| C2-2 | Si coords_var → x_pct = f"{{{{{coords_var}.x_pct}}}" et y_pct = f"{{{{{coords_var}.y_pct}}}" |
Template strings correctes | Syntaxe template incorrecte ou .y_pct pour x_pct |
| C2-3 | Si coords_var absent → literal floats comme avant (fallback existant) | Branch else intacte | Branch else modifiée ou supprimée |
| C2-4 | normalized["coords_var"] = coords_var ajouté pour traçabilité |
Oui | Absent |
| C2-5 | Même mécanisme dans text_input branch | Identique à mouse_click | Absent ou divergent |
| C2-6 | BUG vérifié : text_input x_pct template = {{coords_var.x_pct}} (pas .y_pct deux fois) |
Correct | y_pct en double |
C3 : _coerce_action_coords() helper (Gap B / P1-C)
| # | Critère | GO | NOGO |
|---|---|---|---|
| C3-1 | Helper défini dans api_stream.py (pas replay_engine.py) | api_stream.py | Autre fichier |
| C3-2 | Appel APRÈS _resolve_runtime_vars (L4335), AVANT type_ = action.get("type") (L4337) |
Position correcte | Avant resolver ou après type_ check |
| C3-3 | float pass-through : isinstance(val, float) → continue |
Idempotent sur actions existantes | Pas de float check → conversion inutile |
| C3-4 | string→float : try: action[key] = float(val) |
Conversion correcte | Pas de try/except → crash possible |
| C3-5 | Template non résolu → pause_for_human (pas fallback 0.0/0.0) | val.startswith("{{") and val.endswith("}}") → pause_for_human |
Fallback 0.0/0.0 ou autre coords dangereux |
| C3-6 | Conversion impossible → pause_for_human | ValueError/TypeError → pause_for_human | Exception non catchée |
| C3-7 | _skip_reason documenté pour debug |
Oui | Absent |
| C3-8 | safety_level = "high" pour pause_for_human |
Oui | Absent ou autre valeur |
| C3-9 | Retourne action mutée (pas de new dict) | Mutation in-place | Copie → risque race |
| C3-10 | Keys itérées = ("x_pct", "y_pct") uniquement | Pas de sur-itération | Autres keys modifiées |
C4 : Never-fail contract
| # | Critère | GO | NOGO |
|---|---|---|---|
| C4-1 | _handle_navigate_action ne lance jamais d'exception non catchée |
Contract preserved | Nouvelle exception possible |
| C4-2 | _coerce_action_coords ne lance jamais — tout cas couvert par try/except ou pause_for_human |
Contract preserved | Exception possible |
C5 : Limites de scope POC
| # | Critère | GO | NOGO |
|---|---|---|---|
| C5-1 | Maximum 4 fichiers modifiés | ≤ 4 | > 4 |
| C5-2 | Pas de changement schema VWB dans POC patch | Pas de modification VWB code | VWB code modifié |
| C5-3 | Pas de nouvelle dépendance pip | 0 nouvelles deps | Nouvelle dep |
| C5-4 | Pas de modification OmniParser wiring | _omniparser_available = False intact |
Modifié |
C6 : Test coverage
| # | Critère | GO | NOGO |
|---|---|---|---|
| C6-1 | TR-1 : navigate compile à 1 action (pas []) | Passes | Fails |
| C6-2 | TR-2 : coords_var template resolution + float conversion | Passes | Fails |
| C6-3 | Test _coerce_action_coords : float pass-through |
Passes | Absent |
| C6-4 | Test _coerce_action_coords : string→float conversion |
Passes | Absent |
| C6-5 | Test _coerce_action_coords : template non résolu → pause_for_human |
Passes | Absent |
| C6-6 | Test _coerce_action_coords : conversion impossible → pause_for_human |
Passes | Absent |
| C6-7 | Test idempotence : action existante float non modifiée | Passes | Absent |
| C6-8 | pytest tests/unit/ passe en intégralité |
0 failures | ≥1 failure |
C7 : Risques additionnels (3 identifiés dans PLAN_D1)
| # | Risque | Mitigation attendue | GO | NOGO |
|---|---|---|---|---|
| C7-1 | Résolution partielle (x_pct résolu, y_pct template) | _coerce_action_coords → pause_for_human si ANY key unresolved |
Mitigation presente | Pas de mitigation |
| C7-2 | Idempotence sur mouse_click existant | isinstance(val, float) → continue |
Idempotent | Risque de double conversion |
| C7-3 | Race condition sur variables dict partagé | BFS séquentiel garantit navigate→click ordre | Note dans code/doc | Pas de mention |
Procédure de review
- Lire le patch :
git diffsur les fichiers modifiés par Codex - Vérifier chaque critère C1-C7 : GO/NOGO par ligne
- Exécuter les tests :
cd /home/dom/ai/rpa_vision_v3 && .venv/bin/python -m pytest tests/unit/ -x -v - Produire le verdict : Table GO/NOGO avec justification + verdict global
Format verdict
## QG Verdict — D1 NavigateCoords Patch
| Critère | GO/NOGO | Note |
|---------|---------|------|
| C1 | GO | Branche navigate correcte |
| C2 | NOGO | BUG: y_pct en double dans text_input |
| ... | ... | ... |
**Verdict global**: GO / NOGO (avec réserves listées)
Qwen — framework QG prêt, awaiting Codex patch pour exécution.