refactor(lab): separate release contract identity

This commit is contained in:
DCCONSTRUCTIONS
2026-08-31 19:46:58 +03:00
parent 7b021782d3
commit 104e54f4dd
6 changed files with 111 additions and 26 deletions
@@ -65,13 +65,13 @@ PORTABLE_LAB_V1_RESULT_IDENTITY_SCHEMA: Final = (
"missioncore.recorded-eomt-ddrnet-review-identity/v2"
)
PORTABLE_LAB_V1_RELEASE_SCHEMA: Final = (
"missioncore.observatory-portable-lab-v1-executor-candidate/v1"
"missioncore.observatory-portable-lab-v1-executor-candidate/v2"
)
PORTABLE_LAB_V1_RELEASE_IDENTITY_SCHEMA: Final = (
"missioncore.observatory-portable-lab-v1-executor-candidate-identity/v1"
"missioncore.observatory-portable-lab-v1-executor-candidate-identity/v2"
)
PORTABLE_LAB_V1_EXECUTOR_SEAL_SCHEMA: Final = (
"missioncore.observatory-portable-lab-v1-executor-seal/v1"
"missioncore.observatory-portable-lab-v1-executor-seal/v2"
)
PORTABLE_LAB_V1_DDRNET_PROFILE_SCHEMA: Final = (
"missioncore.lab-v1-eomt-ddrnet-portable-profile/v2"
@@ -452,7 +452,7 @@ class PortableLabV1ReleaseCandidate:
setup_id: str
definition_id: str
definition_version: int
definition_sha256: str
definition_contract_sha256: str
result_contract_sha256: str
executor_image_sha256: str | None
assets: tuple[PortableLabV1ReleaseAsset, ...]
@@ -474,7 +474,10 @@ class PortableLabV1ReleaseCandidate:
or self.definition_version < 1
):
raise PortableLabV1ReleaseError("release definition version is invalid")
_digest(self.definition_sha256, "release definition sha256")
_digest(
self.definition_contract_sha256,
"release definition contract sha256",
)
_digest(self.result_contract_sha256, "release result contract sha256")
if self.executor_image_sha256 is not None:
_digest(self.executor_image_sha256, "release executor image sha256")
@@ -523,7 +526,7 @@ class PortableLabV1ReleaseCandidate:
"setup_id",
"definition_id",
"definition_version",
"definition_sha256",
"definition_contract_sha256",
"result_contract_sha256",
"executor_image_sha256",
"assets",
@@ -563,8 +566,9 @@ class PortableLabV1ReleaseCandidate:
definition_version=_positive_int(
document["definition_version"], "release definition version"
),
definition_sha256=_string(
document["definition_sha256"], "release definition sha256"
definition_contract_sha256=_string(
document["definition_contract_sha256"],
"release definition contract sha256",
),
result_contract_sha256=_string(
document["result_contract_sha256"],
@@ -587,7 +591,7 @@ class PortableLabV1ReleaseCandidate:
"setup_id": self.setup_id,
"definition_id": self.definition_id,
"definition_version": self.definition_version,
"definition_sha256": self.definition_sha256,
"definition_contract_sha256": self.definition_contract_sha256,
"result_contract_sha256": self.result_contract_sha256,
"executor_image_sha256": self.executor_image_sha256,
"assets": [asset.as_dict() for asset in self.assets],
@@ -601,7 +605,8 @@ class PortableLabV1ReleaseCandidate:
definition.setup_id != self.setup_id
or definition.definition_id != self.definition_id
or definition.version != self.definition_version
or definition.definition_sha256 != self.definition_sha256
or definition.executable_contract_sha256
!= self.definition_contract_sha256
or definition.result_contract.contract_sha256
!= self.result_contract_sha256
):
@@ -661,7 +666,7 @@ class PortableLabV1ReleaseCandidate:
"schema_version": PORTABLE_LAB_V1_EXECUTOR_SEAL_SCHEMA,
"release_id": self.release_id,
"candidate_sha256": self.candidate_sha256,
"definition_sha256": self.definition_sha256,
"definition_contract_sha256": self.definition_contract_sha256,
"executor_image_sha256": self.executor_image_sha256,
"asset_sha256s": [asset.sha256 for asset in self.assets],
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
@@ -669,7 +674,7 @@ class PortableLabV1ReleaseCandidate:
return PortableLabV1ExecutorSeal(
release_id=self.release_id,
candidate_sha256=self.candidate_sha256,
definition_sha256=self.definition_sha256,
definition_contract_sha256=self.definition_contract_sha256,
executor_image_sha256=self.executor_image_sha256,
release_sha256=canonical_sha256(identity),
)
@@ -681,7 +686,7 @@ class PortableLabV1ExecutorSeal:
release_id: str
candidate_sha256: str
definition_sha256: str
definition_contract_sha256: str
executor_image_sha256: str
release_sha256: str
@@ -689,7 +694,10 @@ class PortableLabV1ExecutorSeal:
_pattern(self.release_id, _IDENTIFIER, "executor seal release id")
for value, label in (
(self.candidate_sha256, "executor seal candidate sha256"),
(self.definition_sha256, "executor seal definition sha256"),
(
self.definition_contract_sha256,
"executor seal definition contract sha256",
),
(self.executor_image_sha256, "executor seal image sha256"),
(self.release_sha256, "executor seal release sha256"),
):
@@ -700,7 +708,7 @@ class PortableLabV1ExecutorSeal:
"schema_version": PORTABLE_LAB_V1_EXECUTOR_SEAL_SCHEMA,
"release_id": self.release_id,
"candidate_sha256": self.candidate_sha256,
"definition_sha256": self.definition_sha256,
"definition_contract_sha256": self.definition_contract_sha256,
"executor_image_sha256": self.executor_image_sha256,
"release_sha256": self.release_sha256,
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
@@ -505,7 +505,7 @@ def compose_lab_v1_portable_executor_adapter(
) -> PortableWorkerExecutorAdapter:
"""Compose the shared Worker ports without enabling or registering them."""
_verify_candidate_release(candidate, installation)
_verify_candidate_release(candidate, installation, definition)
return PortableWorkerExecutorAdapter(
candidate=candidate,
definition=definition,
@@ -560,6 +560,7 @@ def _verify_runtime_plan(
def _verify_candidate_release(
candidate: PortableWorkerRuntimeCandidate,
installation: PortableLabV1RunnerInstallation,
definition: PortableRunDefinition,
) -> None:
release = installation.release
release_assets = {asset.asset_id: asset for asset in release.assets}
@@ -569,7 +570,9 @@ def _verify_candidate_release(
candidate.setup_id != release.setup_id
or candidate.definition_id != release.definition_id
or candidate.definition_version != release.definition_version
or candidate.definition_sha256 != release.definition_sha256
or candidate.definition_sha256 != definition.definition_sha256
or release.definition_contract_sha256
!= definition.executable_contract_sha256
or candidate.result_contract_sha256 != release.result_contract_sha256
or tuple(phase.phase_id for phase in candidate.phases) != PORTABLE_LAB_V1_RUNTIME_PHASES
or executor is None
@@ -32,6 +32,9 @@ PORTABLE_RUN_DEFINITION_REGISTRY_SCHEMA: Final = (
PORTABLE_RUN_DEFINITION_IDENTITY_SCHEMA: Final = (
"missioncore.observatory-portable-run-definition-identity/v2"
)
PORTABLE_RUN_DEFINITION_EXECUTABLE_CONTRACT_SCHEMA: Final = (
"missioncore.observatory-portable-run-definition-executable-contract/v1"
)
PORTABLE_SOURCE_ADAPTER_IDENTITY_SCHEMA: Final = "missioncore.portable-source-adapter/v1"
PORTABLE_MODEL_MANIFEST_SCHEMA: Final = "missioncore.observatory-portable-model-manifest/v2"
PORTABLE_RESOURCE_PROFILE_SCHEMA: Final = "missioncore.observatory-portable-resource-profile/v2"
@@ -571,6 +574,35 @@ class PortableRunDefinition:
def learned_models(self) -> tuple[str, ...]:
return tuple(model.release_id for model in self.models)
@property
def executable_contract_sha256(self) -> str:
"""Return the stable executable contract, independent of installation state.
Executor availability is deliberately excluded. An executor release may
bind this digest before the resulting release identity is written back to
the full RunDefinition without creating a definition/release digest cycle.
"""
return canonical_sha256(self.executable_contract_document())
def executable_contract_document(self) -> dict[str, object]:
"""Return the canonical computation contract without executor availability."""
return {
"schema_version": PORTABLE_RUN_DEFINITION_EXECUTABLE_CONTRACT_SCHEMA,
"setup_id": self.setup_id,
"definition_id": self.definition_id,
"version": self.version,
"source_requirements": self.source_requirements.as_dict(),
"source_adapter": self.source_adapter.as_dict(),
"components": [component.as_dict() for component in self.components],
"models": [model.as_dict() for model in self.models],
"model_manifest_sha256": self.model_manifest_sha256,
"resource_profile": self.resource_profile.as_dict(),
"result_contract": self.result_contract.as_dict(),
"authority": self.authority.as_dict(),
}
def identity_document(self) -> dict[str, object]:
"""Return the complete canonical executable identity."""