"""Versioned causal candidate feedback; never original/current admission.""" from dataclasses import dataclass import hashlib import math import pickle from typing import cast LEGACY_POLICY = 'original-consumed-feedback-regime-v1' CLOSED_POLICY = 'candidate-native-consumed-co-closed-loop-v1' PROOF_VERSION = 'candidate-native-feedback-chronology-v1' _SEAL = object() def policy(value): if type(value) is not str or value not in (LEGACY_POLICY,CLOSED_POLICY): raise ValueError('explicit candidate feedback policy') return value def uses_closed_loop(engine): # Opting OUT of a shortcut grants no source/native/health authority. Unknown # shells/proxies retain the old path without attribute callbacks or recursion. from openpilot.starpilot.flm.event_controller_replay import EventControllerReplay from openpilot.starpilot.flm.feedback_replay import FeedbackReplayEvidence,_SEAL as feedback_seal if type(engine) is not EventControllerReplay: return False owned = object.__getattribute__(engine,'__dict__') evidence = owned.get('verified_feedback_tape') if type(evidence) is not FeedbackReplayEvidence: return False values = object.__getattribute__(evidence,'__dict__') value = values.get('candidate_policy') return values.get('_seal') is feedback_seal and type(value) is str and value == CLOSED_POLICY @dataclass(frozen=True) class CandidateFeedbackProof: version: str source_digest: str original_digest: str coordinates_digest: str native_policy: str chronology_digest: str row_count: int _seal: object def strict_reference_proof_key(proof, engine, coordinates, request_count): """Fresh exact strict chronology certificate; numerical FD reuse only. The returned immutable primitive bytes detect later mutation of a retained proof object. They never replace the fresh engine/source key at the caller. """ from openpilot.starpilot.flm.feedback_replay import require_feedback_engine if (type(proof) is not CandidateFeedbackProof or proof._seal is not _SEAL or type(coordinates) is not tuple or type(request_count) is not int or any(type(value) not in (int,float) or not math.isfinite(value) for value in coordinates)): return None evidence = require_feedback_engine(engine) fields = (proof.version,proof.source_digest,proof.original_digest,proof.coordinates_digest, proof.native_policy,proof.chronology_digest,proof.row_count) if (any(type(value) is not str for value in fields[:6]) or type(proof.row_count) is not int or proof.version != PROOF_VERSION or evidence.candidate_policy != CLOSED_POLICY or proof.source_digest != evidence.engine_input_digest or proof.original_digest != evidence.original.digest or proof.coordinates_digest != hashlib.sha256(pickle.dumps(coordinates,protocol=5)).hexdigest() or proof.native_policy != 'strict' or not 1 <= proof.row_count == request_count <= 12000 or len(proof.chronology_digest) != 64 or any(c not in '0123456789abcdef' for c in proof.chronology_digest)): return None return pickle.dumps(fields,protocol=5) def _strict_reference_fields(proof, evidence, coordinates, request_count): """Numerical fields only; caller MUST own fresh full pre/post source checks. Public callers retain strict_reference_proof_key and its own fresh admission. This helper creates no seal, proof, cache entry or current/native permission. """ from openpilot.starpilot.flm.feedback_replay import FeedbackReplayEvidence, _SEAL as feedback_seal if (type(proof) is not CandidateFeedbackProof or proof._seal is not _SEAL or type(evidence) is not FeedbackReplayEvidence or evidence._seal is not feedback_seal or type(coordinates) is not tuple or type(request_count) is not int or any(type(value) not in (int,float) or not math.isfinite(value) for value in coordinates)): return None fields = (proof.version,proof.source_digest,proof.original_digest,proof.coordinates_digest, proof.native_policy,proof.chronology_digest,proof.row_count) if (any(type(value) is not str for value in fields[:6]) or type(proof.row_count) is not int or proof.version != PROOF_VERSION or evidence.candidate_policy != CLOSED_POLICY or proof.source_digest != evidence.engine_input_digest or proof.original_digest != evidence.original.digest or proof.coordinates_digest != hashlib.sha256(pickle.dumps(coordinates,protocol=5)).hexdigest() or proof.native_policy != 'strict' or not 1 <= proof.row_count == request_count <= 12000 or len(proof.chronology_digest) != 64 or any(c not in '0123456789abcdef' for c in proof.chronology_digest)): return None return pickle.dumps(fields,protocol=5) class CandidateFeedbackLoop: """One rollout, constant working state; native reports remain their real map. Immutable original observations are never rewritten. Only the next update's safety_limited input is reconstructed. The consumed CO coordinate is exact, not nearest-time lookup, physical delivery or an invented receiver contract. """ def __init__(self, engine, reported, coordinates, *, native_policy, original_baseline=False): from openpilot.starpilot.flm.feedback_replay import require_feedback_engine from openpilot.starpilot.flm.torque_emission_replay import EmissionHistory evidence = require_feedback_engine(engine) if (evidence.candidate_policy != CLOSED_POLICY or type(reported) is not dict or reported or type(engine.emissions) is not EmissionHistory or type(coordinates) is not tuple or any(type(value) not in (int,float) or not math.isfinite(value) for value in coordinates) or type(original_baseline) is not bool or type(native_policy) is not str or native_policy not in ('strict','native-shadow-identification')): raise ValueError('registered candidate native feedback scope') self.engine,self.evidence,self.reported = engine,evidence,reported self.coordinates_digest = hashlib.sha256(pickle.dumps(coordinates,protocol=5)).hexdigest() self.native_policy,self.original_baseline = native_policy,original_baseline self._iterator = iter(self._rows()) self._pending = None self._previous_window = self._previous_ns = self._previous_flag = None self._stream,self._count = hashlib.sha256(),0 def _rows(self): for bounds,subset,activation in self.evidence.windows: context = tuple(row for row in self.evidence.original.context_only if bounds[0] <= row.control_ns <= bounds[1]) if len(context) > 1 or context and context[0].control_ns != bounds[0]: raise ValueError('candidate feedback only actual leading context') for row in context: if row.control_ns >= activation: raise ValueError('unknown leading CO cannot be a candidate treatment') yield bounds,activation,row,None for joined in subset.rows: yield bounds,activation,joined.consumption,joined.publication @staticmethod def _record(bounds,activation,row,pub,input_flag,request,reported,flag): return (PROOF_VERSION,bounds,activation,row.control_ns,row.prior_publish_ns, row.publication_ns,row.recv_frame,row.receipt_ns,row.co_body_sha256, None if pub is None else (pub.output_control_ns,pub.output_apply_ns,pub.output_carrier_sequence, pub.native_identity,pub.applied_sha256),input_flag,request,reported,flag) @staticmethod def _add(stream,record): body = pickle.dumps(record,protocol=5) stream.update(len(body).to_bytes(8,'big')) stream.update(body) def before(self, frame): if self._pending is not None: raise ValueError('candidate feedback previous control unfinished') try: bounds,activation,row,pub = next(self._iterator) except StopIteration as error: raise ValueError('candidate feedback extra controller frame') from error if type(frame.control_ns) is not int or frame.control_ns != row.control_ns: raise ValueError('candidate feedback exact controller chronology') same_window = bounds == self._previous_window if same_window and row.prior_publish_ns != self._previous_ns: raise ValueError('candidate feedback previous publication omitted') previous = cast(bool, self._previous_flag if same_window else row.input_limited) if row.control_ns < activation and previous is not row.input_limited: raise ValueError('candidate changed original preactivation input') values = tuple(pair for pair in frame.inputs if pair[0] == 'safety_limited') if (len(values) != 1 or type(values[0][1]) not in (int,float) or values[0][1] != float(row.input_limited)): raise ValueError('original actual safety_limited input changed') self._pending = bounds,activation,row,pub,previous if previous is row.input_limited: return frame.inputs return tuple((key,float(previous)) if key == 'safety_limited' else (key,value) for key,value in frame.inputs) def _equation(self,bounds,activation,row,pub,input_flag,request,reported_map): from openpilot.starpilot.flm.feedback_observation import f32 request = f32(request) original = self.original_baseline or row.control_ns < activation or pub is None if pub is None or pub.output_control_ns < activation: reported = row.reported_torque if pub is None else pub.reported_torque else: if (not pub.output_control_ns <= pub.output_apply_ns <= pub.publication_ns <= row.control_ns or pub.output_apply_ns not in reported_map): raise ValueError('candidate consumed exact native CO not yet produced') reported = f32(reported_map[pub.output_apply_ns]) flag = bool(row.lat_active and abs(request-reported) > .01) if original and (request != row.requested_torque or reported != row.reported_torque or input_flag is not row.input_limited or flag is not row.published_limited): raise ValueError('candidate changed original baseline or preactivation feedback') return request,reported,flag def after(self,control_ns,request): if self._pending is None or control_ns != self._pending[2].control_ns: raise ValueError('candidate feedback controller pair missing') bounds,activation,row,pub,input_flag = self._pending request,reported,flag = self._equation(bounds,activation,row,pub,input_flag,request,self.reported) self._add(self._stream,self._record(bounds,activation,row,pub,input_flag,request,reported,flag)) self._count += 1 self._previous_window,self._previous_ns,self._previous_flag = bounds,control_ns,flag self._pending = None def finish(self,requests,reported): from openpilot.starpilot.flm.feedback_replay import require_feedback_engine if self._pending is not None or reported is not self.reported or type(requests) is not dict: raise ValueError('candidate feedback complete native/controller maps required') try: next(self._iterator) except StopIteration: pass else: raise ValueError('candidate feedback original control omitted') check = hashlib.sha256() prior_window = prior_ns = prior_flag = None count = 0 for bounds,activation,row,pub in self._rows(): if row.control_ns not in requests: raise ValueError('candidate feedback complete request map required') same_window = prior_window == bounds if same_window and row.prior_publish_ns != prior_ns: raise ValueError('candidate feedback prior candidate omitted') input_flag = prior_flag if same_window else row.input_limited request,torque,flag = self._equation(bounds,activation,row,pub,input_flag,requests[row.control_ns],reported) self._add(check,self._record(bounds,activation,row,pub,input_flag,request,torque,flag)) prior_window,prior_ns,prior_flag = bounds,row.control_ns,flag count += 1 evidence = require_feedback_engine(self.engine) if (evidence.candidate_policy != CLOSED_POLICY or evidence.engine_input_digest != self.evidence.engine_input_digest or evidence.original.digest != self.evidence.original.digest or count != self._count or not 1 <= count <= 12000 or len(requests) != count or check.digest() != self._stream.digest()): raise ValueError('candidate feedback owned chronology/native results changed') return CandidateFeedbackProof(PROOF_VERSION,evidence.engine_input_digest,evidence.original.digest, self.coordinates_digest,self.native_policy,check.hexdigest(),count,_SEAL) def report_policy_matches(submitted,offered): """Declaration matching only; physical/native/current guards remain callers'.""" if type(submitted) is not tuple or len(submitted) not in (3,4) or type(offered) is not dict: return False expected = {'protocol','binding_digest','archive_digest','verified_digest','known_rows','context_rows'} if len(submitted) == 4: if type(submitted[3]) is not str or submitted[3] != CLOSED_POLICY: return False expected |= {'candidate_feedback_policy','candidate_feedback_proof_version'} if (type(offered.get('candidate_feedback_policy')) is not str or offered['candidate_feedback_policy'] != CLOSED_POLICY or type(offered.get('candidate_feedback_proof_version')) is not str or offered['candidate_feedback_proof_version'] != PROOF_VERSION): return False return set(offered) == expected