From 1e64e461137ef37360acaaf335f88ebf75d3d9ca Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sat, 19 Sep 2026 16:31:27 -0700 Subject: [PATCH] Bind standalone native sessions to validated current conditioning bank --- .../ace_chestnut_20260916/ace_worker.py | 14 +++++--- roadscore/prototype/composition_launch.py | 17 ++++++++++ roadscore/prototype/normal_onroad.py | 8 +++-- roadscore/prototype/persistent_assets.py | 4 +++ roadscore/prototype/runtime_manifest.py | 4 +-- .../prototype/test_composition_launch.py | 33 +++++++++++++++++++ roadscore/prototype/test_persistent_assets.py | 8 +++++ 7 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 roadscore/prototype/composition_launch.py create mode 100644 roadscore/prototype/test_composition_launch.py diff --git a/roadscore/experiments/ace_chestnut_20260916/ace_worker.py b/roadscore/experiments/ace_chestnut_20260916/ace_worker.py index aaa233de28..f678daabb0 100644 --- a/roadscore/experiments/ace_chestnut_20260916/ace_worker.py +++ b/roadscore/experiments/ace_chestnut_20260916/ace_worker.py @@ -21,8 +21,11 @@ from link_health import LinkProbe from hook_service import Client from planned_composition import PlannedComposition composition_policy=os.environ.get('ROADSCORE_COMPOSITION_POLICY','prepared-v1') -if composition_policy not in ('prepared-v1','hook-v2'):raise ValueError('Unknown composition policy') -if composition_policy=='hook-v2' and not windowed:raise ValueError('Hook planning requires windowed native sampler') +if composition_policy not in ('prepared-v1','hook-v2','hook-cache-v1'):raise ValueError('Unknown composition policy') +if composition_policy in ('hook-v2','hook-cache-v1') and not windowed:raise ValueError('Hook planning requires windowed native sampler') +if composition_policy=='hook-cache-v1': + from cached_composition import CachedComposition,validate_bank + validate_bank(Path(os.environ['ROADSCORE_PLAN_BANK']),profile=selected()) from tinygrad import Device profile=selected();preparation_id=f'{profile}_{time.time_ns()}' lock=open(G/'gpu.lock','w');fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB) @@ -40,10 +43,13 @@ try: probe=LinkProbe(Device['AMD'],G/'ace_link.jsonl');probe.install_failure_hook(contain=True) if windowed:c.decoder.trace=probe.trace planned=None - if composition_policy=='hook-v2': + if composition_policy in ('hook-v2','hook-cache-v1'): from ace_runtime import Composer as NativeComposer from window_policy import retained_end - planned=PlannedComposition(Client(os.environ['ROADSCORE_PLANNER_URL'],os.environ['ROADSCORE_PLANNER_TOKEN']),G/'hook_sessions'/preparation_id,base_seed,profile) + if composition_policy=='hook-cache-v1': + planned=CachedComposition(Path(os.environ['ROADSCORE_PLAN_BANK']),G/'hook_sessions'/preparation_id,base_seed,profile) + else: + planned=PlannedComposition(Client(os.environ['ROADSCORE_PLANNER_URL'],os.environ['ROADSCORE_PLANNER_TOKEN']),G/'hook_sessions'/preparation_id,base_seed,profile) def planned_generate(role,seed,previous):return planned.generate(lambda case,seed,previous:NativeComposer.generate(c,case,seed,previous),seed,previous,retained_end) sample=planned_generate if planned else c.generate def generate(role,seed,previous): diff --git a/roadscore/prototype/composition_launch.py b/roadscore/prototype/composition_launch.py new file mode 100644 index 0000000000..466d1a5510 --- /dev/null +++ b/roadscore/prototype/composition_launch.py @@ -0,0 +1,17 @@ +"""Choose explicit native cached planning without substituting legacy conditioning.""" +from pathlib import Path +from hook_launch import enabled + + +def configure(session, replay, composer, *, native, transport_only, environ, root): + if transport_only or not enabled(session, replay, composer): + return 'prepared-v1' + if not native: + return 'hook-v2' + bank = Path(environ.get('ROADSCORE_PLAN_BANK', '/data/roadscore-event-assets/conditioning/current')) + from cached_composition import validate_bank + validate_bank(bank, profile=environ.get('ROADSCORE_ACE_PROFILE', 'prism')) + environ['ROADSCORE_PLAN_BANK'] = str(bank.resolve()) + environ.pop('ROADSCORE_PLANNER_URL', None) + environ.pop('ROADSCORE_PLANNER_TOKEN', None) + return 'hook-cache-v1' diff --git a/roadscore/prototype/normal_onroad.py b/roadscore/prototype/normal_onroad.py index 553989f7f1..3178e98e36 100644 --- a/roadscore/prototype/normal_onroad.py +++ b/roadscore/prototype/normal_onroad.py @@ -8,7 +8,8 @@ from pathlib import Path from clock_sync import measure from receiver_environment import assignments as receiver_assignments,resolve_compute from presentation_policy import select_launch -from hook_launch import enabled as hook_enabled, start_planner +from hook_launch import start_planner +from composition_launch import configure as configure_composition from launch_health import check_children, describe_failure, write_failure from session_seed import select_session, seed_argument, seed_environment, remote_assignments R=Path(__file__).resolve().parents[1] @@ -25,7 +26,8 @@ if not a.replay and a.composer=='ace': session=select_session(a.roadscore_seed,judging_seed=judging_seed) presentation=select_launch(a.composer,a.profile,replay=a.replay,judging=bool(session and session['seed_origin']=='judging-route'),render_mode=a.render_mode,policy=a.roadscore_presentation) a.render_mode=presentation['render_mode'] -composition_policy='hook-v2' if hook_enabled(session,a.replay,a.composer) and not a.transport_only else 'prepared-v1' +composition_environment=os.environ.copy();composition_environment['ROADSCORE_ACE_PROFILE']=a.profile +composition_policy=configure_composition(session,a.replay,a.composer,native=native,transport_only=a.transport_only,environ=composition_environment,root=R) from settings import Settings,resolve_route a.routeid=resolve_route(p,a.route,a.routeid) from route_favorites import resolve_favorite @@ -40,7 +42,7 @@ for path in [py,replay,rt/'selfdrive/ui/ui.py']: if native: from native_ownership import verify_offroad verify_offroad() -out=R/'results'/('normal_'+str(int(time.time())));out.mkdir();env=os.environ.copy();env.update(PYTHONDONTWRITEBYTECODE='1',ZMQ='1',OPENPILOT_ZMQ_NAMESPACE='roadscore-native-'+str(os.getpid()),PARAMS_ROOT=str(out/'params'),BASEDIR=str(rt),NOBOARD='1',SIMULATION='1',SKIP_FW_QUERY='1',BIG='0',SP_ALLOW_DESKTOP_FAKE_WIFI='0',SP_ALLOW_DESKTOP_FAKE_BLUETOOTH='0',SP_ONROAD_NAV_DEMO='0',SP_ONROAD_CEM_DEMO='0') +out=R/'results'/('normal_'+str(int(time.time())));out.mkdir();env=composition_environment.copy();env.update(PYTHONDONTWRITEBYTECODE='1',ZMQ='1',OPENPILOT_ZMQ_NAMESPACE='roadscore-native-'+str(os.getpid()),PARAMS_ROOT=str(out/'params'),BASEDIR=str(rt),NOBOARD='1',SIMULATION='1',SKIP_FW_QUERY='1',BIG='0',SP_ALLOW_DESKTOP_FAKE_WIFI='0',SP_ALLOW_DESKTOP_FAKE_BLUETOOTH='0',SP_ONROAD_NAV_DEMO='0',SP_ONROAD_CEM_DEMO='0') if session: env.update(seed_environment(session));(out/'session_seed.json').write_text(json.dumps(session,indent=2));print('RoadScore session seed:',session['generation_seed'],'('+session['seed_origin']+')',flush=True) else: diff --git a/roadscore/prototype/persistent_assets.py b/roadscore/prototype/persistent_assets.py index c281fb4dd9..0dcefa3437 100644 --- a/roadscore/prototype/persistent_assets.py +++ b/roadscore/prototype/persistent_assets.py @@ -11,6 +11,10 @@ def restore_links(root, data=Path('/data')): mapping = {name: state / name for name in ('generated', 'results', 'assets', 'runtime.json')} mapping.update({f'experiments/ace_chestnut_20260916/{name}': assets / 'ace' / name for name in ('weights', 'vae_weights', 'profiles')}) + # Optional provisioned native cache/build folders survive checkout replacement. + for name in ('routes', 'native_build'): + if (state / name).is_dir(): + mapping[name] = state / name missing = [] for relative, target in mapping.items(): valid = target.is_file() if relative == 'runtime.json' else target.is_dir() diff --git a/roadscore/prototype/runtime_manifest.py b/roadscore/prototype/runtime_manifest.py index e2bf2a11ba..92523d65f7 100644 --- a/roadscore/prototype/runtime_manifest.py +++ b/roadscore/prototype/runtime_manifest.py @@ -11,14 +11,14 @@ source=root/('assets/source_'+result['runtime'].get('identity','legacy')+'.wav') if choice()=='ace': source=root/'generated/ace_initial.wav' for folder in [root/'experiments/ace_chestnut_20260916',root/'prototype']: - for name in ['ace_worker.py','ace_runtime.py','window_runtime.py','window_policy.py','native_ace.py','native_vae.py','chunk_decode.py','gesture_bank_v2.py','musical_gestures_v2.py','gesture_bank_v3.py','musical_gestures_v3.py','ending_policy.py','quality_gate.py','link_health.py','safe_extension.py','host_hook_adapter.py','prism_hook_spec.py','hook_planning.py','hook_service.py','planned_composition.py','prepared_session.py','ace_profiles.py','gesture_bank_v4.py','musical_gestures_v4.py']: + for name in ['ace_worker.py','ace_runtime.py','window_runtime.py','window_policy.py','native_ace.py','native_vae.py','chunk_decode.py','gesture_bank_v2.py','musical_gestures_v2.py','gesture_bank_v3.py','musical_gestures_v3.py','ending_policy.py','quality_gate.py','link_health.py','safe_extension.py','host_hook_adapter.py','prism_hook_spec.py','hook_planning.py','hook_service.py','planned_composition.py','cached_composition.py','composition_launch.py','prepared_session.py','ace_profiles.py','gesture_bank_v4.py','musical_gestures_v4.py']: path=folder/name if path.exists():result['implementation_sha256'][str(path)]=hashlib.sha256(path.read_bytes()).hexdigest() if choice()=='ace': profile=root/'experiments/ace_chestnut_20260916/profiles'/selected()/'profile.json' initial=root/'generated/ace_initial.json' if initial.exists():result['ace_initial_provenance']=json.loads(initial.read_text()) - if profile.exists() and result.get('ace_initial_provenance',{}).get('composition_policy')!='hook-v2' and result.get('ace_initial_provenance',{}).get('prepared_profile')==selected():result['prepared_profile_manifest']=json.loads(profile.read_text()) + if profile.exists() and result.get('ace_initial_provenance',{}).get('composition_policy') not in ('hook-v2','hook-cache-v1') and result.get('ace_initial_provenance',{}).get('prepared_profile')==selected():result['prepared_profile_manifest']=json.loads(profile.read_text()) if source.exists():result['source_identity']={'name':result['runtime'].get('identity'),'sha256':hashlib.sha256(source.read_bytes()).hexdigest()} for name,path in [('openpilot','/data/openpilot'),('tinygrad','/data/openpilot/tinygrad_repo')]: process=subprocess.run(['git','-C',path,'rev-parse','HEAD'],text=True,capture_output=True);result[name+'_revision']=process.stdout.strip() if process.returncode==0 else 'unavailable' diff --git a/roadscore/prototype/test_composition_launch.py b/roadscore/prototype/test_composition_launch.py new file mode 100644 index 0000000000..08ba9dc2f6 --- /dev/null +++ b/roadscore/prototype/test_composition_launch.py @@ -0,0 +1,33 @@ +import sys +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import patch +import pytest +from composition_launch import configure + +SESSION = {'seed_origin': 'random-session'} + +def run(env, **kwargs): + return configure(SESSION, False, 'ace', native=True, transport_only=False, + environ=env, root=Path('/unused'), **kwargs) + +def test_native_bank_validated_and_network_credentials_removed(tmp_path): + calls = [] + env = {'ROADSCORE_PLAN_BANK': str(tmp_path), 'ROADSCORE_ACE_PROFILE': 'prism', + 'ROADSCORE_PLANNER_URL': 'http://host', 'ROADSCORE_PLANNER_TOKEN': 'token'} + with patch.dict(sys.modules, cached_composition=SimpleNamespace(validate_bank=lambda path, **kw: calls.append((path,kw)))): + assert run(env) == 'hook-cache-v1' + assert calls == [(tmp_path, {'profile': 'prism'})] + assert 'ROADSCORE_PLANNER_URL' not in env and 'ROADSCORE_PLANNER_TOKEN' not in env + +def test_invalid_native_bank_never_falls_back(): + def reject(*args, **kwargs): raise ValueError('missing current plan bank') + with patch.dict(sys.modules, cached_composition=SimpleNamespace(validate_bank=reject)): + with pytest.raises(ValueError, match='missing current plan bank'): run({}) + +@pytest.mark.parametrize('replay,session,transport', [(True, None, False), (False, {'seed_origin':'judging-route'}, False), (False, SESSION, True)]) +def test_frozen_and_non_generation_do_not_require_bank(replay, session, transport): + assert configure(session, replay, 'ace', native=True, transport_only=transport, environ={}, root=Path('/unused')) == 'prepared-v1' + +def test_mac_fresh_path_unchanged(): + assert configure(SESSION, False, 'ace', native=False, transport_only=False, environ={}, root=Path('/unused')) == 'hook-v2' diff --git a/roadscore/prototype/test_persistent_assets.py b/roadscore/prototype/test_persistent_assets.py index 9dc6a74df0..0c44fcd33f 100644 --- a/roadscore/prototype/test_persistent_assets.py +++ b/roadscore/prototype/test_persistent_assets.py @@ -28,6 +28,14 @@ class PersistentAssetsTest(unittest.TestCase): self.assertEqual(restore_links(self.root, self.data), []) self.assertEqual((self.root / 'experiments/ace_chestnut_20260916/weights/model').read_bytes(), b'preserved') + def test_optional_native_cache_and_build_reconnected(self): + self.provision() + for name in ('routes', 'native_build'): + (self.data / 'roadscore-event-state' / name).mkdir() + self.assertEqual(len(restore_links(self.root, self.data)), 9) + self.assertTrue((self.root / 'routes').is_symlink()) + self.assertTrue((self.root / 'native_build').is_symlink()) + def test_existing_data_is_never_overwritten_or_partially_modified(self): self.provision() (self.root / 'runtime.json').write_text('local settings')