diff --git a/src/k1link/observatory/source_admission.py b/src/k1link/observatory/source_admission.py index 166e643..e98ab09 100644 --- a/src/k1link/observatory/source_admission.py +++ b/src/k1link/observatory/source_admission.py @@ -684,7 +684,7 @@ class RecordedK1SourceAdmissionService: or manifest.session_id != media_artifact.session_id or manifest.artifact_id != media_artifact.artifact_id or manifest.public_source_id != media_artifact.public_source_id - or manifest.byte_length != media_artifact.byte_length + or not 0 < manifest.byte_length <= media_artifact.byte_length or len(manifest.epochs) != 1 or manifest.synchronization != "host-arrival-best-effort" or not _SHA256.fullmatch(manifest.generation_sha256) diff --git a/tests/test_observatory_source_admission.py b/tests/test_observatory_source_admission.py index 01a9a32..068b30b 100644 --- a/tests/test_observatory_source_admission.py +++ b/tests/test_observatory_source_admission.py @@ -335,6 +335,75 @@ def test_portable_source_admission_is_independent_from_session_label( assert capability["camera_profile"]["height"] == 600 +def test_portable_source_admission_accepts_camera_archive_metadata_overhead( + tmp_path: Path, +) -> None: + session_id = "20260828T130511Z_viewer_live" + manifest = _manifest(session_id, tmp_path) + summary_payload = b'{"schema_version":"missioncore.camera-recording/v1"}' + index_payload = b'{"sequence":1,"length":10}\n' + archive_byte_length = manifest.byte_length + len(summary_payload) + len(index_payload) + detail = _detail(session_id, "RAVNOVES004TREE") + detail = replace( + detail, + artifacts=tuple( + replace(artifact, byte_length=archive_byte_length) + if artifact.kind == "recorded-video" + else artifact + for artifact in detail.artifacts + ), + ) + store = _Store(tmp_path, detail) + store.media = ( + replace(store.media[0], byte_length=archive_byte_length), + ) + service = RecordedK1SourceAdmissionService( + data_dir=tmp_path, + session_store=store, # type: ignore[arg-type] + media_inspector=_Inspector(manifest), # type: ignore[arg-type] + requirements=_requirements(), + ) + + admission = service.check(session_id) + + assert admission.frame_count == 1 + assert archive_byte_length > manifest.byte_length + + +def test_portable_source_admission_rejects_media_payload_larger_than_catalog( + tmp_path: Path, +) -> None: + session_id = "20260831T000000Z_viewer_live" + manifest = _manifest(session_id, tmp_path) + catalog_byte_length = manifest.byte_length - 1 + detail = _detail(session_id, "RAVNOVES005") + detail = replace( + detail, + artifacts=tuple( + replace(artifact, byte_length=catalog_byte_length) + if artifact.kind == "recorded-video" + else artifact + for artifact in detail.artifacts + ), + ) + store = _Store(tmp_path, detail) + store.media = ( + replace(store.media[0], byte_length=catalog_byte_length), + ) + service = RecordedK1SourceAdmissionService( + data_dir=tmp_path, + session_store=store, # type: ignore[arg-type] + media_inspector=_Inspector(manifest), # type: ignore[arg-type] + requirements=_requirements(), + ) + + with pytest.raises( + PortableSourceAdmissionIntegrityError, + match="epoch topology", + ): + service.check(session_id) + + def test_admission_seals_exact_catalogued_spatial_replay_metadata( tmp_path: Path, ) -> None: