From 96818d8efc94f6b5dc37fe3a55a264324f8396a7 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:58:09 -0500 Subject: [PATCH] The Time Turner --- .../the_pond/tests/test_dashboard_stats.py | 67 +++++++++++++++++++ starpilot/system/the_pond/utilities.py | 12 ++++ 2 files changed, 79 insertions(+) diff --git a/starpilot/system/the_pond/tests/test_dashboard_stats.py b/starpilot/system/the_pond/tests/test_dashboard_stats.py index fd22ef2d8..367d39b80 100644 --- a/starpilot/system/the_pond/tests/test_dashboard_stats.py +++ b/starpilot/system/the_pond/tests/test_dashboard_stats.py @@ -609,6 +609,73 @@ def test_recent_drives_coalesce_exact_duplicate_time_ranges_only(): assert short_drive["distance"] == 0.5 +def test_completed_routes_with_old_analysis_version_are_refreshed_once(): + route = {"name": "route-1", "modifiedAt": 100, "segmentCount": 1} + stale_stats = { + "routes": { + "route-1": { + "modifiedAt": 100, + "attentionKnown": True, + "analysisComplete": True, + "analysisVersion": utilities.DASHBOARD_ROUTE_ANALYSIS_VERSION - 1, + }, + }, + } + current_stats = { + "routes": { + "route-1": { + "modifiedAt": 100, + "attentionKnown": True, + "analysisComplete": True, + "analysisVersion": utilities.DASHBOARD_ROUTE_ANALYSIS_VERSION, + }, + }, + } + + assert utilities._analysis_candidates([route], stale_stats) == [route] + assert utilities._analysis_candidates([route], current_stats) == [] + + +def test_shell_update_preserves_old_analysis_version_for_reparse(): + params = FakeParams({ + utilities.DASHBOARD_PERSISTENT_STATS_PARAM: { + "routes": { + "route-1": { + "date": "2026-06-18T09:24:00", + "endDate": "2026-06-18T09:34:00", + "distanceMeters": 1000.0, + "duration": 600, + "engagedSeconds": 400.0, + "model": "Orion", + "modifiedAt": 100, + "attentionKnown": True, + "analysisComplete": True, + "analysisVersion": utilities.DASHBOARD_ROUTE_ANALYSIS_VERSION - 1, + }, + }, + }, + }) + shell_drive = { + "name": "route-1", + "date": "2026-06-18T09:23:00", + "endDate": "2026-06-18T09:34:00", + "distanceMeters": 0.0, + "duration": 660, + "engagedSeconds": 0.0, + "model": "Orion", + "routeModifiedAt": 100, + "attentionKnown": False, + "analysisComplete": False, + "analysisVersion": 0, + } + + stats = utilities._update_dashboard_persistent_stats(params, [shell_drive], wall_now=1000) + + assert stats["routes"]["route-1"]["date"] == "2026-06-18T09:24:00" + assert stats["routes"]["route-1"]["analysisVersion"] == utilities.DASHBOARD_ROUTE_ANALYSIS_VERSION - 1 + assert utilities._analysis_candidates([{"name": "route-1", "modifiedAt": 100}], stats) + + def test_unknown_attention_rows_do_not_reset_persisted_clean_records(): params = FakeParams() known_drive = { diff --git a/starpilot/system/the_pond/utilities.py b/starpilot/system/the_pond/utilities.py index 21d9206d9..8f4112d19 100644 --- a/starpilot/system/the_pond/utilities.py +++ b/starpilot/system/the_pond/utilities.py @@ -67,6 +67,7 @@ DASHBOARD_ROUTE_SEGMENT_SAMPLE_LIMIT = 2 DASHBOARD_PERSISTED_ROUTE_LIMIT = 5000 DASHBOARD_PERSIST_MIN_ROUTE_AGE_SECONDS = 120 DASHBOARD_PERSISTENT_STATS_PARAM = "GalaxyDashboardStats" +DASHBOARD_ROUTE_ANALYSIS_VERSION = 2 DASHBOARD_PARAMS_DIR = Path("/data/params/d") DASHBOARD_ANALYZER_LOG_PATH = "/tmp/galaxy_dashboard_analyzer.log" DASHBOARD_TOP_MODEL_LIMIT = 3 @@ -1094,6 +1095,7 @@ def _analyze_route_messages(messages, route_info, model_names, is_metric, deadli "routeModifiedAt": _safe_float(route_info.get("modifiedAt", 0.0), 0.0), "attentionKnown": True, "analysisComplete": analysis_segment_count >= segment_count, + "analysisVersion": DASHBOARD_ROUTE_ANALYSIS_VERSION, } @@ -1194,6 +1196,7 @@ def _route_shell_drive(route_info, params_obj, model_names, is_metric): "routeModifiedAt": _safe_float(route_info.get("modifiedAt", 0.0), 0.0), "attentionKnown": False, "analysisComplete": False, + "analysisVersion": 0, } @@ -1220,6 +1223,7 @@ def _drive_from_persistent_route(route_name, entry, is_metric): "routeModifiedAt": _safe_float(entry.get("modifiedAt", 0.0), 0.0), "attentionKnown": bool(entry.get("attentionKnown", True)), "analysisComplete": bool(entry.get("analysisComplete", False)), + "analysisVersion": max(0, _safe_int(entry.get("analysisVersion", 0), 0)), } @@ -1320,6 +1324,8 @@ def _analysis_candidates(route_infos, persistent_stats): return True if _safe_float(entry.get("modifiedAt", 0.0), 0.0) < _safe_float(route_info.get("modifiedAt", 0.0), 0.0): return True + if _safe_int(entry.get("analysisVersion", 0), 0) < DASHBOARD_ROUTE_ANALYSIS_VERSION: + return True return not bool(entry.get("attentionKnown", True)) or not bool(entry.get("analysisComplete", False)) missing = [route_info for route_info in route_infos if needs_analysis(route_info)] @@ -1717,6 +1723,7 @@ def _normalize_persistent_routes(raw_routes): "modifiedAt": _safe_float(entry.get("modifiedAt", 0.0), 0.0), "attentionKnown": bool(entry.get("attentionKnown", True)), "analysisComplete": bool(entry.get("analysisComplete", False)), + "analysisVersion": max(0, _safe_int(entry.get("analysisVersion", 0), 0)), } return routes @@ -1952,7 +1959,10 @@ def _update_dashboard_persistent_stats(params_obj, drives, wall_now): "modifiedAt": _safe_float(drive.get("routeModifiedAt", 0.0), 0.0), "attentionKnown": attention_known, "analysisComplete": bool(drive.get("analysisComplete", False)), + "analysisVersion": _safe_int(drive.get("analysisVersion", 0), 0), } + if attention_known and next_entry["analysisComplete"]: + next_entry["analysisVersion"] = DASHBOARD_ROUTE_ANALYSIS_VERSION existing_entry = routes.get(route_name) if isinstance(existing_entry, dict): existing_distance = max(0.0, _safe_float(existing_entry.get("distanceMeters", 0.0), 0.0)) @@ -1964,6 +1974,7 @@ def _update_dashboard_persistent_stats(params_obj, drives, wall_now): next_entry["undistracted"] = bool(existing_entry.get("undistracted", existing_entry.get("clean", False))) next_entry["attentionKnown"] = True next_entry["analysisComplete"] = bool(existing_entry.get("analysisComplete", False)) + next_entry["analysisVersion"] = max(0, _safe_int(existing_entry.get("analysisVersion", 0), 0)) if not attention_known and existing_current and existing_distance >= next_distance: next_entry["distanceMeters"] = existing_distance existing_duration = _safe_int(existing_entry.get("duration", 0), 0) @@ -1976,6 +1987,7 @@ def _update_dashboard_persistent_stats(params_obj, drives, wall_now): next_entry["distractedMoments"] = max(0, _safe_int(existing_entry.get("distractedMoments", 0), 0)) next_entry["unresponsiveMoments"] = max(0, _safe_int(existing_entry.get("unresponsiveMoments", 0), 0)) next_entry["analysisComplete"] = bool(existing_entry.get("analysisComplete", False)) + next_entry["analysisVersion"] = max(0, _safe_int(existing_entry.get("analysisVersion", 0), 0)) if (not model_name or model_name == "Unknown model") and _clean_model_label(existing_entry.get("model", "")): next_entry["model"] = _clean_model_label(existing_entry.get("model", "")) next_entry["modelKey"] = canonical_model_key(existing_entry.get("modelKey", "")) or _model_usage_key(next_entry["model"])