mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-10 14:33:42 +08:00
Replay unchanged native limits before judging a learned tune
This commit is contained in:
@@ -278,16 +278,17 @@ def check_original_native(cp, controls, outputs, commands, *, require_change=Tru
|
||||
for output in originals.values():
|
||||
history.verify(by_control[output.control_ns], output)
|
||||
before = history.rollout(baseline, start, end)
|
||||
after = history.rollout(candidate, start, end)
|
||||
if set(before) != set(originals) or any(before[stamp] != delivered(originals[stamp]) for stamp in before):
|
||||
return {'qualified': False, 'reason': 'original_encoder_reconstruction_mismatch'}
|
||||
after = history.rollout(candidate, start, end, allow_unclipped_equal_output=True)
|
||||
except (AttributeError, KeyError, TypeError, ValueError, OverflowError) as error:
|
||||
return {'qualified': False, 'reason': 'original_encoder_or_candidate_quality_refused', 'detail': str(error)[:160]}
|
||||
if set(before) != set(originals) or any(before[stamp] != delivered(originals[stamp]) for stamp in before):
|
||||
return {'qualified': False, 'reason': 'original_encoder_reconstruction_mismatch'}
|
||||
changed = sum(before[k] != after[k] for k in before)
|
||||
return {
|
||||
'qualified': bool(changed or not require_change),
|
||||
'reason': 'registered_encoder_trial_only' if require_change else 'original_registered_encoder_observed',
|
||||
'original_decoded_emissions': len(originals),
|
||||
'limiter_policy': 'same_state_equal_output_unclipped_v1',
|
||||
'candidate_changed_emissions': changed,
|
||||
'physical_improvement_proven': False,
|
||||
'hardware_current_permission': False,
|
||||
|
||||
@@ -268,18 +268,35 @@ class StableRouteSelector:
|
||||
all_ids = ids['training'] + ids['heldout']
|
||||
if any(key not in rows or rows[key] is None for key in all_ids):
|
||||
return None, {'reason': 'original_stable_input_fields_missing', 'original_windows': all_ids}
|
||||
mapped, reports = {}, {}
|
||||
for role, windows in ids.items():
|
||||
mapped[role], reports[role] = self.profile(windows, cohort['cohort'])
|
||||
training, heldout = mapped['training'], mapped['heldout']
|
||||
training, training_report = self.profile(ids['training'], cohort['cohort'])
|
||||
selection = 'training_values_only_no_heldout_resizing'
|
||||
if training is not None:
|
||||
from openpilot.starpilot.flm.offroad_surface import CURVE_NAMES
|
||||
|
||||
if any(training['values'][name] != self.previous[name] for name in CURVE_NAMES):
|
||||
# Quieting is one trial; unrelated turn changes need their own evidence.
|
||||
values = dict(self.previous)
|
||||
for name in CURVE_NAMES:
|
||||
if training['values'][name] != self.previous[name]:
|
||||
values[name] = round(self.previous[name] + 0.6 * (training['values'][name] - self.previous[name]), 12)
|
||||
training = dict(
|
||||
training,
|
||||
values=values,
|
||||
changed_parameters=tuple(name for name in CURVE_NAMES if values[name] != self.previous[name]),
|
||||
maximum_coordinate_step=max(abs(values[name] - self.previous[name]) for name in CURVE_NAMES),
|
||||
bounded_profile_scale=training['bounded_profile_scale'] * 0.6,
|
||||
)
|
||||
selection = 'training_only_conservative_friction_trial_before_heldout'
|
||||
heldout, heldout_report = self.profile(ids['heldout'], cohort['cohort'])
|
||||
agrees = independent_profile_support(training, heldout, self.previous)
|
||||
proof = {
|
||||
'source_policy_commit': ORIGINAL_COMMIT,
|
||||
'source_classifier_commit': CURRENT_CLASSIFIER_SOURCE,
|
||||
'policy': 'original_stable_recommended_bounded_route_v1',
|
||||
'reason': 'independent_support_for_frozen_training_profile' if agrees else 'heldout_profile_does_not_support_training_step',
|
||||
'selection': 'training_values_only_no_heldout_resizing',
|
||||
'roles': reports,
|
||||
'selection': selection,
|
||||
'frozen_training_profile': training,
|
||||
'roles': {'training': training_report, 'heldout': heldout_report},
|
||||
'all_original_windows_still_required': True,
|
||||
'physical_improvement_proven': False,
|
||||
}
|
||||
|
||||
@@ -492,10 +492,6 @@ class OffroadWorker:
|
||||
independent = None
|
||||
stable = proposal.get('policy') == 'original_stable_recommended_bounded_route_v1'
|
||||
provider = self.stable_prior_provider if stable else self.prior_provider
|
||||
if provider is None and self.assessor is analyze_latest:
|
||||
from openpilot.starpilot.flm.offroad_prior_inputs import prior_originals, prior_stable_originals
|
||||
|
||||
provider = prior_stable_originals if stable else prior_originals
|
||||
independent = None if provider is None else provider(assessment, group, cohort, self.root, permitted=self.permitted)
|
||||
if independent is not None and row is not None and independent.receipt.source_sha256 in row['route_sha256s']:
|
||||
independent = None
|
||||
|
||||
@@ -1296,6 +1296,132 @@ class TestOffroadRouteCoverage(unittest.TestCase):
|
||||
|
||||
|
||||
class TestOffroadTrackingPolicy(unittest.TestCase):
|
||||
def test_current_disjoint_windows_do_not_trigger_an_older_drive_rescan(self):
|
||||
from types import SimpleNamespace
|
||||
from openpilot.starpilot.flm import offroad_worker as module
|
||||
from openpilot.starpilot.flm.offroad_processor import CompletedAssessment
|
||||
from openpilot.starpilot.flm.offroad_route import RouteReceipt
|
||||
|
||||
# Dispatch-only fixture: training refuses, so no accepted tune is fabricated.
|
||||
roles = trial()['before']
|
||||
proposal = {'parameter': 'center_taper_max', 'delta': 0.005, 'values': values(0.005)}
|
||||
cohort = {'cohort': 'declared-current-drive', 'whole_windows': 6, 'qualification_roles': roles,
|
||||
'training': dict(roles['training'], reference_rms=0.5), 'heldout': dict(roles['heldout'], reference_rms=0.5), 'proposal': proposal}
|
||||
metadata = {'route_values': tuple(values().items()), 'route_revision': REVISION, 'controller_mode': 'starpilot', 'route_scope': scope().key()}
|
||||
group = {'original_metadata': metadata, 'original_context_sha256': 'f' * 64, 'cohorts': [cohort]}
|
||||
cp = SimpleNamespace(carFingerprint='declared-current-drive')
|
||||
receipt = RouteReceipt('declared-current-drive', 'd' * 64, (), 'a' * 64, 1, 2, 3, ())
|
||||
inventory = {'routeId': receipt.route_id}
|
||||
assessment = CompletedAssessment(receipt, (group,), {}, cp, (), inventory)
|
||||
calls = []
|
||||
|
||||
def assessed(params, root, previous, *, permitted):
|
||||
previous(cp)
|
||||
return assessment
|
||||
|
||||
def refuse_training(actual, original, selected, proposed, previous, root, *, permitted):
|
||||
self.assertIs(actual, assessment)
|
||||
self.assertEqual(selected['qualification_roles'], roles)
|
||||
self.assertEqual(proposed, proposal)
|
||||
self.assertTrue(permitted())
|
||||
calls.append('current_drive_training')
|
||||
return None, {'declared_training_refusal': True}
|
||||
|
||||
def refused_check(*args, **kwargs):
|
||||
self.fail('A refused training step cannot enter validation')
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
params = Params(directory)
|
||||
with (
|
||||
patch.object(module, 'latest_route', return_value=inventory),
|
||||
patch.object(module, 'analyze_latest', new=assessed),
|
||||
patch.object(module, 'qualify', new=refused_check),
|
||||
patch('openpilot.starpilot.flm.offroad_checks.fit_training_step', new=refuse_training),
|
||||
patch('openpilot.starpilot.flm.offroad_prior_inputs.prior_originals', side_effect=AssertionError('Unexpected older drive rescan')),
|
||||
patch('openpilot.starpilot.flm.offroad_prior_inputs.prior_stable_originals', side_effect=AssertionError('Unexpected older drive rescan')),
|
||||
):
|
||||
worker = module.OffroadWorker(params, directory, Path(directory) / 'cache', permitted=lambda: True,
|
||||
assessor=assessed, checker=refused_check, scope_for=lambda cp, params: scope())
|
||||
result = worker.run_once()
|
||||
self.assertEqual(calls, ['current_drive_training'])
|
||||
self.assertFalse(result['configuration_changed'])
|
||||
self.assertEqual(result['reason'], 'no_new_native_feasible_training_step')
|
||||
self.assertEqual(read_saved(params, KEY, MAX_BYTES), (None, True))
|
||||
|
||||
def test_quieting_trial_is_frozen_before_unrelated_heldout_turn_disagreement(self):
|
||||
import copy
|
||||
from openpilot.starpilot.flm.offroad_stable_bridge import StableRouteSelector
|
||||
|
||||
# Declared proposal-policy regression, using the retained profile's numeric shape.
|
||||
# Actual controller/native qualification and automatic storage are not mocked.
|
||||
roles = trial()['before']
|
||||
cohort = {'cohort': '2:left:steady', **roles}
|
||||
original = values()
|
||||
original.update(friction_threshold_mid=0.004625, friction_threshold_fast=0.0061875, friction_threshold_highway=0.0061875, unwind_taper_left=0.01)
|
||||
training = {
|
||||
'values': original,
|
||||
'changed_parameters': tuple(name for name, value in original.items() if value),
|
||||
'maximum_coordinate_step': 0.01,
|
||||
'bounded_profile_scale': 0.625,
|
||||
}
|
||||
independent = values()
|
||||
independent.update(friction_threshold_mid=0.0071, friction_threshold_fast=0.0095, friction_threshold_highway=0.0095)
|
||||
frozen = copy.deepcopy(training)
|
||||
for unsupported in (None, 0.0, -0.0095, 0.0001):
|
||||
with self.subTest(unsupported_primary=unsupported):
|
||||
selector = object.__new__(StableRouteSelector)
|
||||
selector.previous = values()
|
||||
selector.rows = dict.fromkeys(roles['training']['windows'] + roles['heldout']['windows'], [])
|
||||
events = []
|
||||
heldout_values = dict(independent)
|
||||
if unsupported is not None:
|
||||
heldout_values['friction_threshold_fast'] = unsupported
|
||||
|
||||
def declared_profile(windows, selected_cohort, events=events, heldout_values=heldout_values):
|
||||
self.assertEqual(selected_cohort, cohort['cohort'])
|
||||
if windows == tuple(roles['training']['windows']):
|
||||
events.append('training')
|
||||
return copy.deepcopy(frozen), {'declared_role': 'training'}
|
||||
self.assertEqual(windows, tuple(roles['heldout']['windows']))
|
||||
events.append('heldout')
|
||||
return {'values': heldout_values}, {'declared_role': 'heldout'}
|
||||
|
||||
with patch.object(selector, 'profile', side_effect=declared_profile):
|
||||
proposed, proof = selector.propose({}, cohort)
|
||||
self.assertEqual(events, ['training', 'heldout'])
|
||||
self.assertEqual(proof['selection'], 'training_only_conservative_friction_trial_before_heldout')
|
||||
expected = values()
|
||||
expected.update(friction_threshold_mid=0.002775, friction_threshold_fast=0.0037125, friction_threshold_highway=0.0037125)
|
||||
self.assertEqual(proof['frozen_training_profile']['values'], expected)
|
||||
self.assertEqual(training, frozen)
|
||||
if unsupported is None:
|
||||
self.assertIsNotNone(proposed)
|
||||
assert proposed is not None
|
||||
self.assertEqual(proposed['values'], expected)
|
||||
self.assertEqual(proposed['values']['unwind_taper_left'], 0.0)
|
||||
self.assertEqual(set(proof['frozen_training_profile']['changed_parameters']), set(CURVE_NAMES[2:]))
|
||||
self.assertTrue(proof['all_original_windows_still_required'])
|
||||
else:
|
||||
self.assertIsNone(proposed, 'Missing, reversed or weaker primary evidence must still refuse; no resizing or fallback')
|
||||
|
||||
def test_turn_only_training_profile_keeps_original_values(self):
|
||||
from openpilot.starpilot.flm.offroad_stable_bridge import StableRouteSelector
|
||||
|
||||
roles = trial()['before']
|
||||
mapped = values()
|
||||
mapped['unwind_taper_left'] = 0.005
|
||||
profile = {'values': mapped, 'changed_parameters': ('unwind_taper_left',), 'maximum_coordinate_step': 0.005, 'bounded_profile_scale': 1.0}
|
||||
selector = object.__new__(StableRouteSelector)
|
||||
selector.previous = values()
|
||||
selector.rows = dict.fromkeys(roles['training']['windows'] + roles['heldout']['windows'], [])
|
||||
with patch.object(selector, 'profile', return_value=(profile, {'declared_proposal_only': True})):
|
||||
proposed, proof = selector.propose({}, {'cohort': '2:left:unwind', **roles})
|
||||
self.assertIsNotNone(proposed)
|
||||
assert proposed is not None
|
||||
self.assertEqual(proposed['values'], mapped)
|
||||
self.assertEqual(proposed['delta'], 0.005)
|
||||
self.assertEqual(proof['selection'], 'training_values_only_no_heldout_resizing')
|
||||
|
||||
def test_heldout_profile_supports_frozen_training_step_without_identical_floats(self):
|
||||
import copy
|
||||
from openpilot.starpilot.flm.offroad_stable_bridge import independent_profile_support
|
||||
@@ -2234,5 +2360,95 @@ class TestOffroadTrackingPolicy(unittest.TestCase):
|
||||
self.assertEqual(result['improvement_kind'], 'forced_tracking')
|
||||
|
||||
|
||||
class TestOffroadEqualNativeOutput(unittest.TestCase):
|
||||
@staticmethod
|
||||
def boundary(platform, *, unwind=False):
|
||||
from openpilot.starpilot.flm.tests.test_steering_projection import make_interface, control, apply, settle, install_observer, check_packet
|
||||
from openpilot.starpilot.flm.torque_emission_replay import Emission
|
||||
|
||||
ci = make_interface(platform)
|
||||
assert install_observer(ci)
|
||||
initial = settle(ci, control(0.0))[-1][2]
|
||||
limits = dict(initial.limits)
|
||||
step = limits['STEER_DELTA_DOWN' if unwind else 'STEER_DELTA_UP']
|
||||
if unwind:
|
||||
settle(ci, control(4 * step / initial.effective_max))
|
||||
target = (3 if unwind else 2) * step
|
||||
for _ in range(24):
|
||||
_, packets, witness = apply(ci, control(target / initial.effective_max))
|
||||
if witness is None:
|
||||
continue
|
||||
if (witness.unlimited_raw == witness.final_raw == target and
|
||||
witness.final_raw - witness.previous_torque == (-step if unwind else step)):
|
||||
check_packet(ci, packets, witness)
|
||||
stamp = 10_000_000_000 + witness.frame * 10_000_000
|
||||
return Emission(stamp, stamp, stamp + 1_000_000, witness, 'a' * 64, 'b' * 64, 'c' * 64,
|
||||
True, True, False, False, False, False, False, 0, 0, '')
|
||||
raise AssertionError('Actual registered encoder did not provide the unclipped rate boundary')
|
||||
|
||||
def test_exact_same_request_uses_candidate_native_prestate(self):
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA
|
||||
from openpilot.starpilot.flm.torque_emission_replay import EncoderBranch
|
||||
|
||||
for platform in (GM.GMC_ACADIA, HYUNDAI.HYUNDAI_IONIQ_6, TOYOTA.TOYOTA_COROLLA_TSS2):
|
||||
with self.subTest(platform=platform):
|
||||
event = self.boundary(platform)
|
||||
w = event.witness
|
||||
original = EncoderBranch()
|
||||
self.assertEqual(original.step(event, w.requested_torque, actual=True), w.final_raw / w.effective_max)
|
||||
strict = EncoderBranch()
|
||||
strict.previous = w.previous_torque - 1
|
||||
with self.assertRaisesRegex(ValueError, 'limiter/support'):
|
||||
strict.step(event, w.requested_torque)
|
||||
candidate = EncoderBranch(allow_unclipped_equal_output=True)
|
||||
candidate.previous = w.previous_torque - 1
|
||||
self.assertEqual(candidate.step(event, w.requested_torque), (w.final_raw - 1) / w.effective_max)
|
||||
self.assertEqual(candidate.previous, w.final_raw - 1)
|
||||
self.assertFalse(event.limited)
|
||||
self.assertEqual(event.witness, w)
|
||||
|
||||
def test_native_unwind_cap_is_not_an_unsafe_request(self):
|
||||
from opendbc.car.hyundai.values import CAR
|
||||
from openpilot.starpilot.flm.torque_emission_replay import EncoderBranch
|
||||
|
||||
event = self.boundary(CAR.HYUNDAI_IONIQ_6, unwind=True)
|
||||
w = event.witness
|
||||
request = (w.unlimited_raw - 1) / w.effective_max
|
||||
with self.assertRaisesRegex(ValueError, 'limiter/support'):
|
||||
EncoderBranch().step(event, request)
|
||||
branch = EncoderBranch(allow_unclipped_equal_output=True)
|
||||
self.assertEqual(branch.step(event, request), w.final_raw / w.effective_max)
|
||||
self.assertEqual(branch.previous, w.final_raw)
|
||||
for unsafe in ((w.unlimited_raw + 1) / w.effective_max, -w.requested_torque):
|
||||
candidate = EncoderBranch(allow_unclipped_equal_output=True)
|
||||
candidate.previous = w.previous_torque + 100
|
||||
with self.assertRaises(ValueError):
|
||||
candidate.step(event, unsafe)
|
||||
|
||||
def test_equal_output_does_not_relax_original_or_legacy_quality(self):
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA
|
||||
from openpilot.starpilot.flm.torque_emission_replay import EncoderBranch
|
||||
from openpilot.starpilot.flm.tests import test_state_aligned_encoder as legacy
|
||||
|
||||
event = self.boundary(HYUNDAI.HYUNDAI_IONIQ_6)
|
||||
branch = EncoderBranch(allow_unclipped_equal_output=True)
|
||||
branch.previous = event.witness.previous_torque - 1
|
||||
with self.assertRaisesRegex(ValueError, 'actual previous'):
|
||||
branch.step(event, event.witness.requested_torque, actual=True)
|
||||
clipped = legacy.captured_event(TOYOTA.TOYOTA_COROLLA_TSS2)
|
||||
with self.assertRaisesRegex(ValueError, 'limiter/support'):
|
||||
EncoderBranch(allow_unclipped_equal_output=True).step(replace(clipped, limited=False), clipped.witness.requested_torque)
|
||||
for platform in (TOYOTA.TOYOTA_COROLLA_TSS2, GM.GMC_ACADIA):
|
||||
legacy.test_registered_measured_driver_same_request_shadow_and_increased_demand(platform)
|
||||
legacy.test_common_state_unlimited_reference_recorded_opposite_and_original_unlimited_vetoes()
|
||||
legacy.test_unsupported_paired_projection_fails_without_advancing_candidate_native_state()
|
||||
legacy.test_honda_normalized_pre_lookup_comparison_and_modified_filter_shadow_is_pure()
|
||||
legacy.test_honda_nonfinite_reference_and_paired_original_pressed_fail_closed()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -102,7 +102,8 @@ def exact_local_emission(witness, reference_raw, candidate_raw, recorded_debt, r
|
||||
class EncoderBranch:
|
||||
"""Own previous raw/normalized torque and modified-Civic state per branch."""
|
||||
|
||||
def __init__(self, *, quality_contract=RECORDED_DEBT, identification_policy=STRICT_IDENTIFICATION):
|
||||
def __init__(self, *, quality_contract=RECORDED_DEBT, identification_policy=STRICT_IDENTIFICATION, allow_unclipped_equal_output=False):
|
||||
self.allow_unclipped_equal_output = allow_unclipped_equal_output
|
||||
self.quality_contract = clipping_quality(quality_contract)
|
||||
self.identification_policy = validate_identification_policy(identification_policy)
|
||||
if (self.identification_policy == NATIVE_SHADOW_IDENTIFICATION and
|
||||
@@ -198,6 +199,28 @@ class EncoderBranch:
|
||||
admitted = exact_local_emission(witness, reference_raw, raw, original_demand, reference_demand, candidate_demand)
|
||||
if not shadow and (not original_demand or not actual and not admitted):
|
||||
raise ValueError('candidate encoder clipping regression')
|
||||
if (raw is None and self.allow_unclipped_equal_output and not actual and not shadow and not event.limited and
|
||||
witness.mapping_kind == 'round' and not witness.shape and witness.steer_requested and
|
||||
witness.unlimited_raw == witness.final_raw and witness.limiter_kind in ('driver', 'measured')):
|
||||
from opendbc.car.steering_projection import project_request
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_meas_steer_torque_limits
|
||||
|
||||
requested_raw = project_request(witness, request)
|
||||
reference_raw = project_request(witness, witness.requested_torque)
|
||||
# Replaying a different history can engage a rate limiter that was idle
|
||||
# on the original drive. BOTH requests must produce the EXACT same native
|
||||
# output from this SAME candidate prestate, with no larger request.
|
||||
if (reference_raw == witness.unlimited_raw and requested_raw is not None and reference_raw is not None and
|
||||
(requested_raw == reference_raw or requested_raw * reference_raw > 0 and abs(requested_raw) < abs(reference_raw))):
|
||||
params = SimpleNamespace(STEER_MAX=witness.effective_max, **dict(witness.limits))
|
||||
if witness.limiter_kind == 'driver':
|
||||
candidate_output = apply_driver_steer_torque_limits(requested_raw, self.previous, witness.measured_torque, params, int(witness.effective_max))
|
||||
reference_output = apply_driver_steer_torque_limits(reference_raw, self.previous, witness.measured_torque, params, int(witness.effective_max))
|
||||
else:
|
||||
candidate_output = apply_meas_steer_torque_limits(requested_raw, self.previous, witness.measured_torque, params)
|
||||
reference_output = apply_meas_steer_torque_limits(reference_raw, self.previous, witness.measured_torque, params)
|
||||
if candidate_output == reference_output:
|
||||
raw = candidate_output
|
||||
if raw is None and (event.limited or shadow) and witness.limiter_kind in ('driver', 'measured'):
|
||||
from opendbc.car.steering_projection import project_request
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_meas_steer_torque_limits
|
||||
@@ -454,9 +477,10 @@ class EmissionHistory:
|
||||
return base, features
|
||||
|
||||
def rollout(self, requests, start_ns, end_ns, windows=(), *, quality_contract=RECORDED_DEBT,
|
||||
identification_policy=STRICT_IDENTIFICATION, feedback_reported=None):
|
||||
identification_policy=STRICT_IDENTIFICATION, feedback_reported=None, allow_unclipped_equal_output=False):
|
||||
"""Advance on EVERY actual emission, including unscored warmup emissions."""
|
||||
branch = EncoderBranch(quality_contract=quality_contract, identification_policy=identification_policy)
|
||||
branch = EncoderBranch(quality_contract=quality_contract, identification_policy=identification_policy,
|
||||
allow_unclipped_equal_output=allow_unclipped_equal_output)
|
||||
if feedback_reported is not None and (type(feedback_reported) is not dict or feedback_reported):
|
||||
raise ValueError('empty private candidate reported CO sink required')
|
||||
values = {}
|
||||
@@ -469,7 +493,8 @@ class EmissionHistory:
|
||||
raise ValueError('missing consumed controller source')
|
||||
window = next((i for i, (a, b) in enumerate(windows) if a <= event.control_ns <= b), 0)
|
||||
if window != last_window:
|
||||
branch = EncoderBranch(quality_contract=quality_contract, identification_policy=identification_policy)
|
||||
branch = EncoderBranch(quality_contract=quality_contract, identification_policy=identification_policy,
|
||||
allow_unclipped_equal_output=allow_unclipped_equal_output)
|
||||
previous = None
|
||||
self.decode(event)
|
||||
value = branch.step(event, requests[event.control_ns])
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "52558c336dd65c0d44d35ffdc87186a7bb059bd3243d2f18b6fe4522809484a9", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "e94ee72057011ff5d3cd0a0250d65d4ebf13d3f526196d04626c2fec6ddc29db", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user