From 05c99efb681852537c22d7172cd656ac401f391b Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 2 Sep 2026 01:02:38 +0300 Subject: [PATCH] test(observatory): isolate legacy contracts from installed package pins Freeze configuration-only legacy fixtures from 7025e17, update current installed-image assertions, and explicitly reject the prior agent image. Production admission and digest validation are unchanged. --- .../observatory/legacy-lab-v1/README.md | 13 + .../legacy-lab-v1/definitions.json | 241 ++++++++++++++++++ .../observatory/legacy-lab-v1/release.json | 218 ++++++++++++++++ ...st_observatory_portable_lab_v1_executor.py | 12 +- ...est_observatory_portable_worker_runtime.py | 24 +- 5 files changed, 493 insertions(+), 15 deletions(-) create mode 100644 tests/fixtures/observatory/legacy-lab-v1/README.md create mode 100644 tests/fixtures/observatory/legacy-lab-v1/definitions.json create mode 100644 tests/fixtures/observatory/legacy-lab-v1/release.json diff --git a/tests/fixtures/observatory/legacy-lab-v1/README.md b/tests/fixtures/observatory/legacy-lab-v1/README.md new file mode 100644 index 0000000..776ceb4 --- /dev/null +++ b/tests/fixtures/observatory/legacy-lab-v1/README.md @@ -0,0 +1,13 @@ +# Legacy LAB V1 contract fixtures + +Configuration-only snapshots from commit +`7025e173a3370a1b70afaad8579db20903436157`: + +- `definitions.json`: `config/observatory-portable-run-definitions.json`. +- `release.json`: `experiments/perception/worker/observatory_portable/lab-v1-eomt-ddrnet-executor-candidate.json`. + +The legacy orchestration/blocked-release tests must not read the mutable current +installed-package manifest: its asset vocabulary and executable contract differ. +Current installed-package tests continue to read current repository manifests. +These fixtures contain configuration references and digests, not raw recordings, +model weights, credentials, or evidence of current Worker readiness. diff --git a/tests/fixtures/observatory/legacy-lab-v1/definitions.json b/tests/fixtures/observatory/legacy-lab-v1/definitions.json new file mode 100644 index 0000000..ccf916d --- /dev/null +++ b/tests/fixtures/observatory/legacy-lab-v1/definitions.json @@ -0,0 +1,241 @@ +{ + "schema_version": "missioncore.observatory-portable-run-definition-registry/v2", + "definitions": [ + { + "setup_id": "lab-v1-eomt-ddrnet-portable-v1", + "definition_id": "lab-v1-eomt-ddrnet-portable", + "version": 2, + "definition_sha256": "3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2", + "source_requirements": { + "plugin_id": "nodedc.device.xgrids-lixelkity-k1", + "archive_id": "xgrids-k1.viewer-live.evidence", + "required_modalities": [ + "point-cloud", + "trajectory", + "video" + ], + "camera_source_id": "sensor.camera.right", + "camera_semantic_channel_id": "camera.video.recorded", + "recorded_media_type": "video/mp4; codecs=\"avc1.641028\"", + "recorded_media_init_sha256": "e2279963e16d84c91d68e7dbb1f7efed840533387dfeb844b7398bff45fbde38", + "camera_width": 800, + "camera_height": 600, + "calibration_slot": "camera_1", + "calibration_identity_sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9", + "exactly_one_media_epoch": true, + "seekable": true + }, + "source_adapter": { + "adapter_id": "xgrids-k1-recorded-observatory-v2", + "version": 2, + "contract_sha256": "4e12be6d2503e2e237eddb290b28d6a7d16cf983855b6d8e6b4e3b65d2feb0de" + }, + "components": [ + { + "component_id": "ddrnet-portable-runtime-config-v2", + "kind": "configuration", + "sha256": "c0ff657dadc86607d77d232e84d041fbf2d8b63e86d02319e9cd607220d00f21" + }, + { + "component_id": "eomt-recorded-dependency-set-v1", + "kind": "dependency-set", + "sha256": "4eb1f8d33236806e74f9e5bb96b7dce2ac37623dc39b2184be2aa8d7d00e983e" + }, + { + "component_id": "eomt-recorded-orchestrator-v1", + "kind": "orchestrator", + "sha256": "d3e9435939444ab35b27a744ac314e289ebd66a13fa56e3d59f121e088d22774" + }, + { + "component_id": "eomt-recorded-profile-v1", + "kind": "profile", + "sha256": "ea583966bc3409f5cf563cbf4fad05e366907e67187082eb692aff53d9f5d875" + }, + { + "component_id": "eomt-recorded-runner-v1", + "kind": "runner", + "sha256": "651e8e06c3912dffb036b7fd08f2c0623f7563d8306cc7aee05db562798518f4" + }, + { + "component_id": "k1-camera-1-calibration-v1", + "kind": "calibration", + "sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9" + }, + { + "component_id": "k1-valid-fov-identity-v1", + "kind": "valid-fov-identity", + "sha256": "b4dd8ddf2b87c1d520ee8a0868c4fea062d7c14d1bae73ccabd3abe1f3acbac2" + }, + { + "component_id": "k1-valid-fov-mask-v1", + "kind": "valid-fov-mask", + "sha256": "a40cee06b7c6f69b6a09a11563dcfd237f3de833b1ccd31459e66692e528ba63" + }, + { + "component_id": "vegetation-mission-policy-v1", + "kind": "policy", + "sha256": "b75c4ac841d7b4bcc57f7a9c8417ca2317d8ecfa499e72a9af8a8591a2ec0d35" + }, + { + "component_id": "vegetation-provider-label-map-v1", + "kind": "provider-map", + "sha256": "f2b69046b6a740fd9532d2d88e7fabae7c20fb662f783c9502adc9026406f352" + } + ], + "models": [ + { + "release_id": "eomt-cityscapes-large-1024-v1", + "model_id": "tue-mps/cityscapes_semantic_eomt_large_1024", + "revision": "8d6b6d1a3f7b50d441afd7d247c2ed10db186e8f", + "architecture": "EomtForUniversalSegmentation", + "artifacts": [ + { + "role": "config-json", + "byte_length": 1575, + "sha256": "7f4aa94fa4e43c0dbd79a5420edb511120aef62bd82bfbcbcece79948286a650" + }, + { + "role": "model-weights", + "byte_length": 1276175488, + "sha256": "c265da9a74f58f5c3f4826d23ca4ca78beac0b106cca5842beca61580de5b782" + }, + { + "role": "preprocessor-config", + "byte_length": 666, + "sha256": "97e2fbf7f0bdba2cfc90251c5133bae9c27ddc9c4410509f40670be2332854e7" + } + ] + }, + { + "release_id": "lab-v1-ddrnet-39-goose-fine-64-v1", + "model_id": "goose-ddrnet-class-512", + "revision": null, + "architecture": "ddrnet_39", + "artifacts": [ + { + "role": "checkpoint", + "byte_length": 259419077, + "sha256": "b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6" + } + ] + } + ], + "resource_profile": { + "schema_version": "missioncore.observatory-portable-resource-profile/v2", + "profile_id": "worker006-single-gpu-sequential-ai-v1", + "contour_id": "worker-006", + "accelerator_id": "nvidia-rtx-4090", + "concurrency": 1, + "checkpoint_policy": "non-checkpointable", + "allowed_checkpoints": [], + "profile_sha256": "7468138cad115210eda18e7a3350e3423d3d39ac0aae650b3266cdc2bc63fd5d" + }, + "result_contract": { + "schema_version": "missioncore.observatory-portable-result-contract/v2", + "contract_id": "recorded-eomt-ddrnet-review-v2", + "version": 2, + "result_schema": "missioncore.recorded-eomt-ddrnet-review/v2", + "result_kind": "recorded-perception-qualification", + "publication": "observatory", + "contract_sha256": "b3dfaa8e20a0f22fc510d062ac469f010a3281c650059d9ea134f0b3ccb38d9a" + }, + "executor": { + "contour_id": "worker-006", + "state": "not-installed", + "release_id": null, + "release_sha256": null, + "image_sha256": null, + "reason_code": "lab-v1-portable-v2-uninstalled", + "reason": "Portable LAB V1 v2 is not sealed or installed; a commit-bound combined image and Worker 006 receipt are required." + }, + "authority": { + "commands_enabled": false, + "actuation_allowed": false, + "navigation_or_safety_accepted": false, + "production_accepted": false + } + }, + { + "setup_id": "m49-tgs-portable-v2", + "definition_id": "m49-tgs-portable", + "version": 3, + "definition_sha256": "f56d6321bd794ccdfb7d2e3b05d044b11f616ffb81ee29517386cc253046d4eb", + "source_requirements": { + "plugin_id": "nodedc.device.xgrids-lixelkity-k1", + "archive_id": "xgrids-k1.viewer-live.evidence", + "required_modalities": [ + "point-cloud", + "trajectory", + "video" + ], + "camera_source_id": "sensor.camera.right", + "camera_semantic_channel_id": "camera.video.recorded", + "recorded_media_type": "video/mp4; codecs=\"avc1.641028\"", + "recorded_media_init_sha256": "e2279963e16d84c91d68e7dbb1f7efed840533387dfeb844b7398bff45fbde38", + "camera_width": 800, + "camera_height": 600, + "calibration_slot": "camera_1", + "calibration_identity_sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9", + "exactly_one_media_epoch": true, + "seekable": true + }, + "source_adapter": { + "adapter_id": "xgrids-k1-recorded-observatory-v2", + "version": 2, + "contract_sha256": "4e12be6d2503e2e237eddb290b28d6a7d16cf983855b6d8e6b4e3b65d2feb0de" + }, + "components": [ + { + "component_id": "k1-camera-1-calibration-v1", + "kind": "calibration", + "sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9" + }, + { + "component_id": "m49-tgs-portable-profile-v2", + "kind": "configuration", + "sha256": "6128d6af7e6137f9a9473db045e3b155e2105319159f17c32f344b4aedf823a9" + }, + { + "component_id": "m49-tgs-portable-runner-v1", + "kind": "runner", + "sha256": "e3bb2e91c70712eff74e8e69718075a407e042fb494616d65984500da42b21a9" + } + ], + "models": [], + "resource_profile": { + "schema_version": "missioncore.observatory-portable-resource-profile/v2", + "profile_id": "worker006-cpu-single-run-portable-v2", + "contour_id": "worker-006", + "accelerator_id": "cpu-only", + "concurrency": 1, + "checkpoint_policy": "non-checkpointable", + "allowed_checkpoints": [], + "profile_sha256": "49e373f1cbf314e7fab2d176db295f3d3d9ddbbcbd724bbde988ef60ad767dee" + }, + "result_contract": { + "schema_version": "missioncore.observatory-portable-result-contract/v2", + "contract_id": "m49-tgs-portable-review-v2", + "version": 2, + "result_schema": "missioncore.recorded-tgs-costmap-review/v2", + "result_kind": "recorded-perception-qualification", + "publication": "observatory", + "contract_sha256": "9dd80c8e2504559d2156fca933de6eb27901e35305e6853aeb84707e1cb13892" + }, + "executor": { + "contour_id": "worker-006", + "state": "ready", + "release_id": "m49-tgs-portable-executor-v1", + "release_sha256": "c5b0670d943fe0452ef4bbfbc144ab2439a1a674f9ef164798ad9f8b1ecc29fa", + "image_sha256": "f9278ab21aa65045be993dd19bffc25f49955e19598893ac78cc4761ca63ecf3", + "reason_code": null, + "reason": null + }, + "authority": { + "commands_enabled": false, + "actuation_allowed": false, + "navigation_or_safety_accepted": false, + "production_accepted": false + } + } + ] +} diff --git a/tests/fixtures/observatory/legacy-lab-v1/release.json b/tests/fixtures/observatory/legacy-lab-v1/release.json new file mode 100644 index 0000000..146a908 --- /dev/null +++ b/tests/fixtures/observatory/legacy-lab-v1/release.json @@ -0,0 +1,218 @@ +{ + "assets": [ + { + "asset_id": "ddrnet-checkpoint", + "byte_length": 259419077, + "kind": "model-artifact", + "repository_path": null, + "sha256": "b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6" + }, + { + "asset_id": "ddrnet-goose-image", + "byte_length": null, + "kind": "container-image", + "repository_path": null, + "sha256": "591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd" + }, + { + "asset_id": "ddrnet-goose-mapping", + "byte_length": null, + "kind": "runtime-artifact", + "repository_path": null, + "sha256": "88ae319ba5a3877dd3ae0773f693a6a5fdc283934140de9dfaff029108aefd7f" + }, + { + "asset_id": "ddrnet-goose-runner", + "byte_length": 32877, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/lab_v1_vegetation_goose/run_goose_vegetation_benchmark.py", + "sha256": "b18ad60f277eea69a240a28f290611b94627fb9707faf1bb3e6e22102dad67c1" + }, + { + "asset_id": "ddrnet-image-dockerfile", + "byte_length": 1793, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/lab_v1_vegetation_goose/Dockerfile", + "sha256": "8203fd01e05d8f5bcce11c328dd39dbb6b97df54ca5d9690706536bafe3d3bad" + }, + { + "asset_id": "ddrnet-portable-config", + "byte_length": 4324, + "kind": "repository-file", + "repository_path": "config/perception/lab-v1-eomt-ddrnet-portable-v2.json", + "sha256": "c0ff657dadc86607d77d232e84d041fbf2d8b63e86d02319e9cd607220d00f21" + }, + { + "asset_id": "eomt-config-json", + "byte_length": 1575, + "kind": "model-artifact", + "repository_path": null, + "sha256": "7f4aa94fa4e43c0dbd79a5420edb511120aef62bd82bfbcbcece79948286a650" + }, + { + "asset_id": "eomt-dependency-set", + "byte_length": null, + "kind": "definition-component", + "repository_path": null, + "sha256": "4eb1f8d33236806e74f9e5bb96b7dce2ac37623dc39b2184be2aa8d7d00e983e" + }, + { + "asset_id": "eomt-evaluation-helper", + "byte_length": 28915, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/run_evaluation_prelabels.py", + "sha256": "25baf30c0df564734e08f38ace88cc4bc147cacf240c761622279511e361daa4" + }, + { + "asset_id": "eomt-image", + "byte_length": null, + "kind": "container-image", + "repository_path": null, + "sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794" + }, + { + "asset_id": "eomt-model-weights", + "byte_length": 1276175488, + "kind": "model-artifact", + "repository_path": null, + "sha256": "c265da9a74f58f5c3f4826d23ca4ca78beac0b106cca5842beca61580de5b782" + }, + { + "asset_id": "eomt-orchestrator", + "byte_length": 21489, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/Invoke-E4FullSessionSegmentation.ps1", + "sha256": "d3e9435939444ab35b27a744ac314e289ebd66a13fa56e3d59f121e088d22774" + }, + { + "asset_id": "eomt-preprocessor-config", + "byte_length": 666, + "kind": "model-artifact", + "repository_path": null, + "sha256": "97e2fbf7f0bdba2cfc90251c5133bae9c27ddc9c4410509f40670be2332854e7" + }, + { + "asset_id": "eomt-profile", + "byte_length": 3805, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/e3_k1_camera1_profile.json", + "sha256": "ea583966bc3409f5cf563cbf4fad05e366907e67187082eb692aff53d9f5d875" + }, + { + "asset_id": "eomt-profile-runtime", + "byte_length": 45789, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/run_e3_rectified_segmentation.py", + "sha256": "01881862d4eaa218955f776a948124bf19c34be2b5ec282115daeacb15c53ae6" + }, + { + "asset_id": "eomt-recorded-runtime", + "byte_length": 34899, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/run_recorded_perception_epoch.py", + "sha256": "4dcc4fc8bdf33702651a199be69d0dd4fadb243d2e65aee1c3d1ae7a58fdf675" + }, + { + "asset_id": "eomt-runner", + "byte_length": 30720, + "kind": "repository-file", + "repository_path": "experiments/perception/worker/run_e4_full_session_segmentation.py", + "sha256": "651e8e06c3912dffb036b7fd08f2c0623f7563d8306cc7aee05db562798518f4" + }, + { + "asset_id": "k1-calibration", + "byte_length": null, + "kind": "definition-component", + "repository_path": null, + "sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9" + }, + { + "asset_id": "k1-valid-fov-identity", + "byte_length": null, + "kind": "definition-component", + "repository_path": null, + "sha256": "b4dd8ddf2b87c1d520ee8a0868c4fea062d7c14d1bae73ccabd3abe1f3acbac2" + }, + { + "asset_id": "k1-valid-fov-mask", + "byte_length": null, + "kind": "definition-component", + "repository_path": null, + "sha256": "a40cee06b7c6f69b6a09a11563dcfd237f3de833b1ccd31459e66692e528ba63" + }, + { + "asset_id": "lab-v1-portable-contracts", + "byte_length": 115196, + "kind": "repository-file", + "repository_path": "src/k1link/observatory/portable_lab_v1_executor.py", + "sha256": "2a18fe862f0934c78e86156708d8b80a3c31644a68829c8e673a0f48538f3473" + }, + { + "asset_id": "lab-v1-portable-worker", + "byte_length": 47392, + "kind": "repository-file", + "repository_path": "src/k1link/observatory/portable_lab_v1_worker.py", + "sha256": "56aa40390b8413fdf7f7293329e89420305914abd478821c08bfe8b0a2ec3df8" + }, + { + "asset_id": "portable-result-contracts", + "byte_length": 22008, + "kind": "repository-file", + "repository_path": "src/k1link/observatory/portable_result_contract.py", + "sha256": "936d6f20789c26e9bed1c9e34ee51259368eb95bc7e3aa549821d1f7de79030c" + }, + { + "asset_id": "portable-worker-runtime", + "byte_length": 39120, + "kind": "repository-file", + "repository_path": "src/k1link/observatory/portable_worker_runtime.py", + "sha256": "205d32116f1ba75d5257d15cc79b5146574d93acd52028a5950f6fca4d8129cb" + }, + { + "asset_id": "vegetation-policy", + "byte_length": 3022, + "kind": "repository-file", + "repository_path": "config/perception/lab-v1-vegetation-mission-policy-v1.json", + "sha256": "b75c4ac841d7b4bcc57f7a9c8417ca2317d8ecfa499e72a9af8a8591a2ec0d35" + }, + { + "asset_id": "vegetation-provider-map", + "byte_length": 2756, + "kind": "repository-file", + "repository_path": "config/perception/lab-v1-vegetation-provider-label-map-v1.json", + "sha256": "f2b69046b6a740fd9532d2d88e7fabae7c20fb662f783c9502adc9026406f352" + } + ], + "authority": { + "actuation_allowed": false, + "commands_enabled": false, + "navigation_or_safety_accepted": false, + "production_accepted": false + }, + "candidate_sha256": "9d40369e393aea4b1a09a5784336442a082b1b6126afaac379f1ecece39b54f9", + "declared_blockers": [ + "combined-executor-entrypoint-uninstalled", + "combined-executor-image-unsealed", + "commit-bound-source-unavailable", + "ddrnet-component-port-uninstalled", + "eomt-component-port-uninstalled", + "fixture-smoke-unaccepted", + "worker-installation-receipt-unavailable" + ], + "definition_id": "lab-v1-eomt-ddrnet-portable", + "definition_contract_sha256": "504e197db72e8bad258a2e4ec117d6d8247297847bd651470f8d43601e2e1fc7", + "definition_version": 2, + "executor_image_sha256": null, + "phases": [ + "source-materialization", + "eomt-full-session", + "ddrnet-full-session", + "result-v2-assembly", + "result-v2-validation", + "portable-result-packaging" + ], + "release_id": "lab-v1-eomt-ddrnet-worker006-candidate-v2", + "result_contract_sha256": "b3dfaa8e20a0f22fc510d062ac469f010a3281c650059d9ea134f0b3ccb38d9a", + "schema_version": "missioncore.observatory-portable-lab-v1-executor-candidate/v2", + "setup_id": "lab-v1-eomt-ddrnet-portable-v1" +} diff --git a/tests/test_observatory_portable_lab_v1_executor.py b/tests/test_observatory_portable_lab_v1_executor.py index 90875de..08acaba 100644 --- a/tests/test_observatory_portable_lab_v1_executor.py +++ b/tests/test_observatory_portable_lab_v1_executor.py @@ -63,15 +63,9 @@ from k1link.observatory.worker_agent import ( ) REPOSITORY_ROOT = Path(__file__).resolve().parents[1] -DEFINITIONS_PATH = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json" -RELEASE_PATH = ( - REPOSITORY_ROOT - / "experiments" - / "perception" - / "worker" - / "observatory_portable" - / "lab-v1-eomt-ddrnet-executor-candidate.json" -) +LEGACY_FIXTURES = REPOSITORY_ROOT / "tests/fixtures/observatory/legacy-lab-v1" +DEFINITIONS_PATH = LEGACY_FIXTURES / "definitions.json" +RELEASE_PATH = LEGACY_FIXTURES / "release.json" PORTABLE_DDRNET_CONFIG = ( REPOSITORY_ROOT / "config" diff --git a/tests/test_observatory_portable_worker_runtime.py b/tests/test_observatory_portable_worker_runtime.py index dc346e3..fec3394 100644 --- a/tests/test_observatory_portable_worker_runtime.py +++ b/tests/test_observatory_portable_worker_runtime.py @@ -94,10 +94,10 @@ def test_production_candidates_bind_exact_definitions_and_both_are_ready() -> No assert lab_v1.blockers == () assert all(phase.state == "implemented" for phase in lab_v1.phases) assert lab_v1.executor_identity().release_sha256 == ( - "4788005a24ae3e1664aa7aaaf728fb816243bfde8555c80a608f40d1061925b3" + "667858623962cd6d9849a8985b803f59e429916b5c56f76a6fc6c80c0c54526b" ) assert lab_v1.executor_identity().image_sha256 == ( - "840175ccac731b16b7f2815eaebc6e4ef426c49b57daf1911064ff5b7945a8fd" + "5ad7d95baac63af13812cb693d492add4e806a333aba8e60edb2ea1aba754373" ) m49 = by_setup["m49-tgs-portable-v2"] @@ -207,12 +207,12 @@ def test_m49_portable_runner_source_release_is_content_addressed() -> None: def test_ready_lab_candidate_still_requires_complete_local_asset_admission() -> None: candidate = _runtime().resolve( "lab-v1-eomt-ddrnet-portable-v1", - "11460f0fa03f713b4f64c8742bc30cc31eac66321931e1edd035553f5c72974a", + "269d71a24b4e63cff54e01273f9d4b35fc6cdd72bc6fadec206169ae0777e6ac", ) bindings = { "agent-image": PortableWorkerLocalAssetBinding( asset_id="agent-image", - image_sha256=("840175ccac731b16b7f2815eaebc6e4ef426c49b57daf1911064ff5b7945a8fd"), + image_sha256=("5ad7d95baac63af13812cb693d492add4e806a333aba8e60edb2ea1aba754373"), ), "ddrnet-goose-runner": PortableWorkerLocalAssetBinding( asset_id="ddrnet-goose-runner", @@ -236,11 +236,11 @@ def test_ready_lab_candidate_still_requires_complete_local_asset_admission() -> ), "ddrnet-step-image": PortableWorkerLocalAssetBinding( asset_id="ddrnet-step-image", - image_sha256=("759eeac66145221b3c64f226b9ba9220363af36e4fda9353ef8169551587901c"), + image_sha256=("e6c986100613ec804f0e0076ca8695abf43ff88ef9d5d85f6857e0b41db74051"), ), "eomt-step-image": PortableWorkerLocalAssetBinding( asset_id="eomt-step-image", - image_sha256=("29a7e8caef51e1809f11b66498ef8bf3f9b2c5d3de40acdab2968cdd34b9623b"), + image_sha256=("adba3dc8c97b161ba261ec44fca9ebe1680f117d1bcb1481440172cc3331a174"), ), "vegetation-policy": PortableWorkerLocalAssetBinding( asset_id="vegetation-policy", @@ -276,6 +276,18 @@ def test_ready_lab_candidate_still_requires_complete_local_asset_admission() -> assert "asset-ddrnet-checkpoint-missing" in admission.blockers assert "asset-eomt-model-cache-missing" in admission.blockers + # A previously admitted image must not pass the current package's digest fence. + old_bindings = { + **bindings, + "agent-image": replace( + bindings["agent-image"], + image_sha256="840175ccac731b16b7f2815eaebc6e4ef426c49b57daf1911064ff5b7945a8fd", + ), + } + old_admission = inspect_runtime_candidate(candidate, old_bindings) + assert "asset-agent-image-mismatched" in old_admission.blockers + assert old_admission.ready is False + def test_local_asset_tampering_is_reported_without_execution(tmp_path: Path) -> None: candidate = _runtime().resolve(