From be0f2cf7baa0ee932d18991bd083361dbfeef39c Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Fri, 6 Nov 2026 18:56:19 +0000 Subject: [PATCH] Use original metadata identities for independent route profiles --- .../starpilot/flm/offroad_prior_inputs.py | 20 +++++--- .../flm/tests/test_offroad_backend.py | 47 +++++++++++++++++++ prebuilt.json | 2 +- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/openpilot/starpilot/flm/offroad_prior_inputs.py b/openpilot/starpilot/flm/offroad_prior_inputs.py index 496f2feb48..3d9047b55e 100644 --- a/openpilot/starpilot/flm/offroad_prior_inputs.py +++ b/openpilot/starpilot/flm/offroad_prior_inputs.py @@ -143,20 +143,21 @@ def _prior_originals(assessment, group, selected_cohort, root, *, permitted, sta metadata = collected.context_metadata[context] if json.dumps({k: metadata.get(k) for k in expected}, sort_keys=True, allow_nan=False) != json.dumps(expected, sort_keys=True, allow_nan=False): continue - compatible.extend((w, metadata) for w in windows if cohort(w) == selected_cohort['cohort']) + # Keep the original metadata key: point comparison keys deliberately omit route values. + compatible.extend((w, metadata, context) for w in windows if cohort(w) == selected_cohort['cohort']) # Choose a single original model context by input coverage, never by error. - contexts = {w.points[0].context_key for w, _ in compatible} + contexts = {w.points[0].context_key for w, _, _ in compatible} if not contexts: continue - context = min(contexts, key=lambda key: (-len({w.points[0].segment for w, _ in compatible if w.points[0].context_key == key}), key)) - compatible = [(w, metadata) for w, metadata in compatible if w.points[0].context_key == context] + context = min(contexts, key=lambda key: (-len({w.points[0].segment for w, _, _ in compatible if w.points[0].context_key == key}), key)) + compatible = [(w, metadata, original_context) for w, metadata, original_context in compatible if w.points[0].context_key == context] chosen = [] tracking = [] input_segments = ( tuple(n for n in collected.receipt.decoded_segments if 0 < n < len(route['segments']) - 1) if stable else interior_segments(len(route['segments'])) ) for number in input_segments: - windows = sorted(((w, m) for w, m in compatible if w.points[0].segment == number), key=lambda row: row[0].points[0].mono_ns) + windows = sorted(((w, m) for w, m, _ in compatible if w.points[0].segment == number), key=lambda row: row[0].points[0].mono_ns) if windows: w, metadata = windows[0] tracking.append(w) @@ -174,7 +175,12 @@ def _prior_originals(assessment, group, selected_cohort, root, *, permitted, sta if stable: for number in input_segments: candidates = sorted( - (w for w in profile_windows.get(context, ()) if w.points[0].segment == number and cohort(w) == selected_cohort['cohort']), + ( + w + for original_context in sorted({key for _, _, key in compatible}) + for w in profile_windows.get(original_context, ()) + if w.points[0].context_key == context and w.points[0].segment == number and cohort(w) == selected_cohort['cohort'] + ), key=lambda w: w.points[0].mono_ns, ) if candidates: @@ -192,7 +198,7 @@ def _prior_originals(assessment, group, selected_cohort, root, *, permitted, sta tuple(chosen), tuple(tracking), tuple(profile_spans), - collected.context_metadata[context] if stable else None, + collected.context_metadata[compatible[0][2]] if stable else None, ) return None diff --git a/openpilot/starpilot/flm/tests/test_offroad_backend.py b/openpilot/starpilot/flm/tests/test_offroad_backend.py index 722461a4f7..07f70ea3c5 100644 --- a/openpilot/starpilot/flm/tests/test_offroad_backend.py +++ b/openpilot/starpilot/flm/tests/test_offroad_backend.py @@ -1638,6 +1638,53 @@ class TestOffroadTrackingPolicy(unittest.TestCase): self.assertTrue(readable) self.assertEqual(decode(raw)[NAMESPACE][scope().key()]['route_sha256s'], ['d' * 64] + (['e' * 64] if independent else [])) + def test_prior_profile_join_keeps_original_metadata_key_and_rejects_other_map(self): + from types import SimpleNamespace + from openpilot.starpilot.flm import offroad_prior_inputs as module + from openpilot.starpilot.flm.offroad_route import SelectedSourceReceipt + + # Declared input-join fixture only; no candidate fitting or accepted tune is mocked. + current_id, prior_id = '0000000a--aaaaaaaaaa', '00000009--bbbbbbbbbb' + metadata_key, comparison_key, other_key = 'd' * 64, 'e' * 64, 'f' * 64 + metadata = {'route_values': tuple(values().items()), 'route_revision': REVISION, 'baseline_revision': 'c' * 64} + changed = dict(metadata, route_values=tuple(values(0.01).items())) + + def window(segment, identity): + points = tuple( + SimpleNamespace(segment=segment, mono_ns=segment * 10_000_000_000 + i * 10_000_000, context_key=comparison_key, desired=0.2, speed_mps=25.0) + for i in range(8) + ) + return SimpleNamespace(identity=identity, points=points, domain='lateral_acceleration') + + native = [window(n, f'{n:064x}') for n in range(1, 7)] + profile = [window(n, f'{n + 10:064x}') for n in range(1, 4)] + wrong_map = [window(n, f'{n + 20:064x}') for n in range(1, 7)] + receipt = SelectedSourceReceipt(prior_id, 'b' * 64, (), 'a' * 64, 1, 2, 3, (), tuple(range(8))) + collected = SimpleNamespace( + receipt=receipt, + state={'cp': object()}, + windows={metadata_key: native, other_key: wrong_map}, + context_metadata={metadata_key: metadata, other_key: changed}, + baseline_tracking={}, + stable_windows={metadata_key: profile, other_key: wrong_map}, + ) + route = {'routeId': prior_id, 'segments': [{'files': {'rlog': True}} for _ in range(8)]} + assessment = SimpleNamespace(receipt=SimpleNamespace(route_id=current_id, cp_sha256='a' * 64, completed_wall_ns=10)) + with ( + patch.object(module.DriveHistory, 'snapshot', return_value={'scanIncomplete': False, 'routes': [route]}), + patch.object(module, 'collect_route_inputs', return_value=collected), + patch.object(module, 'cohort', return_value='2:right:steady'), + ): + result = module.prior_stable_originals(assessment, {'original_metadata': metadata}, {'cohort': '2:right:steady'}, '.', permitted=lambda: True) + self.assertIsNotNone(result) + assert result is not None + self.assertEqual(tuple(row['identity'] for row in result.windows), tuple(w.identity for w in native)) + self.assertEqual(tuple(row['identity'] for row in result.profile_windows), tuple(w.identity for w in profile)) + self.assertEqual(result.profile_metadata, metadata) + self.assertEqual(result.receipt, receipt) + self.assertEqual(result.source_inventory, route) + self.assertFalse({row['identity'] for row in result.profile_windows} & {w.identity for w in wrong_map}) + def test_prior_source_reader_requires_real_end_and_matching_cp(self): import bz2 from contextlib import contextmanager diff --git a/prebuilt.json b/prebuilt.json index d37e9ac741..7fbcfc83ae 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "16be21cbdff9d1e7baf298e553a3c6ac519daa127f1293a050d3ea7ac70d354e", "schema_version": 1} +{"protected_inventory_sha256": "2cf0604813fb378a0b92ed78c384f71b57be90c37c0c80dda6a500dcb35c9bbf", "schema_version": 1}