From 0f52f4c70e37633787e96ccda465e6397e9991e9 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sun, 20 Sep 2026 10:58:08 -0700 Subject: [PATCH] Guard followed route identity and expose current prepared playhead --- roadscore/prototype/mac_showcase.html | 2 +- roadscore/prototype/mac_showcase.py | 8 +++++++- roadscore/prototype/paired_demo_controls.py | 8 ++++++-- roadscore/prototype/test_mac_showcase_controls.py | 9 ++++++++- roadscore/prototype/test_paired_demo_controls.py | 11 ++++++++++- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/roadscore/prototype/mac_showcase.html b/roadscore/prototype/mac_showcase.html index 185ac347c3..2705f58ef4 100644 --- a/roadscore/prototype/mac_showcase.html +++ b/roadscore/prototype/mac_showcase.html @@ -30,7 +30,7 @@ async function refresh(){ document.querySelector('#state').textContent=j.state+' · '+Math.max(0,j.audio_s||0).toFixed(1)+' s · '+(j.muted?'MUTED':'system audio output'); const follow=j.paired_controls?.following; document.querySelector('#scope').textContent=follow?.enabled?'Galaxy is master · phone and comma controls are mirrored to this Mac':j.paired_controls?.enabled?'Mac + comma controls enabled · each button requests the same change on both':'Mac-only controls · comma is not paired in this session'; - const followErrors={peer_session_changed:'Comma replay restarted · reconnect this Mac session',local_session_changed:'Mac replay changed · reconnect this session',peer_action_in_progress:'Waiting for the Mac request to reach Galaxy',local_replay_unready:'Waiting for the Mac replay',peer_not_ready_for_replay:'Waiting for a fresh comma replay',peer_status_stale:'Galaxy status is stale',peer_timeout:'Galaxy status timed out'}; + const followErrors={peer_session_changed:'Comma replay restarted · reconnect this Mac session',local_session_changed:'Mac replay changed · reconnect this session',peer_route_mismatch:'Comma and Mac routes do not match',peer_route_changed:'Comma route changed · reconnect this Mac session',invalid_peer_route:'Galaxy route identity unavailable',peer_action_in_progress:'Waiting for the Mac request to reach Galaxy',local_replay_unready:'Waiting for the Mac replay',peer_not_ready_for_replay:'Waiting for a fresh comma replay',peer_status_stale:'Galaxy status is stale',peer_timeout:'Galaxy status timed out'}; document.querySelector('#following').textContent=!follow?.enabled?'':follow.status==='following'?'Mac controls match fresh Galaxy state':follow.status==='applying'?'Applying Galaxy controls on Mac':followErrors[follow.error]||'Galaxy control mirroring paused · status unavailable'; const local=j.demo.available?j.demo.mode+' / signals '+j.demo.signal_mode:'unready'; document.querySelector('#targets').textContent='Mac '+local+'; comma '+commaStatus(j.paired_controls); diff --git a/roadscore/prototype/mac_showcase.py b/roadscore/prototype/mac_showcase.py index 14fadc935b..356559087b 100644 --- a/roadscore/prototype/mac_showcase.py +++ b/roadscore/prototype/mac_showcase.py @@ -70,6 +70,8 @@ def control_server(project, out, shared, port, forwarder=None, *, follow_peer=Fa following.update(status='pending',error='peer_action_in_progress',snapshot=None);return demo=operator.demo_status() if not demo['available']:raise ValueError('local_replay_unready') + local_route=demo.get('route') or module.read_json(out/'status.json').get('route') + if snapshot['route']!=local_route:raise ValueError('peer_route_mismatch') if local_session is None:local_session=demo['session_id'] elif demo['session_id']!=local_session:raise ValueError('local_session_changed') signature=(snapshot['session_id'],local_session,snapshot['mode'],snapshot['signal_mode'],sequence) @@ -84,7 +86,8 @@ def control_server(project, out, shared, port, forwarder=None, *, follow_peer=Fa following.update(status='following' if matching else 'applying',error=None,snapshot=snapshot) except Exception as error: known={'peer_action_in_progress','peer_session_changed','local_session_changed','local_replay_unready', - 'peer_not_ready_for_replay','invalid_peer_session','forwarder_disabled'} + 'peer_not_ready_for_replay','invalid_peer_session','forwarder_disabled','invalid_peer_route', + 'peer_route_changed','peer_route_mismatch'} reason=str(error) if str(error) in known else 'peer_timeout' if isinstance(error,TimeoutError) else 'peer_status_unavailable' with control_lock:following.update(status='paused',error=reason,snapshot=None) def follow(): @@ -235,6 +238,9 @@ def audio_worker(a): if now-last_status>=.08: shared.update(state='READY' if anchor else 'PREPARING',audio_s=max(0,(position or 0)/rate)) snapshot=dict(command_wall=now,input_mode='replay',compute='prepared-core',composer='ace',profile='prism',style='Prism',readiness=shared['state'],section='PREPARED PRISM',route=a.route,buffered=max(0,(len(audio)-(position or 0))/rate),presentation_session_id=session,demo_engagement_mode=controls.mode,demo_signal_mode=controls.signal_mode,engagement_presentation={'enabled':True},generation_invoked=False) + if anchor is not None and state['route_t'] is not None and np.isfinite(state['route_t']): + snapshot.update(route_t=float(state['route_t']),elapsed=max(0,(position or 0)/rate), + source_model_ns=int(sm.logMonoTime['modelV2'])) snapshot=delay.apply(snapshot,rendered) # The command API must remain available during initial DAC lead-in. snapshot.setdefault('engagement_presentation',{'enabled':True}) diff --git a/roadscore/prototype/paired_demo_controls.py b/roadscore/prototype/paired_demo_controls.py index d0c2db7dab..0d7abd5c86 100644 --- a/roadscore/prototype/paired_demo_controls.py +++ b/roadscore/prototype/paired_demo_controls.py @@ -141,6 +141,7 @@ class PairedDemoControls: self._active = None self._closed = False self._bound_session = None + self._bound_route = None self._write_revision = 0 def read_status(self): @@ -162,12 +163,15 @@ class PairedDemoControls: received = time.monotonic() if received-started >= min(self.timeout,.75):raise TimeoutError() session = _session(status) + route = status['demo'].get('route') + if not isinstance(route,str) or not route or route=='live':raise ValueError('invalid_peer_route') with self._lock: if self._closed:raise ValueError('forwarder_disabled') if revision != self._write_revision or self._active is not None:raise ValueError('peer_action_in_progress') - if self._bound_session is None:self._bound_session=session + if self._bound_session is None:self._bound_session,self._bound_route=session,route elif session != self._bound_session:raise ValueError('peer_session_changed') - return dict(session_id=session,mode=status['demo']['mode'],signal_mode=status['demo']['signal_mode'], + elif route != self._bound_route:raise ValueError('peer_route_changed') + return dict(session_id=session,route=route,mode=status['demo']['mode'],signal_mode=status['demo']['signal_mode'], request_started_wall=started,received_wall=received,peer_status=status) def _result(self, status, action, value, *, acknowledged=False, applied=False, diff --git a/roadscore/prototype/test_mac_showcase_controls.py b/roadscore/prototype/test_mac_showcase_controls.py index b674345e37..a13a3bcf8e 100644 --- a/roadscore/prototype/test_mac_showcase_controls.py +++ b/roadscore/prototype/test_mac_showcase_controls.py @@ -191,7 +191,7 @@ class ControlTests(unittest.TestCase): def following_peer(self): status=dict(available=True,offroad=True,state='READY',live={'enabled':False}, - demo=dict(available=True,session_id='comma-session',mode='recorded',signal_mode='recorded')) + demo=dict(available=True,session_id='comma-session',route='fixture-route',mode='recorded',signal_mode='recorded')) calls=[] def transport(method,*args): calls.append(method) @@ -238,6 +238,13 @@ class ControlTests(unittest.TestCase): self.wait_for(lambda:self.request()[1]['paired_controls']['following']['error']=='local_session_changed') self.assertFalse((self.out/'demo_engagement.json').exists()) + def test_different_local_route_cannot_receive_peer_controls(self): + peer,_=self.following_peer() + self.state['route']='other-route';self.write_state() + peer['demo']['mode']='engaged' + self.wait_for(lambda:self.request()[1]['paired_controls']['following']['error']=='peer_route_mismatch') + self.assertFalse((self.out/'demo_engagement.json').exists()) + class RememberedConfigTests(unittest.TestCase): def setUp(self): diff --git a/roadscore/prototype/test_paired_demo_controls.py b/roadscore/prototype/test_paired_demo_controls.py index ce29b3cba4..dc986bd91e 100644 --- a/roadscore/prototype/test_paired_demo_controls.py +++ b/roadscore/prototype/test_paired_demo_controls.py @@ -16,7 +16,7 @@ from paired_demo_controls import GalaxyPeer, PairedDemoControls, _NoRedirect, _h BASE = 'https://galaxy.firestar.link/ABCDEFGHIJKLMNOP' COOKIE = 'ABCDEFGHIJKLMNOP%3A' + 'a' * 64 READY = {'available': True, 'offroad': True, 'state': 'READY', 'live': {'enabled': False}, - 'demo': {'available': True, 'session_id': 'independent-peer-session', + 'demo': {'available': True, 'session_id': 'independent-peer-session', 'route':'fixture-route', 'mode': 'recorded', 'signal_mode': 'recorded'}} @@ -153,6 +153,15 @@ class PairedTests(unittest.TestCase): self.transport.status['demo']['readiness']='PREPARING' with self.assertRaisesRegex(ValueError,'peer_not_ready_for_replay'):controls.read_status() + def test_follower_requires_and_pins_exact_route_identity(self): + controls=self.forwarder() + self.transport.status['demo'].pop('route') + with self.assertRaisesRegex(ValueError,'invalid_peer_route'):controls.read_status() + self.transport.status['demo']['route']='fixture-route' + self.assertEqual(controls.read_status()['route'],'fixture-route') + self.transport.status['demo']['route']='different-route' + with self.assertRaisesRegex(ValueError,'peer_route_changed'):controls.read_status() + def test_late_status_and_disabled_reader_never_return_applied_state(self): controls = self.forwarder() with patch('paired_demo_controls.time.monotonic',side_effect=[100.,101.]):