#!/usr/bin/env python3 """Validate the local synthetic HEVC+audio bundle audit artifact. The local audit is our passwordless proof that the client can generate the same coded flash/tone train we will later send through the WAN and RCT path. This validator keeps the acceptance rules in one reusable place so later hardware failures can be compared against a known-good client-origin manifest. """ from __future__ import annotations import json import sys from pathlib import Path EXPECTED_EVENTS = 16 EXPECTED_AUDIO_PACKETS_PER_EVENT = 2 EXPECTED_VIDEO_PERIOD_US = 1_000_000 MAX_AUDIO_VIDEO_SKEW_US = 120_000 def fail(message: str) -> None: """Exit with a compact validation error for shell scripts and operators.""" raise SystemExit(f"local HEVC bundle audit failed: {message}") def require(condition: bool, message: str) -> None: """Keep validation checks readable while preserving precise error context.""" if not condition: fail(message) def load_manifest(path: Path) -> dict: """Read the manifest JSON generated by the local Rust bundle preflight.""" try: return json.loads(path.read_text()) except FileNotFoundError: fail(f"missing audit manifest: {path}") except json.JSONDecodeError as exc: fail(f"invalid audit JSON: {exc}") def validate_manifest(data: dict) -> dict: """Validate manifest-level and per-event timing invariants. Input: the `lesavka.local-hevc-bundle-audit.v1` JSON object. Output: the summary object for caller reporting. The checks intentionally match the analyzer evidence floor and sync-probe event train: sixteen ordered coded HEVC frames, two nearby audio packets per frame, and no per-bundle audio skew outside the coded pulse width. """ require( data.get("schema") == "lesavka.local-hevc-bundle-audit.v1", f"unexpected schema {data.get('schema')!r}", ) summary = data.get("summary") or {} events = data.get("events") or [] expected_summary = { "video_codec": "hevc", "metadata_mode": "1920x1080@30", "bundles": EXPECTED_EVENTS, "coded_video_events": EXPECTED_EVENTS, "annex_b_video_events": EXPECTED_EVENTS, "audio_packets": EXPECTED_EVENTS * EXPECTED_AUDIO_PACKETS_PER_EVENT, "bundles_with_audio_before_video": EXPECTED_EVENTS, "bundles_with_audio_after_video": EXPECTED_EVENTS, "monotonic_bundle_sequences": True, } for key, expected in expected_summary.items(): require( summary.get(key) == expected, f"summary {key} expected {expected!r}, got {summary.get(key)!r}", ) require(len(events) == EXPECTED_EVENTS, f"expected {EXPECTED_EVENTS} events, got {len(events)}") previous_seq = 0 previous_video_pts = None for index, event in enumerate(events, start=1): seq = int(event.get("bundle_seq", -1)) code = int(event.get("event_code", -1)) video_pts = int(event.get("video_capture_pts_us", -1)) send_pts = int(event.get("video_send_pts_us", -1)) capture_start = int(event.get("capture_start_us", -1)) capture_end = int(event.get("capture_end_us", -1)) audio_pts = [int(value) for value in event.get("audio_capture_pts_us") or []] max_skew = int(event.get("max_audio_video_skew_us", -1)) require(seq == index, f"event {index} has bundle_seq={seq}") require(code == index, f"event {index} has event_code={code}") require(seq > previous_seq, f"event {index} sequence is not monotonic") previous_seq = seq require(event.get("has_annex_b_start_code") is True, f"event {index} lacks Annex-B") require( int(event.get("audio_packets", -1)) == EXPECTED_AUDIO_PACKETS_PER_EVENT, f"event {index} has wrong audio packet count", ) require(len(audio_pts) == EXPECTED_AUDIO_PACKETS_PER_EVENT, f"event {index} missing audio PTS") require(capture_start <= video_pts <= capture_end, f"event {index} video outside bounds") require(all(capture_start <= pts <= capture_end for pts in audio_pts), f"event {index} audio outside bounds") require(any(pts < video_pts for pts in audio_pts), f"event {index} lacks pre-video audio") require(any(pts > video_pts for pts in audio_pts), f"event {index} lacks post-video audio") require(send_pts >= video_pts, f"event {index} send PTS precedes capture PTS") require(max_skew <= MAX_AUDIO_VIDEO_SKEW_US, f"event {index} audio/video skew {max_skew}us") if previous_video_pts is not None: period = video_pts - previous_video_pts require( period == EXPECTED_VIDEO_PERIOD_US, f"event {index} period {period}us != {EXPECTED_VIDEO_PERIOD_US}us", ) previous_video_pts = video_pts return summary def main(argv: list[str]) -> int: """Validate one manifest path and print a concise operator summary.""" if len(argv) != 2: print(f"usage: {argv[0]} AUDIT_JSON", file=sys.stderr) return 2 path = Path(argv[1]) summary = validate_manifest(load_manifest(path)) print("local HEVC bundle audit validation: pass") print(f"- schema: lesavka.local-hevc-bundle-audit.v1") print(f"- mode: {summary['metadata_mode']} codec={summary['video_codec']}") print(f"- bundles: {summary['bundles']}") print(f"- coded video events: {summary['coded_video_events']}/{EXPECTED_EVENTS}") print(f"- event codes: 1..{EXPECTED_EVENTS}") print(f"- Annex-B video events: {summary['annex_b_video_events']}/{EXPECTED_EVENTS}") print(f"- audio packets: {summary['audio_packets']}") print( "- bundles with audio before/after video: " f"{summary['bundles_with_audio_before_video']}/{summary['bundles_with_audio_after_video']}" ) print(f"- monotonic bundle sequences: {summary['monotonic_bundle_sequences']}") return 0 if __name__ == "__main__": raise SystemExit(main(sys.argv))