feat(perception): enforce six-layer freshness through receipt
This commit is contained in:
@@ -181,6 +181,9 @@ class LayerEvidence:
|
||||
source_time_ns: int | None
|
||||
state: str
|
||||
payload_sha256: str | None
|
||||
# Old callers omit this field. New layered results require it explicitly:
|
||||
# recomputing a derived layer cannot refresh the age of its required inputs.
|
||||
oldest_required_input_time_ns: int | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.layer not in REQUIRED_LAYERS:
|
||||
@@ -191,13 +194,22 @@ class LayerEvidence:
|
||||
if self.state == "unavailable":
|
||||
if any(
|
||||
x is not None
|
||||
for x in (self.source_sequence, self.source_time_ns, self.payload_sha256)
|
||||
for x in (
|
||||
self.source_sequence,
|
||||
self.source_time_ns,
|
||||
self.payload_sha256,
|
||||
self.oldest_required_input_time_ns,
|
||||
)
|
||||
):
|
||||
raise RealtimeContractError("unavailable evidence cannot claim a payload")
|
||||
else:
|
||||
_integer(self.source_sequence, "source_sequence")
|
||||
_integer(self.source_time_ns, "source_time_ns")
|
||||
_digest(self.payload_sha256, "payload_sha256")
|
||||
if self.oldest_required_input_time_ns is not None:
|
||||
_integer(self.oldest_required_input_time_ns, "oldest_required_input_time_ns")
|
||||
if self.oldest_required_input_time_ns > cast(int, self.source_time_ns):
|
||||
raise RealtimeContractError("required input is newer than its layer")
|
||||
|
||||
|
||||
def validate_scene_layers(
|
||||
@@ -223,7 +235,10 @@ def validate_scene_layers(
|
||||
raise RealtimeContractError("layer belongs to another stream epoch")
|
||||
if layer.source_time_ns is None:
|
||||
continue
|
||||
age_ns = source_time_ns - layer.source_time_ns
|
||||
if layer.source_time_ns > source_time_ns:
|
||||
raise RealtimeContractError("future observations are not causal input")
|
||||
stamp = layer.oldest_required_input_time_ns
|
||||
age_ns = source_time_ns - (layer.source_time_ns if stamp is None else stamp)
|
||||
if age_ns < 0:
|
||||
raise RealtimeContractError("future observations are not causal input")
|
||||
if age_ns > maximum_layer_age_ms * 1_000_000 and layer.state in ("current", "held"):
|
||||
|
||||
Reference in New Issue
Block a user