Add fail-closed live supervisor policy and Galaxy controller boundary

This commit is contained in:
firestar5683
2026-09-19 21:16:47 -07:00
parent 87bc35bc5b
commit ec380c1e59
4 changed files with 263 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
"""Galaxy contract. Missing target health/lifecycle adapter is explicitly unavailable.
No SSH, device probing, or process start occurs while importing/constructing.
The target adapter must retain supervisor ownership across separate UI requests.
"""
from pathlib import Path
import time
from live_supervisor import blocked_reason
class LiveController:
def __init__(self, root=Path('/data/roadscore'), *, adapter=None, clock=time.monotonic):
self.root=Path(root);self.clock=clock;self.adapter=adapter
if self.adapter is None:
try:
from live_target_adapter import create
except ModuleNotFoundError as error:
if error.name!='live_target_adapter':raise
else:self.adapter=create(self.root)
def status(self):
if self.adapter is None:
return dict(available=False,enabled=False,state='COLD',can_enable=False,
reason='Live target health/lifecycle adapter is not installed; hardware readiness is unverified')
try:
status=self.adapter.status()
observation,authorization=self.adapter.health_and_authorization()
reason=blocked_reason(observation,authorization,self.clock(),require_parked=True)
return {**status,'available':True,'can_enable':not status.get('enabled',False) and not reason,
'reason':reason or status.get('reason','')}
except Exception as error:
return dict(available=False,enabled=False,state='DEGRADED',can_enable=False,reason='Live supervisor status unavailable: '+str(error))
def set_enabled(self, enabled):
if type(enabled) is not bool:raise ValueError('enabled must be a boolean')
if self.adapter is None:
if not enabled:return self.status()
raise RuntimeError(self.status()['reason'])
if not enabled:
self.adapter.stop_owned()
return self.status()
observation,authorization=self.adapter.health_and_authorization()
reason=blocked_reason(observation,authorization,self.clock(),require_parked=True)
if reason:raise RuntimeError(reason)
# Adapter must recheck atomically with launch and continue its watchdog.
self.adapter.enable(observation,authorization)
return self.status()
+105
View File
@@ -0,0 +1,105 @@
"""Fail-closed live lifecycle policy. No Params writes, replay, or bench power code.
A target integration must supply genuine current observations and own the child
processes. Importing this module never probes a device or starts a process.
"""
from dataclasses import dataclass
import math
import time
import uuid
@dataclass(frozen=True)
class Observation:
monotonic: float
parked: bool
model_fresh: bool
car_fresh: bool
modeld_healthy: bool
device_healthy: bool
chestnut_healthy: bool
baseline_id: str
car_id: str
driving_model_local: bool = False
@dataclass(frozen=True)
class Authorization:
baseline_id: str
car_id: str
coexistence_verified: bool = False
user_authorized: bool = False
def blocked_reason(observation, authorization, now, *, require_parked):
if not isinstance(observation, Observation) or not isinstance(authorization, Authorization):
return 'Target health and explicit car/baseline authorization are unavailable'
if authorization.user_authorized is not True or authorization.coexistence_verified is not True:
return 'Car baseline and ACE/modeld coexistence must be verified and authorized'
if not authorization.baseline_id or not authorization.car_id or (observation.baseline_id, observation.car_id) != (authorization.baseline_id, authorization.car_id):
return 'Current car or software baseline differs from the authorized configuration'
if not math.isfinite(observation.monotonic) or not 0 <= now-observation.monotonic <= 1.:
return 'Live health observation is stale or has an invalid clock'
if not all(value is True for value in (observation.model_fresh, observation.car_fresh, observation.modeld_healthy,
observation.device_healthy, observation.chestnut_healthy, observation.driving_model_local)):
return 'Fresh car/model input and healthy local driving model, device, and Chestnut are required'
if require_parked and observation.parked is not True:
return 'Park before preparing or enabling live RoadScore'
return ''
class LiveSupervisor:
"""Adapter callbacks operate ONLY this session's worker/app, with no shell takeover.
prepare(session_id) launches direct ACE with a fresh seed/current bank/quality.
start_app(session_id) starts app --input live using accepted initial music.
stop_owned() must terminate only this supervisor's owned process groups.
These callbacks are deliberately injected, never an implicit hardware adapter.
"""
def __init__(self, prepare, start_app, stop_owned, *, clock=time.monotonic):
self.prepare=prepare;self.start_app=start_app;self.stop_owned=stop_owned;self.clock=clock
self.state='COLD';self.reason='';self.session_id=None;self.enabled=False;self.driver_ready=False
def status(self):
return dict(available=True,enabled=self.enabled,state=self.state,reason=self.reason,
session_id=self.session_id,driver_ready=self.driver_ready)
def enable(self, observation, authorization):
if self.enabled:return self.status()
reason=blocked_reason(observation,authorization,self.clock(),require_parked=True)
if reason:raise RuntimeError(reason)
self.session_id=uuid.uuid4().hex;self.driver_ready=False;self.enabled=True;self.state='PREPARING';self.reason='Preparing accepted audio while parked'
try:self.prepare(self.session_id)
except Exception:
self.stop('Preparation failed');raise
return self.status()
def confirm_driver_ready(self, session_id, observation, authorization):
if not self.enabled or self.state!='READY' or session_id!=self.session_id:
raise RuntimeError('Driver readiness must confirm the current prepared live session')
reason=blocked_reason(observation,authorization,self.clock(),require_parked=True)
if reason:raise RuntimeError(reason)
try:self.start_app(self.session_id)
except Exception:
self.stop('Live input/audio startup failed');raise
self.driver_ready=True;self.state='STARTING';self.reason='Waiting for live input/audio readiness'
return self.status()
def tick(self, observation, authorization, *, worker_healthy, accepted_ready=False, app_ready=False, app_healthy=True):
if not self.enabled:return self.status()
reason=blocked_reason(observation,authorization,self.clock(),require_parked=not self.driver_ready)
if reason or not worker_healthy or (self.driver_ready and not app_healthy):
return self.stop(reason or 'Owned composer or audio process failed')
if self.state=='PREPARING' and accepted_ready:
self.state='READY';self.reason='Prepared while parked; explicit driver-ready confirmation required'
if self.state=='STARTING' and app_ready:
self.state='LIVE';self.reason=''
return self.status()
def stop(self, reason='Stopped by operator'):
# Always callable, including when health is stale or car is moving.
self.enabled=False;self.driver_ready=False;self.state='STOPPING';self.reason=reason
try:self.stop_owned()
except Exception:
self.state='DEGRADED';self.reason='Owned process shutdown failed; inspect target supervisor';raise
self.state='COLD';return self.status()
@@ -0,0 +1,57 @@
# Live supervisor candidate: unavailable until target adapter validation
`live_controller.LiveController(root=Path)` provides the Galaxy contract:
`status()` returns available/enabled/state/can_enable/reason/session_id, and
`set_enabled(bool)` starts parked preparation or stops only owned live processes.
OFF bypasses health/parked gates. ON rechecks current health server-side.
**There is no installed target adapter in this change.** Default status is
available=false, can_enable=false. No process launches, hardware probes, Params
writes, car-control publishers, model selection, or device access occur here.
This is reviewed orchestration policy plus an explicit disabled integration
boundary, not a road-tested live service.
The controller expects `live_target_adapter.create(root)` to return a persistent
service client with status(), health_and_authorization(), enable(observation,
authorization), and stop_owned(). Its daemon can use LiveSupervisor. It must
atomically revalidate on start and tick frequently enough to meet the health
freshness bound. Fresh accepted worker readiness must match the exact session
seed/bank/profile; an old worker_ready file is not sufficient.
The adapter must collect genuine modelV2/carState/manager/device/Chestnut health.
Observation age must be <=1 second. Input booleans must reflect per-message ages,
model frameDropPerc/modelExecutionTime and process health under an owner-validated
budget; a recent collector timestamp must not make stale source messages fresh.
Parkedness comes from fresh standstill/speed/gear evidence, **not IsOnroad=false**;
an ignition-on parked car is permitted. Verify actual driving-model placement
is local from current modeld/Chestnut telemetry, including UsbGpu* Params and
chestnutState as appropriate, without writing them. User's requested small model
alone does not prove local execution. Missing/unknown placement fails closed.
Persist explicit user authorization and verified coexistence bound to current
car and code/config baseline. No such verification is manufactured here.
Sequence: authorized healthy parked environment -> PREPARING -> accepted READY
-> explicit driver-ready confirmation of the exact session while parked ->
STARTING -> verified app/audio readiness -> LIVE. Movement before driver-ready,
stale health, modeld degradation, or worker/app failure invokes owned-only stop.
No auto-resume after failure. Driver-ready is not inferred from enable or from
vehicle movement. No navigation is required; app --input live must use fresh
current messages only and preserve the no-future-input policy.
ACE worker can run directly with current cached conditioning, fresh session seed,
quality policy, and GPU/session locks. `run_worker.sh` supplies interpreter/device
and conservative thread-count environment. It does not mutate CPU settings.
**Do not use power_worker.py or worker_service.py for this lane**: both remain
intentionally offroad/bench guarded; power_worker also changes CPU online bits,
clock/governor and affinity. Do not weaken those guards, stop manager/modeld, set
replay namespaces, publish fake carState, or reset another worker. Target adapter
must refuse another GPU/session owner and retain only its own process-group
handles for stop. Preserve active results/current and create unique session
archives; output/Bluetooth setup requires root lifecycle integration.
Actual ACE/modeld CPU/memory/thermal coexistence has not been measured. Chestnut
model allocation, USB link faults and normal CPU scheduling can still affect the
platform. Offroad resident results do not establish driving coexistence. The
candidate stays unavailable until the sole hardware owner validates the read-only
collector and coexistence baseline; explicit driver-ready is separately required
before an attended road test.
@@ -0,0 +1,54 @@
from dataclasses import replace
from unittest.mock import Mock
import pytest
from live_supervisor import Observation,Authorization,LiveSupervisor,blocked_reason
from live_controller import LiveController
GOOD=Observation(10.,True,True,True,True,True,True,'baseline','car',True)
AUTH=Authorization('baseline','car',True,True)
def make():
calls=Mock()
return LiveSupervisor(calls.prepare,calls.start_app,calls.stop,clock=lambda:10.),calls
@pytest.mark.parametrize('field,value',[('monotonic',8.),('monotonic',11.),('monotonic',float('nan')),('parked',False),('driving_model_local',False),('model_fresh',False),('car_fresh',False),('modeld_healthy',False),('device_healthy',False),('chestnut_healthy',False),('baseline_id','different'),('car_id','different')])
def test_fail_closed_before_process_start(field,value):
sup,calls=make()
with pytest.raises(RuntimeError):sup.enable(replace(GOOD,**{field:value}),AUTH)
calls.prepare.assert_not_called()
def test_capability_not_implied_by_car_connection():
sup,calls=make()
with pytest.raises(RuntimeError):sup.enable(GOOD,replace(AUTH,coexistence_verified=False))
calls.prepare.assert_not_called()
def test_gate_then_health_failure_stops_only_owned_callbacks():
sup,calls=make();sup.enable(GOOD,AUTH)
sup.tick(GOOD,AUTH,worker_healthy=True,accepted_ready=True)
calls.start_app.assert_not_called()
with pytest.raises(RuntimeError):sup.confirm_driver_ready('previous-session',GOOD,AUTH)
sup.confirm_driver_ready(sup.session_id,GOOD,AUTH)
assert sup.state=='STARTING'
sup.tick(replace(GOOD,parked=False),AUTH,worker_healthy=True,app_ready=True)
assert sup.state=='LIVE'
sup.tick(replace(GOOD,model_fresh=False),AUTH,worker_healthy=True)
calls.stop.assert_called_once();assert sup.state=='COLD'
def test_movement_before_ready_stops_preparation():
sup,calls=make();sup.enable(GOOD,AUTH)
sup.tick(replace(GOOD,parked=False),AUTH,worker_healthy=True)
calls.stop.assert_called_once();calls.start_app.assert_not_called()
def test_controller_off_ignores_missing_health_and_only_stops_adapter():
adapter=Mock();adapter.status.return_value={'enabled':False,'state':'COLD'}
adapter.health_and_authorization.side_effect=RuntimeError('stale')
controller=LiveController(adapter=adapter,clock=lambda:10.)
controller.set_enabled(False);adapter.stop_owned.assert_called_once()
with pytest.raises(RuntimeError):controller.set_enabled(True)
adapter.enable.assert_not_called()
def test_missing_adapter_unavailable():
controller=LiveController()
assert not controller.status()['available'] and not controller.status()['can_enable']
with pytest.raises(RuntimeError):controller.set_enabled(True)
assert controller.set_enabled(False)['enabled'] is False