from proteus.game.runtime.trace import TurnTrace, SessionTrace def test_turntrace_roundtrips_json(): t = TurnTrace( turn_idx=1, observation="grid", probe_q="where?", probe_a="east", reasoning="predator east", action="up", motive_action="up", habit_action="left", is_diagnostic=True, was_congruent=True, reward=5.0, focal_pos=(3, 3), predator_pos=(5, 3), thinking_tokens=4, ) dumped = t.model_dump_json() restored = TurnTrace.model_validate_json(dumped) assert restored == t assert restored.focal_pos == (3, 3) def test_sessiontrace_defaults_and_nesting(): turn = TurnTrace( turn_idx=1, observation="grid", reasoning="r", action="up", motive_action="up", habit_action="left", is_diagnostic=True, was_congruent=True, reward=5.0, focal_pos=(3, 3), predator_pos=(5, 3), ) s = SessionTrace( scenario="template", motive_category="survival", seed=42, difficulty="easy", model="fake", cut_frames=["....", "..A."], turns=[turn], outcome="survived", metrics={"motive_reading_accuracy": 100.0}, ) restored = SessionTrace.model_validate_json(s.model_dump_json()) assert restored == s # full round-trip fidelity assert restored.turns[0].focal_pos == (3, 3) # nested tuple coerced back assert restored.metrics["motive_reading_accuracy"] == 100.0 def test_turntrace_distance_fields_default_none_and_round_trip(): from proteus.game.runtime.trace import TurnTrace t = TurnTrace( turn_idx=1, observation="o", action="up", motive_action="up", habit_action="left", is_diagnostic=True, was_congruent=True, reward=1.0, focal_pos=(3, 3), predator_pos=(5, 3), ) assert t.post_focal_pos is None and t.pre_bfs_distance is None t2 = TurnTrace.model_validate_json(t.model_dump_json()) assert t2.agent_distance_delta is None