feat(core): signature de trajectoire stable pour identite workflow (Phase 0, F1)
Primitive partagee (SP-4/SP-2/competences) : hashe la sequence ordonnee (action_type, target) d'un parcours en ignorant les champs session-specifiques (node_id, timestamp, coordonnees) -> deux apprentissages du meme parcours = meme signature = base du create-or-update (decision F1). Le target stable peut etre compose avec screen_signature() existante. Test TDD: tests/unit/test_trajectory_signature.py (5 tests, RED->GREEN). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
59
tests/unit/test_trajectory_signature.py
Normal file
59
tests/unit/test_trajectory_signature.py
Normal file
@@ -0,0 +1,59 @@
|
||||
"""TDD — signature de trajectoire (Phase 0 ; primitive partagée SP-4 / SP-2 / compétences).
|
||||
|
||||
Propriété centrale : la signature identifie une TRAJECTOIRE (séquence d'actions sur des
|
||||
cibles stables). Elle doit être **stable entre sessions** — donc indépendante des champs
|
||||
session-spécifiques (IDs de nœuds, timestamps, coordonnées). C'est ce qui rend le
|
||||
create-or-update (décision F1) possible : deux apprentissages du même parcours = même id.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
|
||||
from core.execution.trajectory_signature import trajectory_signature
|
||||
|
||||
|
||||
def test_deterministic_same_sequence():
|
||||
steps = [
|
||||
{"action_type": "mouse_click", "target": "menu Fichier"},
|
||||
{"action_type": "text_input", "target": "champ recherche"},
|
||||
]
|
||||
assert trajectory_signature(steps) == trajectory_signature(steps)
|
||||
|
||||
|
||||
def test_ignores_session_specific_fields():
|
||||
"""Deux sessions du MÊME parcours (mêmes action_type+target) mais IDs de nœuds /
|
||||
timestamps / coords différents → MÊME signature."""
|
||||
session_a = [
|
||||
{"action_type": "mouse_click", "target": "menu Fichier",
|
||||
"node_id": "n_abc", "timestamp": 1000, "x": 12, "y": 34},
|
||||
{"action_type": "text_input", "target": "champ recherche",
|
||||
"node_id": "n_def", "timestamp": 1100, "x": 50, "y": 60},
|
||||
]
|
||||
session_b = [
|
||||
{"action_type": "mouse_click", "target": "menu Fichier",
|
||||
"node_id": "n_zzz", "timestamp": 9000, "x": 99, "y": 88},
|
||||
{"action_type": "text_input", "target": "champ recherche",
|
||||
"node_id": "n_yyy", "timestamp": 9100, "x": 11, "y": 22},
|
||||
]
|
||||
assert trajectory_signature(session_a) == trajectory_signature(session_b)
|
||||
|
||||
|
||||
def test_order_sensitive():
|
||||
a = [{"action_type": "mouse_click", "target": "A"},
|
||||
{"action_type": "text_input", "target": "B"}]
|
||||
b = list(reversed(a))
|
||||
assert trajectory_signature(a) != trajectory_signature(b)
|
||||
|
||||
|
||||
def test_target_discriminates():
|
||||
a = [{"action_type": "mouse_click", "target": "bouton Valider"}]
|
||||
b = [{"action_type": "mouse_click", "target": "bouton Annuler"}]
|
||||
assert trajectory_signature(a) != trajectory_signature(b)
|
||||
|
||||
|
||||
def test_returns_sha256_hex():
|
||||
sig = trajectory_signature([{"action_type": "mouse_click", "target": "x"}])
|
||||
assert len(sig) == 64
|
||||
assert all(c in "0123456789abcdef" for c in sig)
|
||||
Reference in New Issue
Block a user