feat(observatory): add installed package dispatch and durable publication

Checkpoint existing backend lifecycle changes. Focused verification found nine legacy fixture failures in portable LAB V1 executor/runtime tests; repair follows separately without rewriting this snapshot. ADR date retains its intentional Markdown hard break.
This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 00:58:37 +03:00
parent d655d6998d
commit a945d665dd
47 changed files with 6008 additions and 1034 deletions
@@ -789,12 +789,23 @@ def test_result_v2_assembler_and_exact_validator_round_trip(
)
plan = PortableWorkerRuntimePlan(
job_id=sealed.job_id,
request_sha256=sealed.request_sha256,
identity_sha256=sealed.identity_sha256,
submission_receipt_sha256=sealed.submission_receipt_sha256,
claim_generation=sealed.claim_generation,
adapter_id="m49-tgs-worker006-portable-v2",
candidate_sha256="f" * 64,
setup_id=sealed.setup_id,
definition_id=sealed.definition_id,
definition_version=sealed.definition_version,
definition_sha256=sealed.definition_sha256,
source_session_id=sealed.source_session_id,
source_catalog_sha256=sealed.source_catalog_sha256,
source_bundle_sha256=sealed.source_bundle_sha256,
source_capability_manifest_sha256=sealed.source_capability_manifest_sha256,
source_adapter_id=sealed.source_adapter_id,
source_adapter_version=sealed.source_adapter_version,
source_adapter_sha256=sealed.source_adapter_sha256,
result_contract_sha256=definition.result_contract.contract_sha256,
phases=M49_PORTABLE_RUNTIME_PHASES,
)
@@ -0,0 +1,362 @@
from __future__ import annotations
import hashlib
import json
from dataclasses import replace
from pathlib import Path
from typing import cast
import httpx
import pytest
from k1link.observatory.installed_lab_package_runner import (
DockerEngineInstalledLabLauncher,
InstalledLabDockerLaunch,
InstalledLabDockerMount,
InstalledLabLocalAssetBinding,
InstalledLabPackageProfileRunner,
InstalledLabPackageRunnerError,
)
from k1link.observatory.installed_lab_packages import (
INSTALLED_LAB_PACKAGE_REGISTRY_SCHEMA,
InstalledLabContainer,
InstalledLabPackageMount,
seal_installed_lab_package,
)
from k1link.observatory.portable_result_contract import (
PORTABLE_RESULT_PACKAGE_IDENTITY_SCHEMA,
PortableResultArtifact,
PortableResultPackageManifest,
canonical_json,
result_identity_document,
run_definition_document,
)
from k1link.observatory.portable_run_definitions import (
PortableRunDefinitionRegistry,
canonical_sha256,
)
from k1link.observatory.portable_worker_runtime import (
PortableWorkerRuntimePlan,
PortableWorkerRuntimeRegistry,
PortableWorkerSourceStage,
)
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
DEFINITIONS_FILE = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
RUNTIME_FILE = REPOSITORY_ROOT / "config" / "observatory-worker-runtime-candidates.json"
def _package_and_runtime():
definitions = PortableRunDefinitionRegistry.from_file(DEFINITIONS_FILE)
runtime = PortableWorkerRuntimeRegistry.from_file(
RUNTIME_FILE,
definitions=definitions,
)
definition = definitions.resolve_setup("m49-tgs-portable-v2")
candidate = runtime.resolve(definition.setup_id, definition.definition_sha256)
assert candidate.executor is not None
mounted_asset = next(
asset for asset in candidate.reusable_assets if asset.kind != "container-image"
)
writer = InstalledLabContainer(
container_id="portable-result-writer",
role="result-writer",
image_sha256=candidate.executor.image_sha256,
argv=("/missioncore/package/run", INSTALLED_LAB_PACKAGE_REGISTRY_SCHEMA),
depends_on=(),
mounts=(
InstalledLabPackageMount(
asset_id=mounted_asset.asset_id,
target=f"/missioncore/package/assets/{mounted_asset.asset_id}",
),
),
network="none",
gpu_count=0,
memory_bytes=8 * 1024**3,
nano_cpus=2_000_000_000,
pids_limit=512,
shm_bytes=64 * 1024**2,
tmpfs_bytes=512 * 1024**2,
timeout_seconds=3600,
)
package = seal_installed_lab_package(
package_id="m49-generic-runner-test",
package_version=1,
setup_id=definition.setup_id,
definition_id=definition.definition_id,
definition_version=definition.version,
definition_sha256=definition.definition_sha256,
runtime_candidate_sha256=candidate.candidate_sha256,
source_adapter_sha256=definition.source_adapter.contract_sha256,
result_contract_sha256=definition.result_contract.contract_sha256,
executor_identity=candidate.executor_identity(),
execution_mode="single-container",
asset_ids=tuple(asset.asset_id for asset in candidate.reusable_assets),
containers=(writer,),
)
return definition, candidate, package, mounted_asset.asset_id
def _fixed_stack(package):
writer_template = package.containers[0]
step = replace(
writer_template,
container_id="compute-step",
role="step",
)
writer = replace(writer_template, depends_on=(step.container_id,))
return seal_installed_lab_package(
package_id=package.package_id,
package_version=package.package_version,
setup_id=package.setup_id,
definition_id=package.definition_id,
definition_version=package.definition_version,
definition_sha256=package.definition_sha256,
runtime_candidate_sha256=package.runtime_candidate_sha256,
source_adapter_sha256=package.source_adapter_sha256,
result_contract_sha256=package.result_contract_sha256,
executor_identity=package.executor_identity,
execution_mode="fixed-stack",
asset_ids=package.asset_ids,
containers=(step, writer),
)
def _plan(definition, candidate) -> PortableWorkerRuntimePlan:
return PortableWorkerRuntimePlan(
job_id=f"observatory-run-{'1' * 32}",
request_sha256="4" * 64,
identity_sha256="5" * 64,
submission_receipt_sha256="6" * 64,
claim_generation=1,
adapter_id=candidate.adapter_id,
candidate_sha256=candidate.candidate_sha256,
setup_id=definition.setup_id,
definition_id=definition.definition_id,
definition_version=definition.version,
definition_sha256=definition.definition_sha256,
source_session_id="source-a",
source_catalog_sha256="7" * 64,
source_bundle_sha256="2" * 64,
source_capability_manifest_sha256="3" * 64,
source_adapter_id=definition.source_adapter.adapter_id,
source_adapter_version=definition.source_adapter.version,
source_adapter_sha256=definition.source_adapter.contract_sha256,
result_contract_sha256=definition.result_contract.contract_sha256,
phases=tuple(phase.phase_id for phase in candidate.phases),
)
def _write_result(root: Path, *, plan: PortableWorkerRuntimePlan, definition) -> None:
result_id = "portable-result-generic-runner"
artifact_payload = canonical_json(
{"schema_version": definition.result_contract.result_schema, "verified": True}
)
artifact_path = root / "artifacts" / "result.json"
artifact_path.parent.mkdir(parents=True)
artifact_path.write_bytes(artifact_payload)
artifact = PortableResultArtifact(
role="result-document",
relative_path="artifacts/result.json",
media_type="application/json",
byte_length=len(artifact_payload),
sha256=hashlib.sha256(artifact_payload).hexdigest(),
)
job = {
"job_id": plan.job_id,
"request_sha256": plan.request_sha256,
"identity_sha256": plan.identity_sha256,
"submission_receipt_sha256": plan.submission_receipt_sha256,
"claim_generation": plan.claim_generation,
}
source = {
"session_id": plan.source_session_id,
"catalog_sha256": plan.source_catalog_sha256,
"bundle_sha256": plan.source_bundle_sha256,
"capability_manifest_sha256": plan.source_capability_manifest_sha256,
"adapter": {
"adapter_id": definition.source_adapter.adapter_id,
"version": definition.source_adapter.version,
"adapter_sha256": definition.source_adapter.contract_sha256,
},
}
run_definition = run_definition_document(definition)
result = result_identity_document(definition, result_id)
authority = definition.authority.as_dict()
created_at_utc = "2026-09-01T12:00:00Z"
identity_document = {
"schema_version": PORTABLE_RESULT_PACKAGE_IDENTITY_SCHEMA,
"created_at_utc": created_at_utc,
"job": job,
"source": source,
"run_definition": run_definition,
"result": result,
"authority": authority,
"artifacts": [artifact.as_dict()],
}
manifest = PortableResultPackageManifest(
identity_sha256=canonical_sha256(identity_document),
created_at_utc=created_at_utc,
job=job,
source=source,
run_definition=run_definition,
result=result,
authority=authority,
artifacts=(artifact,),
)
(root / "manifest.json").write_bytes(manifest.canonical_bytes)
def test_generic_fixed_stack_runs_topologically_and_returns_verified_package(
tmp_path: Path,
) -> None:
definition, candidate, single, mounted_asset_id = _package_and_runtime()
package = _fixed_stack(single)
source_root = tmp_path / "materialized-source"
source_root.mkdir()
asset_path = tmp_path / "installed-asset"
asset_path.write_bytes(b"asset")
plan = _plan(definition, candidate)
launches: list[InstalledLabDockerLaunch] = []
def launcher(launch: InstalledLabDockerLaunch) -> None:
launches.append(launch)
if launch.container.role == "result-writer":
output = next(
mount for mount in launch.mounts if mount.container_path == "/missioncore/output"
)
_write_result(Path(output.engine_path), plan=plan, definition=definition)
runner = InstalledLabPackageProfileRunner(
package=package,
definition=definition,
controller_work_root=tmp_path,
engine_work_root=str(tmp_path),
local_assets=(
InstalledLabLocalAssetBinding(
asset_id=mounted_asset_id,
controller_path=asset_path,
engine_path=str(asset_path),
),
),
launcher=launcher,
token_factory=lambda: "abcdef0123456789",
)
draft = runner.run(
plan,
PortableWorkerSourceStage(
root=source_root,
source_bundle_sha256=plan.source_bundle_sha256,
source_capability_manifest_sha256=plan.source_capability_manifest_sha256,
source_adapter_sha256=definition.source_adapter.contract_sha256,
),
)
assert [launch.container.container_id for launch in launches] == [
"compute-step",
"portable-result-writer",
]
writer_launch = launches[-1]
assert any(
mount.container_path == "/missioncore/input/steps/compute-step"
and mount.read_only
for mount in writer_launch.mounts
)
assert draft.result_id == "portable-result-generic-runner"
assert draft.result_contract_sha256 == definition.result_contract.contract_sha256
assert (draft.root / "manifest.json").is_file()
for launch in launches:
writable = [mount for mount in launch.mounts if not mount.read_only]
assert [mount.container_path for mount in writable] == ["/missioncore/output"]
def test_generic_docker_launcher_uses_hardened_one_shot_contract() -> None:
_definition, _candidate, package, _mounted_asset_id = _package_and_runtime()
requests: list[httpx.Request] = []
create_document: dict[str, object] = {}
container_id = "f" * 64
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
if request.url.path.startswith("/v1.47/images/"):
return httpx.Response(
200,
json={"Id": f"sha256:{package.containers[0].image_sha256}"},
)
if request.url.path == "/v1.47/containers/create":
create_document.update(json.loads(request.content))
return httpx.Response(201, json={"Id": container_id, "Warnings": None})
if request.url.path.endswith("/start"):
return httpx.Response(204)
if request.url.path.endswith("/wait"):
return httpx.Response(200, json={"StatusCode": 0, "Error": None})
if request.method == "DELETE":
return httpx.Response(204)
raise AssertionError(f"unexpected request: {request.method} {request.url}")
launch = InstalledLabDockerLaunch(
package_id=package.package_id,
container=package.containers[0],
mounts=(
InstalledLabDockerMount("/engine/plan.json", "/missioncore/input/run-plan.json", True),
InstalledLabDockerMount("/engine/source", "/missioncore/input/source", True),
InstalledLabDockerMount("/engine/output", "/missioncore/output", False),
InstalledLabDockerMount("/engine/asset", "/missioncore/package/assets/runner", True),
),
labels={
"com.nodedc.authority": "observation-only",
"com.nodedc.component": package.containers[0].container_id,
"com.nodedc.definition-sha256": package.definition_sha256,
"com.nodedc.job-id": f"observatory-run-{'1' * 32}",
"com.nodedc.managed-by": "mission-core-worker",
"com.nodedc.package-sha256": package.package_sha256,
"com.nodedc.product": "mission-core",
"com.nodedc.stack": "observatory",
},
name_token="0123456789abcdef",
)
DockerEngineInstalledLabLauncher(transport_factory=lambda: httpx.MockTransport(handler))(launch)
assert [request.method for request in requests] == [
"GET",
"POST",
"POST",
"POST",
"DELETE",
]
assert create_document["Image"] == f"sha256:{package.containers[0].image_sha256}"
assert create_document["NetworkDisabled"] is True
host = cast(dict[str, object], create_document["HostConfig"])
assert host["NetworkMode"] == "none"
assert host["ReadonlyRootfs"] is True
assert host["CapDrop"] == ["ALL"]
assert host["SecurityOpt"] == ["no-new-privileges:true"]
assert host["Privileged"] is False
assert host["DeviceRequests"] == []
mounts = cast(list[dict[str, object]], host["Mounts"])
assert sum(not cast(bool, mount["ReadOnly"]) for mount in mounts) == 1
assert next(mount for mount in mounts if not mount["ReadOnly"])["Target"] == (
"/missioncore/output"
)
def test_generic_docker_launcher_verifies_image_inventory_without_container_calls() -> None:
image_sha256s = ("1" * 64, "2" * 64)
requests: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
digest = request.url.path.split("sha256:", 1)[1].split("/", 1)[0]
return httpx.Response(200, json={"Id": f"sha256:{digest}"})
DockerEngineInstalledLabLauncher(
transport_factory=lambda: httpx.MockTransport(handler)
).verify_images(image_sha256s)
assert [request.method for request in requests] == ["GET", "GET"]
assert all("/images/sha256:" in request.url.path for request in requests)
with pytest.raises(InstalledLabPackageRunnerError, match="inventory"):
DockerEngineInstalledLabLauncher(
transport_factory=lambda: httpx.MockTransport(handler)
).verify_images(tuple(reversed(image_sha256s)))
@@ -0,0 +1,216 @@
from __future__ import annotations
import json
from dataclasses import replace
from pathlib import Path
from typing import cast
import pytest
from k1link.observatory.installed_lab_packages import (
INSTALLED_LAB_PACKAGE_REGISTRY_SCHEMA,
InstalledLabContainer,
InstalledLabPackageError,
InstalledLabPackageMount,
InstalledLabPackageRegistry,
seal_installed_lab_package,
)
from k1link.observatory.portable_run_definitions import PortableRunDefinitionRegistry
from k1link.observatory.portable_worker_runtime import (
PortableWorkerResultPublisher,
PortableWorkerRuntimeRegistry,
PortableWorkerSourceMaterializer,
)
from k1link.observatory.worker_agent import (
ObservatoryWorkerExecutionResult,
ObservatoryWorkerExecutorRegistration,
SealedObservatoryRecordedJob,
)
from k1link.observatory.worker_service import (
ObservatoryWorkerPackageExecutorBuildContext,
build_ready_executor_registry_from_packages,
)
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
DEFINITIONS_FILE = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
RUNTIME_FILE = REPOSITORY_ROOT / "config" / "observatory-worker-runtime-candidates.json"
def _registries() -> tuple[PortableRunDefinitionRegistry, PortableWorkerRuntimeRegistry]:
definitions = PortableRunDefinitionRegistry.from_file(DEFINITIONS_FILE)
runtime = PortableWorkerRuntimeRegistry.from_file(
RUNTIME_FILE,
definitions=definitions,
)
return definitions, runtime
def _m49_package():
definitions, runtime = _registries()
definition = definitions.resolve_setup("m49-tgs-portable-v2")
candidate = runtime.resolve(definition.setup_id, definition.definition_sha256)
assert candidate.executor is not None
non_image_asset = next(
asset for asset in candidate.reusable_assets if asset.kind != "container-image"
)
container = InstalledLabContainer(
container_id="portable-result-writer",
role="result-writer",
image_sha256=candidate.executor.image_sha256,
argv=("/missioncore/package/run", INSTALLED_LAB_PACKAGE_REGISTRY_SCHEMA),
depends_on=(),
mounts=(
InstalledLabPackageMount(
asset_id=non_image_asset.asset_id,
target=f"/missioncore/package/assets/{non_image_asset.asset_id}",
),
),
network="none",
gpu_count=0,
memory_bytes=8 * 1024**3,
nano_cpus=2_000_000_000,
pids_limit=512,
shm_bytes=64 * 1024**2,
tmpfs_bytes=512 * 1024**2,
timeout_seconds=3600,
)
return seal_installed_lab_package(
package_id="m49-container-contract-test",
package_version=1,
setup_id=definition.setup_id,
definition_id=definition.definition_id,
definition_version=definition.version,
definition_sha256=definition.definition_sha256,
runtime_candidate_sha256=candidate.candidate_sha256,
source_adapter_sha256=definition.source_adapter.contract_sha256,
result_contract_sha256=definition.result_contract.contract_sha256,
executor_identity=candidate.executor_identity(),
execution_mode="single-container",
asset_ids=tuple(asset.asset_id for asset in candidate.reusable_assets),
containers=(container,),
)
def test_installed_package_binds_exact_ready_definition_and_runtime() -> None:
definitions, runtime = _registries()
package = _m49_package()
definition = definitions.resolve(package.setup_id, package.definition_sha256)
candidate = runtime.resolve(package.setup_id, package.definition_sha256)
package.bind(definition, candidate)
document = package.identity_document()
serialized = json.dumps(document, sort_keys=True)
assert document["container_io"] == {
"schema_version": "missioncore.observatory-installed-lab-container-io/v2",
"source_root": "/missioncore/input/source",
"plan_path": "/missioncore/input/run-plan.json",
"step_input_root": "/missioncore/input/steps",
"result_root": "/missioncore/output",
"work_root": "/missioncore/work",
}
assert "host_path" not in serialized
assert "source_session_id" not in serialized
def test_installed_package_registry_round_trips_and_rejects_host_inputs(
tmp_path: Path,
) -> None:
definitions, runtime = _registries()
package = _m49_package()
document = {
"schema_version": INSTALLED_LAB_PACKAGE_REGISTRY_SCHEMA,
"packages": [{**package.identity_document(), "package_sha256": package.package_sha256}],
}
path = tmp_path / "installed-packages.json"
path.write_text(json.dumps(document), encoding="utf-8")
loaded = InstalledLabPackageRegistry.from_file(
path,
definitions=definitions,
runtime_registry=runtime,
)
assert loaded.resolve(package.setup_id, package.definition_sha256) == package
document["packages"][0]["containers"][0]["host_path"] = "/unsafe"
path.write_text(json.dumps(document), encoding="utf-8")
with pytest.raises(InstalledLabPackageError, match="forbidden"):
InstalledLabPackageRegistry.from_file(
path,
definitions=definitions,
runtime_registry=runtime,
)
def test_package_topology_and_digest_fail_closed() -> None:
package = _m49_package()
with pytest.raises(InstalledLabPackageError, match="digest"):
replace(package, package_sha256="f" * 64)
first = replace(
package.containers[0],
container_id="first-service",
role="step",
depends_on=("portable-result-writer",),
)
writer = replace(
package.containers[0],
depends_on=("first-service",),
)
with pytest.raises(InstalledLabPackageError, match="cycle"):
seal_installed_lab_package(
package_id=package.package_id,
package_version=package.package_version,
setup_id=package.setup_id,
definition_id=package.definition_id,
definition_version=package.definition_version,
definition_sha256=package.definition_sha256,
runtime_candidate_sha256=package.runtime_candidate_sha256,
source_adapter_sha256=package.source_adapter_sha256,
result_contract_sha256=package.result_contract_sha256,
executor_identity=package.executor_identity,
execution_mode="fixed-stack",
asset_ids=package.asset_ids,
containers=(first, writer),
)
with pytest.raises(InstalledLabPackageError, match="unsafe"):
InstalledLabPackageMount(
asset_id=package.asset_ids[0],
target="../../host",
)
def test_one_generic_factory_builds_installed_ready_subset(tmp_path: Path) -> None:
definitions, runtime = _registries()
package = _m49_package()
contexts: list[ObservatoryWorkerPackageExecutorBuildContext] = []
class Executor:
def execute(
self,
job: SealedObservatoryRecordedJob,
) -> ObservatoryWorkerExecutionResult:
raise AssertionError(job)
def factory(
context: ObservatoryWorkerPackageExecutorBuildContext,
) -> ObservatoryWorkerExecutorRegistration:
contexts.append(context)
return ObservatoryWorkerExecutorRegistration(
context.package.executor_identity,
Executor(),
)
executors = build_ready_executor_registry_from_packages(
definitions=definitions,
runtime_registry=runtime,
packages=InstalledLabPackageRegistry((package,)),
executor_factory=factory,
source_transport=cast(PortableWorkerSourceMaterializer, object()),
result_transport=cast(PortableWorkerResultPublisher, object()),
work_root=tmp_path,
)
assert executors.supported_identities == (package.executor_identity,)
assert [context.package.package_id for context in contexts] == [package.package_id]
@@ -0,0 +1,63 @@
from __future__ import annotations
import socket
import socketserver
import threading
import pytest
import k1link.observatory.installed_lab_worker_container_main as container_main
from k1link.observatory.worker_container_proxy import (
FixedObservatoryContainerLoopbackProxy,
)
class _EchoHandler(socketserver.BaseRequestHandler):
def handle(self) -> None:
payload = self.request.recv(1024)
self.request.sendall(payload)
def test_generic_proxy_bridges_loopback_to_fixed_upstream() -> None:
upstream = socketserver.ThreadingTCPServer(("127.0.0.1", 0), _EchoHandler)
upstream_thread = threading.Thread(target=upstream.serve_forever, daemon=True)
upstream_thread.start()
try:
upstream_port = upstream.server_address[1]
assert isinstance(upstream_port, int)
with FixedObservatoryContainerLoopbackProxy(
listen_port=0,
upstream_host="127.0.0.1",
upstream_port=upstream_port,
) as proxy, socket.create_connection(("127.0.0.1", proxy.listen_port)) as client:
client.sendall(b"generic-observatory-proxy")
client.shutdown(socket.SHUT_WR)
assert client.recv(1024) == b"generic-observatory-proxy"
finally:
upstream.shutdown()
upstream.server_close()
upstream_thread.join(timeout=5.0)
def test_generic_entrypoint_owns_proxy_around_package_worker(
monkeypatch: pytest.MonkeyPatch,
) -> None:
lifecycle: list[str] = []
class _Proxy:
def __enter__(self) -> _Proxy:
lifecycle.append("proxy-started")
return self
def __exit__(self, *_args: object) -> None:
lifecycle.append("proxy-stopped")
def worker(arguments: object) -> int:
lifecycle.append(f"worker:{arguments!r}")
return 23
monkeypatch.setattr(container_main, "FixedObservatoryContainerLoopbackProxy", _Proxy)
monkeypatch.setattr(container_main.installed_lab_worker_service, "main", worker)
assert container_main.main(("--once",)) == 23
assert lifecycle == ["proxy-started", "worker:('--once',)", "proxy-stopped"]
@@ -0,0 +1,131 @@
from __future__ import annotations
import json
from pathlib import Path
import pytest
from k1link.observatory.installed_lab_worker_service import (
INSTALLED_LAB_ASSET_BINDINGS_SCHEMA,
InstalledLabWorkerAssetBindings,
InstalledLabWorkerCompositionError,
InstalledLabWorkerEntrypointConfiguration,
InstalledLabWorkerValidationConfiguration,
)
def _binding_document() -> dict[str, object]:
return {
"schema_version": INSTALLED_LAB_ASSET_BINDINGS_SCHEMA,
"engine_work_root": "D:\\NDC_MISSIONCORE\\runtime\\worker\\work",
"assets": [
{
"asset_id": "eomt-image",
"controller_path": None,
"engine_path": None,
"image_sha256": "1" * 64,
},
{
"asset_id": "ddrnet-checkpoint",
"controller_path": "/runtime/assets/ddrnet-checkpoint.pth",
"engine_path": (
"D:\\NDC_MISSIONCORE\\runtime\\assets\\ddrnet-checkpoint.pth"
),
"image_sha256": None,
},
],
}
def test_asset_binding_file_loads_only_reviewed_paths_or_image_identities(
tmp_path: Path,
) -> None:
path = tmp_path / "bindings.json"
path.write_text(json.dumps(_binding_document()), encoding="utf-8")
bindings = InstalledLabWorkerAssetBindings.from_file(path)
assert bindings.engine_work_root.endswith("\\work")
assert tuple(item.asset_id for item in bindings.assets) == (
"ddrnet-checkpoint",
"eomt-image",
)
assert bindings.assets[0].controller_path == Path(
"/runtime/assets/ddrnet-checkpoint.pth"
)
assert bindings.assets[1].image_sha256 == "1" * 64
def test_asset_binding_file_rejects_ambiguous_locator(tmp_path: Path) -> None:
document = _binding_document()
assets = document["assets"]
assert isinstance(assets, list)
first = assets[0]
assert isinstance(first, dict)
first["controller_path"] = "/runtime/image"
first["engine_path"] = "D:\\runtime\\image"
path = tmp_path / "bindings.json"
path.write_text(json.dumps(document), encoding="utf-8")
with pytest.raises(InstalledLabWorkerCompositionError, match="ambiguous"):
InstalledLabWorkerAssetBindings.from_file(path)
def test_entrypoint_environment_contains_paths_but_no_executable_selector(
tmp_path: Path,
) -> None:
environment = {
"MISSIONCORE_OBSERVATORY_WORKER_TOKEN_FILE": str(tmp_path / "worker.token"),
"MISSIONCORE_OBSERVATORY_WORKER_WORK_ROOT": str(tmp_path / "work"),
"MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE": str(tmp_path / "definitions.json"),
"MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE": str(
tmp_path / "runtime.json"
),
"MISSIONCORE_OBSERVATORY_WORKER_PACKAGE_REGISTRY_FILE": str(
tmp_path / "packages.json"
),
"MISSIONCORE_OBSERVATORY_WORKER_PACKAGE_ASSET_BINDINGS_FILE": str(
tmp_path / "bindings.json"
),
}
configuration = InstalledLabWorkerEntrypointConfiguration.from_environment(environment)
assert configuration.package_registry_file == tmp_path / "packages.json"
assert configuration.asset_bindings_file == tmp_path / "bindings.json"
assert not any("COMMAND" in key or "MODULE" in key for key in environment)
def test_validation_environment_requires_no_token_or_backend_url(tmp_path: Path) -> None:
environment = {
"MISSIONCORE_OBSERVATORY_WORKER_WORK_ROOT": str(tmp_path / "work"),
"MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE": str(tmp_path / "definitions.json"),
"MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE": str(
tmp_path / "runtime.json"
),
"MISSIONCORE_OBSERVATORY_WORKER_PACKAGE_REGISTRY_FILE": str(
tmp_path / "packages.json"
),
"MISSIONCORE_OBSERVATORY_WORKER_PACKAGE_ASSET_BINDINGS_FILE": str(
tmp_path / "bindings.json"
),
}
configuration = InstalledLabWorkerValidationConfiguration.from_environment(environment)
assert configuration.work_root == tmp_path / "work"
assert not any("TOKEN" in key or "BASE_URL" in key for key in environment)
def test_asset_binding_file_rejects_unresolved_engine_path(tmp_path: Path) -> None:
document = _binding_document()
assets = document["assets"]
assert isinstance(assets, list)
file_asset = assets[1]
assert isinstance(file_asset, dict)
file_asset["engine_path"] = "D:\\runtime\\lab$release\\asset"
path = tmp_path / "bindings.json"
path.write_text(json.dumps(document), encoding="utf-8")
with pytest.raises(InstalledLabWorkerCompositionError, match="locator is invalid"):
InstalledLabWorkerAssetBindings.from_file(path)
@@ -544,6 +544,19 @@ def test_entrypoint_environment_requires_all_absolute_fixed_files(tmp_path: Path
assert configuration.lab_v1_release_candidate_file == (
service_module._FIXED_LAB_V1_RELEASE_CANDIDATE_FILE # noqa: SLF001
)
m49_only = service_module.M49WorkerEntrypointConfiguration.from_environment(
{
key: value
for key, value in environment.items()
if key
not in {
service_module.LAB_V1_WORKER_INSTALLATION_RECEIPT_FILE_ENV,
service_module.LAB_V1_WORKER_RELEASE_CANDIDATE_FILE_ENV,
}
}
)
assert m49_only.lab_v1_installation_receipt_file is None
assert m49_only.lab_v1_release_candidate_file is None
environment[service_module.LAB_V1_WORKER_RELEASE_CANDIDATE_FILE_ENV] = str(
tmp_path / "lab-v1-release.json"
)
@@ -126,7 +126,7 @@ def _source_documents(definition: PortableRunDefinition) -> tuple[bytes, bytes]:
"spatial_replay": {},
"camera": {
"artifact_id": "camera-recording",
"public_source_id": "sensor.camera.right",
"public_source_id": "recorded.camera.right",
"generation_sha256": CAMERA_GENERATION_SHA256,
"synchronization": "host-arrival-best-effort",
"epoch": {
@@ -198,22 +198,22 @@ def _camera_job(tmp_path: Path) -> CameraComputeJob:
root.mkdir()
files = [
{
"path": "input/camera/sensor.camera.right/epoch-1/summary.json",
"path": "input/camera/recorded.camera.right/epoch-1/summary.json",
"byte_length": 2,
"sha256": "1" * 64,
},
{
"path": "input/camera/sensor.camera.right/epoch-1/index.jsonl",
"path": "input/camera/recorded.camera.right/epoch-1/index.jsonl",
"byte_length": 3,
"sha256": "2" * 64,
},
{
"path": "input/camera/sensor.camera.right/epoch-1/init.mp4",
"path": "input/camera/recorded.camera.right/epoch-1/init.mp4",
"byte_length": 4,
"sha256": CAMERA_INIT_SHA256,
},
{
"path": "input/camera/sensor.camera.right/epoch-1/segments/1.m4s",
"path": "input/camera/recorded.camera.right/epoch-1/segments/1.m4s",
"byte_length": 7,
"sha256": CAMERA_SEGMENT_SHA256,
},
@@ -221,7 +221,7 @@ def _camera_job(tmp_path: Path) -> CameraComputeJob:
input_document = {
"kind": "canonical-camera-epoch",
"session_id": SOURCE_SESSION_ID,
"source_id": "sensor.camera.right",
"source_id": "recorded.camera.right",
"codec_epoch": 1,
"synchronization": "host-arrival-best-effort",
"media_type": "video/mp4; codecs=\"avc1.641028\"",
@@ -254,7 +254,7 @@ def _camera_job(tmp_path: Path) -> CameraComputeJob:
job_root=root,
manifest_path=root / "job.json",
session_id=SOURCE_SESSION_ID,
source_id="sensor.camera.right",
source_id="recorded.camera.right",
codec_epoch=1,
input_sha256=input_sha256,
input_byte_length=16,
@@ -477,7 +477,7 @@ def _component_outputs(
"job_id": plan.source_input.camera_job_id,
"input_sha256": plan.source_input.camera_input_sha256,
"session_id": SOURCE_SESSION_ID,
"source_id": "sensor.camera.right",
"source_id": "recorded.camera.right",
"codec_epoch": 1,
"timestamp_basis": "session-time-seconds",
"timeline_start_seconds": 1.0,
@@ -857,7 +857,7 @@ def _release_for_definition(
)
def test_release_candidate_matches_repository_but_stays_honestly_blocked(
def test_legacy_release_candidate_reports_repository_drift_and_stays_blocked(
tmp_path: Path,
) -> None:
release = PortableLabV1ReleaseCandidate.from_file(
@@ -870,7 +870,16 @@ def test_release_candidate_matches_repository_but_stays_honestly_blocked(
repository_assets = {
asset.asset_id for asset in release.assets if asset.repository_path is not None
}
assert set(inspection.matched_assets) == repository_assets
mismatched_assets = {
blocker.removeprefix("asset-").removesuffix("-mismatched")
for blocker in inspection.blockers
if blocker.startswith("asset-") and blocker.endswith("-mismatched")
}
assert set(inspection.matched_assets) | mismatched_assets == repository_assets
assert {
"lab-v1-portable-contracts",
"portable-worker-runtime",
} <= mismatched_assets
assert inspection.ready is False
assert "executor-image-unsealed" in inspection.blockers
assert "commit-bound-source-unavailable" in inspection.blockers
@@ -1132,7 +1141,7 @@ def test_shared_worker_stage_materializes_a_version_bound_camera_job(
materialized.camera_job_root
/ "input"
/ "camera"
/ "sensor.camera.right"
/ "recorded.camera.right"
/ "epoch-1"
/ "init.mp4"
).read_bytes() == init_payload
@@ -1140,7 +1149,7 @@ def test_shared_worker_stage_materializes_a_version_bound_camera_job(
materialized.camera_job_root
/ "input"
/ "camera"
/ "sensor.camera.right"
/ "recorded.camera.right"
/ "epoch-1"
/ "segments"
/ "1.m4s"
@@ -1273,14 +1282,25 @@ def test_profile_runner_sequences_exact_components_and_packages_sealed_job(
)
runtime_plan = PortableWorkerRuntimePlan(
job_id=sealed.job_id,
request_sha256=sealed.request_sha256,
identity_sha256=sealed.identity_sha256,
submission_receipt_sha256=sealed.submission_receipt_sha256,
claim_generation=sealed.claim_generation,
adapter_id="lab-v1-eomt-ddrnet-worker006-v1",
candidate_sha256="f" * 64,
setup_id=sealed.setup_id,
definition_id=sealed.definition_id,
definition_version=sealed.definition_version,
definition_sha256=sealed.definition_sha256,
source_session_id=sealed.source_session_id,
source_catalog_sha256=sealed.source_catalog_sha256,
source_bundle_sha256=sealed.source_bundle_sha256,
source_capability_manifest_sha256=(
sealed.source_capability_manifest_sha256
),
source_adapter_id=sealed.source_adapter_id,
source_adapter_version=sealed.source_adapter_version,
source_adapter_sha256=sealed.source_adapter_sha256,
result_contract_sha256=definition.result_contract.contract_sha256,
phases=PORTABLE_LAB_V1_RUNTIME_PHASES,
)
@@ -115,9 +115,8 @@ class _Launcher:
def _ready_definition() -> PortableRunDefinition:
base = PortableRunDefinitionRegistry.from_file(DEFINITIONS_PATH).resolve(
base = PortableRunDefinitionRegistry.from_file(DEFINITIONS_PATH).resolve_setup(
service_module.PORTABLE_LAB_V1_SETUP_ID,
"3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2",
)
executor = PortableExecutorAvailability(
contour_id=base.executor.contour_id,
@@ -52,7 +52,30 @@ INIT_SHA256 = "e2279963e16d84c91d68e7dbb1f7efed840533387dfeb844b7398bff45fbde38"
def _blocked_registry() -> PortableRunDefinitionRegistry:
return PortableRunDefinitionRegistry.from_file(REGISTRY_PATH)
production = PortableRunDefinitionRegistry.from_file(REGISTRY_PATH)
ready = production.resolve_setup("lab-v1-eomt-ddrnet-portable-v1")
executor = PortableExecutorAvailability(
contour_id=ready.executor.contour_id,
state="not-installed",
release_id=None,
release_sha256=None,
image_sha256=None,
reason_code="executor-not-installed",
reason="Executor release is not sealed or installed on Worker 006.",
)
identity = ready.identity_document()
identity["executor"] = executor.identity_document()
blocked = replace(
ready,
executor=executor,
definition_sha256=canonical_sha256(identity),
)
return PortableRunDefinitionRegistry(
(
blocked,
production.resolve_setup("m49-tgs-portable-v2"),
)
)
def _ready_registry() -> PortableRunDefinitionRegistry:
@@ -29,6 +29,7 @@ from k1link.observatory.portable_result_publisher import (
PortableObservatoryResultPublisher,
resolve_published_portable_calculation_profile,
)
from k1link.observatory.portable_result_view import PortableResultViewService
from k1link.observatory.portable_run_definitions import (
PortableRunDefinition,
PortableRunDefinitionRegistry,
@@ -372,7 +373,18 @@ def test_verified_package_publishes_immutable_binding_and_profile_provenance(
assert first.binding.session_id == RESULT_ID
assert first.binding.source_session_id == SOURCE_SESSION_ID
assert first.binding.config_sha256 == definition.definition_sha256
assert first.binding.replay_capability is None
assert first.binding.replay_capability is not None
assert first.binding.replay_capability.as_dict() == {
"schema_version": "missioncore.observation-lab-replay-capability/v2",
"kind": "portable-result-review",
"viewer_profile": "portable-result",
"timeline": "result-defined",
"activation": "explicit",
"commands_enabled": False,
}
assert first.binding.provenance["replay_capability"] == (
first.binding.replay_capability.as_dict()
)
assert first.binding.provenance["calculation_profile"] == {
"schema_version": OBSERVATORY_CALCULATION_PROFILE_SCHEMA,
"setup_id": definition.setup_id,
@@ -388,6 +400,15 @@ def test_verified_package_publishes_immutable_binding_and_profile_provenance(
assert store.get_lab_instance(RESULT_ID) == first.binding
assert store.get_session(SOURCE_SESSION_ID).summary.lab is None
view = PortableResultViewService(
sessions=store,
artifacts=CentralArtifactStore(tmp_path / "central-artifacts"),
).read(RESULT_ID)
assert view["result_id"] == RESULT_ID
assert view["source_session_id"] == SOURCE_SESSION_ID
assert view["definition_sha256"] == definition.definition_sha256
assert view["viewer_capability"] == first.binding.replay_capability.as_dict()
summary = store.get_session(RESULT_ID).summary
assert summary.display_name == (
"Portable result source · полный маршрут и воспроизведение"
@@ -19,7 +19,7 @@ from k1link.observatory.portable_run_definitions import (
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
REGISTRY_PATH = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
DEFINITION_SHA256 = "3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2"
DEFINITION_SHA256 = "269d71a24b4e63cff54e01273f9d4b35fc6cdd72bc6fadec206169ae0777e6ac"
MODEL_MANIFEST_SHA256 = "3fd2d43af73bd73f89d9ffae95d8770cfdeb46033ec967509124fac6ae4afe56"
M49_DEFINITION_SHA256 = "f56d6321bd794ccdfb7d2e3b05d044b11f616ffb81ee29517386cc253046d4eb"
M49_MODEL_MANIFEST_SHA256 = "489a43448f720a9b5c7993dc8279d167b77191a586f0d87b6d38b81cf728e2f1"
@@ -47,6 +47,26 @@ def _first(document: dict[str, object]) -> dict[str, object]:
return first
def _blocked_lab_definition():
ready = _registry().resolve_setup("lab-v1-eomt-ddrnet-portable-v1")
executor = PortableExecutorAvailability(
contour_id=ready.executor.contour_id,
state="not-installed",
release_id=None,
release_sha256=None,
image_sha256=None,
reason_code="executor-not-installed",
reason="Executor release is not sealed or installed on Worker 006.",
)
identity = ready.identity_document()
identity["executor"] = executor.identity_document()
return replace(
ready,
executor=executor,
definition_sha256=canonical_sha256(identity),
)
def test_production_definition_is_source_independent_and_requirements_are_immutable() -> None:
definition = _registry().definitions[0]
requirements = definition.source_requirements
@@ -279,32 +299,43 @@ def test_duplicate_definition_and_incomplete_ready_executor_are_rejected(
executor = first["executor"]
assert isinstance(executor, dict)
executor["state"] = "ready"
executor["release_id"] = None
executor["release_sha256"] = None
executor["image_sha256"] = None
executor["reason_code"] = None
executor["reason"] = None
with pytest.raises(PortableRunDefinitionRegistryError, match="release identity"):
PortableRunDefinitionRegistry.from_file(_write(tmp_path, incomplete))
def test_blocked_lab_definition_does_not_hide_ready_m49_definition() -> None:
def test_production_definitions_are_both_ready_and_convertible() -> None:
registry = _registry()
definition = registry.definitions[0]
assert definition.executor.state == "not-installed"
assert definition.executor.release_id is None
assert definition.executor.release_sha256 is None
assert definition.executor.image_sha256 is None
with pytest.raises(
PortableRunDefinitionUnavailableError,
match="not sealed or installed",
):
definition.to_recorded_run_definition()
assert definition.executor.state == "ready"
assert definition.executor.release_id == "lab-v1-installed-package-v1"
assert definition.executor.release_sha256 == (
"667858623962cd6d9849a8985b803f59e429916b5c56f76a6fc6c80c0c54526b"
)
assert definition.executor.image_sha256 == (
"5ad7d95baac63af13812cb693d492add4e806a333aba8e60edb2ea1aba754373"
)
assert definition.to_recorded_run_definition().setup_id == definition.setup_id
ready = registry.ready_recorded_definitions()
assert tuple(row.setup_id for row in ready) == ("m49-tgs-portable-v2",)
assert tuple(row.setup_id for row in ready) == (
"lab-v1-eomt-ddrnet-portable-v1",
"m49-tgs-portable-v2",
)
assert registry.to_recorded_registry().definitions == ready
def test_conversion_to_recorded_definition_requires_and_preserves_sealed_identities() -> None:
blocked = _registry().definitions[0]
blocked = _blocked_lab_definition()
with pytest.raises(
PortableRunDefinitionUnavailableError,
match="not sealed or installed",
):
blocked.to_recorded_run_definition()
ready_executor = PortableExecutorAvailability(
contour_id="worker-006",
state="ready",
@@ -339,30 +370,11 @@ def test_conversion_to_recorded_definition_requires_and_preserves_sealed_identit
def test_blocked_definition_does_not_hide_an_unrelated_ready_definition() -> None:
registry = _registry()
blocked_lab = registry.resolve_setup("lab-v1-eomt-ddrnet-portable-v1")
blocked_lab = _blocked_lab_definition()
ready_m49 = registry.resolve_setup("m49-tgs-portable-v2")
ready_executor = PortableExecutorAvailability(
contour_id="worker-006",
state="ready",
release_id="lab-v1-eomt-ddrnet-executor-v1",
release_sha256="1" * 64,
image_sha256="2" * 64,
reason_code=None,
reason=None,
)
identity = blocked_lab.identity_document()
identity["executor"] = ready_executor.identity_document()
ready_lab = replace(
blocked_lab,
executor=ready_executor,
definition_sha256=canonical_sha256(identity),
)
mixed = PortableRunDefinitionRegistry((ready_lab, ready_m49))
mixed = PortableRunDefinitionRegistry((blocked_lab, ready_m49))
expected = (
ready_lab.to_recorded_run_definition(),
ready_m49.to_recorded_run_definition(),
)
expected = (ready_m49.to_recorded_run_definition(),)
assert mixed.ready_recorded_definitions() == expected
assert mixed.to_recorded_registry().definitions == expected
assert mixed.resolve_setup("m49-tgs-portable-v2") is ready_m49
@@ -394,7 +406,7 @@ def test_production_lab_v1_model_component_and_result_identities_are_exact() ->
"ea583966bc3409f5cf563cbf4fad05e366907e67187082eb692aff53d9f5d875"
)
assert components["eomt-recorded-runner-v1"].sha256 == (
"651e8e06c3912dffb036b7fd08f2c0623f7563d8306cc7aee05db562798518f4"
"1e64869de48d10f1531c742e6067c4c3ae2a709c5eb0d770d1fab74b4a2431ff"
)
assert components["k1-camera-1-calibration-v1"].sha256 == (
"05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9"
+28 -2
View File
@@ -92,7 +92,7 @@ def test_portable_setup_catalog_exposes_capability_and_executor_separately() ->
setup = document["setups"][0]
assert setup["origin"] == "portable-definition"
assert setup["source_compatibility"]["outcome"] == "pass"
assert setup["executor"]["state"] == "not-installed"
assert setup["executor"]["state"] == "ready"
assert setup["existing_results"] == []
assert setup["preflight"]["outcome"] == "blocked"
assert setup["preflight"]["submission_allowed"] is False
@@ -153,6 +153,32 @@ def _ready_lab_registry() -> PortableRunDefinitionRegistry:
)
def _blocked_lab_registry() -> PortableRunDefinitionRegistry:
ready = PortableRunDefinitionRegistry.from_file(REGISTRY_PATH).resolve_setup(
"lab-v1-eomt-ddrnet-portable-v1"
)
executor = PortableExecutorAvailability(
contour_id="worker-006",
state="not-installed",
release_id=None,
release_sha256=None,
image_sha256=None,
reason_code="executor-not-installed",
reason="Immutable executor release не установлен.",
)
identity = ready.identity_document()
identity["executor"] = executor.identity_document()
return PortableRunDefinitionRegistry(
(
replace(
ready,
executor=executor,
definition_sha256=canonical_sha256(identity),
),
)
)
class _PortableBinding:
def __init__(
self,
@@ -278,7 +304,7 @@ def test_portable_api_check_sha_fences_ready_submission(tmp_path: Path) -> None:
def test_portable_api_rejects_blocked_lab_executor_before_binding_submit(
tmp_path: Path,
) -> None:
full_registry = PortableRunDefinitionRegistry.from_file(REGISTRY_PATH)
full_registry = _blocked_lab_registry()
ready_registry = _ready_lab_registry()
queue = ObservatoryRecordedJobQueue(
tmp_path,
@@ -79,15 +79,18 @@ def test_local_worker_gate_is_fail_closed_and_accepts_only_exact_one() -> None:
)
def test_validator_registry_fails_when_one_required_profile_is_absent() -> None:
def test_validator_registry_selects_only_contracts_present_in_definitions() -> None:
definitions = _definitions()
only_lab_v1 = PortableRunDefinitionRegistry((definitions.definitions[0],))
with pytest.raises(
PortableWorkerIntegrationError,
match="required portable setup is unavailable",
):
portable_result_validator_registry(only_lab_v1)
validators = portable_result_validator_registry(only_lab_v1)
assert validators.registrations == (
PortableResultContractValidatorRegistration(
PORTABLE_LAB_V1_RESULT_CONTRACT_SHA256,
validate_lab_v1_result_v2,
),
)
def test_server_integration_constructs_dormant_transport_and_publisher(
+145 -54
View File
@@ -13,6 +13,7 @@ from k1link.observatory.portable_run_definitions import (
canonical_sha256,
)
from k1link.observatory.portable_worker_runtime import (
PortableWorkerAssetRequirement,
PortableWorkerAssetVerification,
PortableWorkerExecutorAdapter,
PortableWorkerExecutorSeal,
@@ -25,6 +26,7 @@ from k1link.observatory.portable_worker_runtime import (
PortableWorkerRuntimeRegistryError,
PortableWorkerRuntimeUnavailableError,
PortableWorkerSourceStage,
_matches_tree,
inspect_runtime_candidate,
)
from k1link.observatory.worker_agent import (
@@ -49,6 +51,27 @@ def _runtime() -> PortableWorkerRuntimeRegistry:
)
def _blocked_candidate(candidate):
phases = tuple(
PortableWorkerRuntimePhase(phase.phase_id, "missing")
for phase in candidate.phases
)
blockers = ("executor-release-unsealed",)
identity = candidate.identity_document()
identity["state"] = "blocked"
identity["executor"] = None
identity["phases"] = [phase.as_dict() for phase in phases]
identity["blockers"] = list(blockers)
return replace(
candidate,
state="blocked",
executor=None,
phases=phases,
blockers=blockers,
candidate_sha256=canonical_sha256(identity),
)
def _all_keys(value: object) -> set[str]:
if isinstance(value, dict):
return set(value) | {nested for child in value.values() for nested in _all_keys(child)}
@@ -57,7 +80,7 @@ def _all_keys(value: object) -> set[str]:
return set()
def test_production_candidates_bind_exact_definitions_and_only_m49_is_ready() -> None:
def test_production_candidates_bind_exact_definitions_and_both_are_ready() -> None:
registry = _runtime()
assert {candidate.setup_id for candidate in registry.candidates} == {
@@ -66,15 +89,16 @@ def test_production_candidates_bind_exact_definitions_and_only_m49_is_ready() ->
}
by_setup = {candidate.setup_id: candidate for candidate in registry.candidates}
lab_v1 = by_setup["lab-v1-eomt-ddrnet-portable-v1"]
assert lab_v1.ready is False
assert lab_v1.executor is None
assert "executor-release-unsealed" in lab_v1.blockers
assert any(phase.state == "missing" for phase in lab_v1.phases)
with pytest.raises(
PortableWorkerRuntimeUnavailableError,
match="no executor identity",
):
lab_v1.executor_identity()
assert lab_v1.ready is True
assert lab_v1.executor is not None
assert lab_v1.blockers == ()
assert all(phase.state == "implemented" for phase in lab_v1.phases)
assert lab_v1.executor_identity().release_sha256 == (
"4788005a24ae3e1664aa7aaaf728fb816243bfde8555c80a608f40d1061925b3"
)
assert lab_v1.executor_identity().image_sha256 == (
"840175ccac731b16b7f2815eaebc6e4ef426c49b57daf1911064ff5b7945a8fd"
)
m49 = by_setup["m49-tgs-portable-v2"]
assert m49.ready is True
@@ -180,15 +204,15 @@ def test_m49_portable_runner_source_release_is_content_addressed() -> None:
assert hashlib.sha256(path.read_bytes()).hexdigest() == item["sha256"]
def test_lab_candidate_verifies_reusable_repository_assets_without_claiming_executor() -> None:
def test_ready_lab_candidate_still_requires_complete_local_asset_admission() -> None:
candidate = _runtime().resolve(
"lab-v1-eomt-ddrnet-portable-v1",
"3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2",
"11460f0fa03f713b4f64c8742bc30cc31eac66321931e1edd035553f5c72974a",
)
bindings = {
"ddrnet-goose-image": PortableWorkerLocalAssetBinding(
asset_id="ddrnet-goose-image",
image_sha256=("591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"),
"agent-image": PortableWorkerLocalAssetBinding(
asset_id="agent-image",
image_sha256=("840175ccac731b16b7f2815eaebc6e4ef426c49b57daf1911064ff5b7945a8fd"),
),
"ddrnet-goose-runner": PortableWorkerLocalAssetBinding(
asset_id="ddrnet-goose-runner",
@@ -201,39 +225,22 @@ def test_lab_candidate_verifies_reusable_repository_assets_without_claiming_exec
/ "run_goose_vegetation_benchmark.py"
),
),
"eomt-image": PortableWorkerLocalAssetBinding(
asset_id="eomt-image",
image_sha256=("58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"),
),
"eomt-orchestrator": PortableWorkerLocalAssetBinding(
asset_id="eomt-orchestrator",
"ddrnet-portable-config": PortableWorkerLocalAssetBinding(
asset_id="ddrnet-portable-config",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "config"
/ "perception"
/ "worker"
/ "Invoke-E4FullSessionSegmentation.ps1"
/ "lab-v1-eomt-ddrnet-portable-v2.json"
),
),
"eomt-profile": PortableWorkerLocalAssetBinding(
asset_id="eomt-profile",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "e3_k1_camera1_profile.json"
),
"ddrnet-step-image": PortableWorkerLocalAssetBinding(
asset_id="ddrnet-step-image",
image_sha256=("759eeac66145221b3c64f226b9ba9220363af36e4fda9353ef8169551587901c"),
),
"eomt-runner": PortableWorkerLocalAssetBinding(
asset_id="eomt-runner",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "run_e4_full_session_segmentation.py"
),
"eomt-step-image": PortableWorkerLocalAssetBinding(
asset_id="eomt-step-image",
image_sha256=("29a7e8caef51e1809f11b66498ef8bf3f9b2c5d3de40acdab2968cdd34b9623b"),
),
"vegetation-policy": PortableWorkerLocalAssetBinding(
asset_id="vegetation-policy",
@@ -258,18 +265,16 @@ def test_lab_candidate_verifies_reusable_repository_assets_without_claiming_exec
admission = inspect_runtime_candidate(candidate, bindings)
states = {item.asset_id: item.state for item in admission.assets}
assert states["ddrnet-goose-image"] == "matched"
assert states["agent-image"] == "matched"
assert states["ddrnet-goose-runner"] == "matched"
assert states["eomt-image"] == "matched"
assert states["eomt-orchestrator"] == "matched"
assert states["eomt-profile"] == "matched"
assert states["eomt-runner"] == "matched"
assert states["ddrnet-portable-config"] == "missing"
assert states["ddrnet-portable-config"] == "matched"
assert states["ddrnet-step-image"] == "matched"
assert states["eomt-step-image"] == "matched"
assert states["ddrnet-checkpoint"] == "missing"
assert states["eomt-model-weights"] == "missing"
assert states["eomt-model-cache"] == "missing"
assert admission.ready is False
assert "ddrnet-component-port-uninstalled" in admission.blockers
assert "worker-installation-receipt-unavailable" in admission.blockers
assert "asset-ddrnet-checkpoint-missing" in admission.blockers
assert "asset-eomt-model-cache-missing" in admission.blockers
def test_local_asset_tampering_is_reported_without_execution(tmp_path: Path) -> None:
@@ -305,6 +310,78 @@ def test_local_asset_tampering_is_reported_without_execution(tmp_path: Path) ->
assert "asset-travel-tgs-image-mismatched" in admission.blockers
def test_sealed_local_tree_uses_manifest_receipt_and_member_metadata(
tmp_path: Path,
) -> None:
tree = tmp_path / "model-tree"
tree.mkdir()
(tree / "model.bin").write_bytes(b"model")
(tree / "profile.json").write_bytes(b"{}\n")
manifest = (
b"model.bin\t5\t9372c470eeadd5ec5f36cb0b9adf10545c93c5132503830bf1465fe7654b117b\n"
b"profile.json\t3\tca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356\n"
)
identity_sha256 = hashlib.sha256(manifest).hexdigest()
(tree / "tree-manifest.tsv").write_bytes(manifest)
(tree / "tree-receipt.json").write_text(
json.dumps(
{
"schema_version": "missioncore.sealed-tree-runtime/v1",
"asset_id": "eomt-model-tree",
"identity_algorithm": "relative-path-tab-size-tab-file-sha256-lf/v1",
"identity_sha256": identity_sha256,
"file_count": 2,
"byte_length": 8,
"manifest_relative_path": "tree-manifest.tsv",
}
),
encoding="utf-8",
)
requirement = PortableWorkerAssetRequirement(
asset_id="eomt-model-tree",
kind="local-tree",
sha256=identity_sha256,
byte_length=8,
component_id=None,
model_release_id=None,
model_artifact_role=None,
)
assert _matches_tree(requirement, tree) is True
receipt = json.loads((tree / "tree-receipt.json").read_text(encoding="utf-8"))
receipt.update(
{
"source_image_sha256": "1" * 64,
"source_path": "/usr/lib/ffmpeg/7.0",
"binaries": {
"model-bin": {
"relative_path": "model.bin",
"byte_length": 5,
"sha256": (
"9372c470eeadd5ec5f36cb0b9adf10545c93c5132503830bf1465fe7654b117b"
),
}
},
}
)
(tree / "tree-receipt.json").write_text(json.dumps(receipt), encoding="utf-8")
assert _matches_tree(requirement, tree) is True
receipt["unexpected"] = "not-admitted"
(tree / "tree-receipt.json").write_text(json.dumps(receipt), encoding="utf-8")
assert _matches_tree(requirement, tree) is False
receipt.pop("unexpected")
(tree / "tree-receipt.json").write_text(json.dumps(receipt), encoding="utf-8")
(tree / "profile.json").write_bytes(b"drift")
assert _matches_tree(requirement, tree) is False
def test_runtime_registry_rejects_digest_drift_and_executable_instructions(
tmp_path: Path,
) -> None:
@@ -328,12 +405,23 @@ def test_runtime_registry_rejects_digest_drift_and_executable_instructions(
def test_runtime_plan_is_identity_only_and_observation_only() -> None:
plan = PortableWorkerRuntimePlan(
job_id="observatory-run-" + ("a" * 32),
request_sha256="6" * 64,
identity_sha256="7" * 64,
submission_receipt_sha256="8" * 64,
claim_generation=1,
adapter_id="m49-tgs-worker006-portable-v2",
candidate_sha256="1" * 64,
setup_id="m49-tgs-portable-v2",
definition_id="m49-tgs-portable",
definition_version=3,
definition_sha256="2" * 64,
source_session_id="source-a",
source_catalog_sha256="9" * 64,
source_bundle_sha256="3" * 64,
source_capability_manifest_sha256="4" * 64,
source_adapter_id="xgrids-k1-recorded-observatory-v2",
source_adapter_version=2,
source_adapter_sha256="a" * 64,
result_contract_sha256="5" * 64,
phases=("source-delivery", "portable-tgs-runner"),
).as_dict()
@@ -373,10 +461,10 @@ def test_ready_local_adapter_composes_only_local_ports_and_exact_job(
definition_sha256=canonical_sha256(definition_identity),
)
blocked = _runtime().resolve(
blocked = _blocked_candidate(_runtime().resolve(
"lab-v1-eomt-ddrnet-portable-v1",
base_definition.definition_sha256,
)
))
executor_seal = PortableWorkerExecutorSeal(
release_id="lab-v1-portable-executor-v1",
release_sha256="1" * 64,
@@ -512,7 +600,10 @@ def test_ready_local_adapter_composes_only_local_ports_and_exact_job(
result_sha256=result_sha256,
)
with pytest.raises(PortableWorkerRuntimeUnavailableError):
with pytest.raises(
PortableWorkerRuntimeRegistryError,
match="blocked local runtime cannot bind a ready RunDefinition",
):
PortableWorkerExecutorAdapter(
candidate=blocked,
definition=base_definition,
@@ -0,0 +1,109 @@
from __future__ import annotations
from datetime import UTC, datetime
from pathlib import Path
from types import SimpleNamespace
from typing import Any, cast
from k1link.observatory.portable_publication_reconciler import (
PortablePublicationReconciler,
)
NOW = datetime(2026, 9, 1, 12, 0, tzinfo=UTC)
class _Queue:
def __init__(self, jobs: tuple[SimpleNamespace, ...]) -> None:
self.jobs = jobs
self.published: list[str] = []
self.failed: list[str] = []
def pending_publications(self) -> tuple[SimpleNamespace, ...]:
return self.jobs
def mark_published(self, job_id: str) -> None:
self.published.append(job_id)
def mark_publication_failed(self, job_id: str, *, message: str) -> None:
assert message
self.failed.append(job_id)
class _Transport:
def __init__(self, root: Path) -> None:
self.root = root
self.calls: list[str] = []
def package_root_for_terminal(self, job: SimpleNamespace) -> Path:
self.calls.append(job.job_id)
return self.root
class _Publisher:
def __init__(self) -> None:
self.calls: list[str] = []
def publish(self, *, job: SimpleNamespace, package_root: Path) -> None:
assert package_root.is_dir()
self.calls.append(job.job_id)
def test_reconciler_publishes_pending_result_without_compute(tmp_path: Path) -> None:
package = tmp_path / "package"
package.mkdir()
job = _job("pending", attempts=0, updated_at="2026-09-01T11:59:00Z")
queue = _Queue((job,))
transport = _Transport(package)
publisher = _Publisher()
reconciler = PortablePublicationReconciler(
queue=cast(Any, queue),
artifact_transport=cast(Any, transport),
result_publisher=cast(Any, publisher),
clock=lambda: NOW,
)
result = reconciler.run_once()
assert result.published == 1
assert result.failed == 0
assert queue.published == [job.job_id]
assert transport.calls == [job.job_id]
assert publisher.calls == [job.job_id]
def test_reconciler_defers_backoff_and_exhausts_bounded_attempts(tmp_path: Path) -> None:
package = tmp_path / "package"
package.mkdir()
deferred = _job("failed", attempts=1, updated_at="2026-09-01T11:59:50Z")
exhausted = _job("failed", attempts=5, updated_at="2026-09-01T10:00:00Z")
queue = _Queue((deferred, exhausted))
transport = _Transport(package)
publisher = _Publisher()
reconciler = PortablePublicationReconciler(
queue=cast(Any, queue),
artifact_transport=cast(Any, transport),
result_publisher=cast(Any, publisher),
clock=lambda: NOW,
)
result = reconciler.run_once()
assert result.deferred == 1
assert result.exhausted == 1
assert result.published == 0
assert transport.calls == []
assert publisher.calls == []
def _job(
publication_state: str,
*,
attempts: int,
updated_at: str,
) -> SimpleNamespace:
return SimpleNamespace(
job_id=f"observatory-run-{attempts:032x}",
publication_state=publication_state,
publication_attempts=attempts,
updated_at_utc=updated_at,
)
@@ -0,0 +1,87 @@
from __future__ import annotations
from pathlib import Path
from typing import Any, cast
from fastapi import FastAPI
from fastapi.testclient import TestClient
from k1link.sessions.store import SessionStore
from k1link.web.observatory_api import build_observatory_router
JOB_ID = "observatory-run-0123456789abcdef0123456789abcdef"
class _Job:
state = "succeeded"
publication_state = "failed"
def as_dict(self) -> dict[str, object]:
return {
"job_id": JOB_ID,
"state": self.state,
"publication": {"state": self.publication_state},
}
class _Queue:
def __init__(self) -> None:
self.job = _Job()
self.published: list[str] = []
def get(self, job_id: str) -> _Job:
assert job_id == JOB_ID
return self.job
def mark_published(self, job_id: str) -> _Job:
assert job_id == JOB_ID
self.published.append(job_id)
self.job.publication_state = "published"
return self.job
class _Transport:
def __init__(self, package_root: Path) -> None:
self.package_root = package_root
self.calls: list[str] = []
def package_root_for_terminal(self, job: _Job) -> Path:
assert job.state == "succeeded"
self.calls.append(JOB_ID)
return self.package_root
class _Publisher:
def __init__(self) -> None:
self.calls: list[tuple[_Job, Path]] = []
def publish(self, *, job: _Job, package_root: Path) -> None:
self.calls.append((job, package_root))
def test_operator_retry_republishes_without_creating_compute_work(tmp_path: Path) -> None:
queue = _Queue()
package_root = tmp_path / "sealed-result"
package_root.mkdir()
transport = _Transport(package_root)
publisher = _Publisher()
app = FastAPI()
app.include_router(
build_observatory_router(
SessionStore(tmp_path / "repository", data_dir=tmp_path / "data"),
recorded_job_queue=cast(Any, queue),
recorded_binding_service=cast(Any, object()),
portable_artifact_transport=cast(Any, transport),
portable_result_publisher=cast(Any, publisher),
)
)
response = TestClient(app).post(
f"/api/v1/observatory/runs/{JOB_ID}/publication/retry"
)
assert response.status_code == 200
assert response.json()["publication"]["state"] == "published"
assert transport.calls == [JOB_ID]
assert publisher.calls == [(queue.job, package_root)]
assert queue.published == [JOB_ID]
+124
View File
@@ -21,6 +21,7 @@ from k1link.observatory.recorded_jobs import (
ObservatoryRecordedQueueStaleClaimError,
ObservatoryRecordedReconciliationRequest,
ObservatoryRecordedResourceReleaseAttestation,
RecordedExecutorIdentity,
RecordedRunDefinition,
RecordedRunDefinitionRegistry,
)
@@ -392,6 +393,129 @@ def test_claim_is_exactly_idempotent_including_empty_result(tmp_path: Path) -> N
)
def test_capability_aware_claim_skips_incompatible_queued_job(tmp_path: Path) -> None:
first, second = _definitions().definitions
compatible = replace(
second,
setup_id="compatible-portable-v1",
definition_id="compatible-portable",
definition_sha256="8" * 64,
executor_release_sha256="9" * 64,
executor_image_sha256="a" * 64,
model_manifest_sha256="b" * 64,
resource_profile_sha256="c" * 64,
)
queue = ObservatoryRecordedJobQueue(
tmp_path,
definitions=RecordedRunDefinitionRegistry((first, compatible)),
clock=lambda: NOW,
)
incompatible_job, _ = queue.submit(_intent(idempotency_key="capability:first"), enqueue=True)
compatible_job, _ = queue.submit(
_intent(
idempotency_key="capability:second",
setup_id=compatible.setup_id,
definition_sha256=compatible.definition_sha256,
),
enqueue=True,
)
capability = RecordedExecutorIdentity(
release_sha256=compatible.executor_release_sha256,
image_sha256=compatible.executor_image_sha256,
model_manifest_sha256=compatible.model_manifest_sha256,
resource_profile_sha256=compatible.resource_profile_sha256,
)
claim = queue.claim_next(
claimant_id="recorded-worker",
claim_request_id="capability-aware-poll",
supported_executor_identities=(capability,),
)
assert claim is not None
assert claim.job.job_id == compatible_job.job_id
assert queue.get(incompatible_job.job_id).state == "queued"
def test_capability_claim_identity_binds_snapshot_and_empty_snapshot_claims_nothing(
tmp_path: Path,
) -> None:
queue = _queue(tmp_path)
job, _ = queue.submit(_intent(), enqueue=True)
assert queue.claim_next(
claimant_id="recorded-worker",
claim_request_id="capability-empty-poll",
supported_executor_identities=(),
) is None
capability = RecordedExecutorIdentity(
release_sha256=EXECUTOR_RELEASE_SHA,
image_sha256=EXECUTOR_IMAGE_SHA,
model_manifest_sha256=MODEL_MANIFEST_SHA,
resource_profile_sha256=RESOURCE_PROFILE_SHA,
)
with pytest.raises(ObservatoryRecordedQueueConflictError, match="claim"):
queue.claim_next(
claimant_id="recorded-worker",
claim_request_id="capability-empty-poll",
supported_executor_identities=(capability,),
)
legacy_claim = queue.claim_next(
claimant_id="recorded-worker",
claim_request_id="legacy-poll-after-empty-capability",
)
assert legacy_claim is not None
assert legacy_claim.job.job_id == job.job_id
def test_execution_success_uses_durable_idempotent_publication_outbox(
tmp_path: Path,
) -> None:
queue = _queue(tmp_path)
job, _ = queue.submit(_intent(), enqueue=True)
claim = queue.claim_next(
claimant_id="recorded-worker",
claim_request_id="publication-outbox-claim",
)
assert claim is not None
queue.start(job.job_id, claim_token=claim.claim_token)
completed = queue.complete_for_publication(
job.job_id,
claim_token=claim.claim_token,
result_id="portable-result-001",
result_sha256=RESULT_SHA,
)
assert completed.state == "succeeded"
assert completed.publication_state == "pending"
assert completed.publication_attempts == 0
assert queue.pending_publications() == (completed,)
failed = queue.mark_publication_failed(
job.job_id,
message="catalog temporarily unavailable",
)
assert failed.publication_state == "failed"
assert failed.publication_attempts == 1
assert failed.publication_error == "catalog temporarily unavailable"
replay = queue.complete_for_publication(
job.job_id,
claim_token=claim.claim_token,
result_id="portable-result-001",
result_sha256=RESULT_SHA,
)
assert replay == failed
published = queue.mark_published(job.job_id)
assert published.publication_state == "published"
assert published.publication_attempts == 2
assert published.publication_error is None
assert published.published_at_utc == NOW
assert queue.mark_published(job.job_id) == published
assert queue.pending_publications() == ()
def test_claim_lease_renews_idempotently_and_requeues_expired_unstarted_job(
tmp_path: Path,
) -> None:
+9 -11
View File
@@ -114,10 +114,12 @@ class FakeTransport:
*,
claimant_id: str,
claim_request_id: str,
supported_executor_identities: tuple[ObservatoryWorkerExecutorIdentity, ...],
) -> Mapping[str, object] | None:
claim = self.queue.claim_next(
claimant_id=claimant_id,
claim_request_id=claim_request_id,
supported_executor_identities=supported_executor_identities,
)
if claim is None:
return None
@@ -397,7 +399,7 @@ def test_worker_agent_leaves_empty_queue_untouched(tmp_path: Path) -> None:
assert executor.jobs == []
def test_exact_release_mismatch_fails_closed_without_start(tmp_path: Path) -> None:
def test_exact_release_mismatch_is_not_claimed_or_failed(tmp_path: Path) -> None:
queue = _queue(tmp_path)
job_id = _enqueue(queue)
transport = FakeTransport(queue)
@@ -409,19 +411,13 @@ def test_exact_release_mismatch_fails_closed_without_start(tmp_path: Path) -> No
identity=_identity(release_sha256="b" * 64),
).run_once()
assert report.state == "failed"
assert report.failure_code == "executor-not-allowlisted"
assert report.state == "empty"
assert report.failure_code is None
assert transport.starts == []
assert transport.successes == []
assert transport.failures == [
(
job_id,
"executor-not-allowlisted",
"Exact executor identity is not installed on Worker 006.",
)
]
assert transport.failures == []
assert executor.jobs == []
assert queue.get(job_id).state == "failed"
assert queue.get(job_id).state == "queued"
def _spoof_claimant(payload: dict[str, object]) -> dict[str, object]:
@@ -525,9 +521,11 @@ class BlockingEmptyTransport:
*,
claimant_id: str,
claim_request_id: str,
supported_executor_identities: tuple[ObservatoryWorkerExecutorIdentity, ...],
) -> Mapping[str, object] | None:
assert claimant_id == WORKER_006_CONTOUR_ID
assert claim_request_id == "worker-006:overlap-test"
assert supported_executor_identities == ()
self.entered.set()
assert self.release.wait(timeout=2)
return None
+139 -21
View File
@@ -11,6 +11,7 @@ from fastapi.testclient import TestClient
from k1link.observatory.portable_artifact_transport import (
PortableArtifactTransportUnavailableError,
)
from k1link.observatory.portable_result_contract import PortableResultPublisherError
from k1link.observatory.recorded_jobs import (
ObservatoryRecordedJobIntent,
ObservatoryRecordedJobQueue,
@@ -32,7 +33,7 @@ WORKER_HEADERS = {
"Authorization": f"Bearer {WORKER_TOKEN}",
OBSERVATORY_WORKER_CONTOUR_HEADER: "worker-006",
}
CLAIM_SCHEMA = "missioncore.observatory-worker-claim-request/v1"
CLAIM_SCHEMA = "missioncore.observatory-worker-claim-request/v2"
START_SCHEMA = "missioncore.observatory-worker-start-request/v1"
RENEW_SCHEMA = "missioncore.observatory-worker-renew-request/v1"
CHECKPOINT_SCHEMA = "missioncore.observatory-worker-checkpoint-request/v1"
@@ -61,6 +62,22 @@ def _definition() -> RecordedRunDefinition:
)
def _claim_request(claim_request_id: str) -> dict[str, object]:
definition = _definition()
return {
"schema_version": CLAIM_SCHEMA,
"claim_request_id": claim_request_id,
"supported_executor_identities": [
{
"release_sha256": definition.executor_release_sha256,
"image_sha256": definition.executor_image_sha256,
"model_manifest_sha256": definition.model_manifest_sha256,
"resource_profile_sha256": definition.resource_profile_sha256,
}
],
}
def _services(tmp_path: Path) -> tuple[TestClient, ObservatoryRecordedJobQueue]:
definition = _definition()
queue = ObservatoryRecordedJobQueue(
@@ -85,6 +102,28 @@ class _ArtifactTransportWithoutCompletedPackage:
raise PortableArtifactTransportUnavailableError("package is incomplete")
class _CompletedArtifactTransport:
def __init__(self, package_root: Path) -> None:
self.package_root = package_root
def require_completed_for_success(self, **_values: object) -> Path:
return self.package_root
def package_root_for_terminal(self, _job: object) -> Path:
return self.package_root
class _TransientResultPublisher:
def __init__(self) -> None:
self.attempts = 0
def publish(self, **_values: object) -> object:
self.attempts += 1
if self.attempts == 1:
raise PortableResultPublisherError("catalog temporarily unavailable")
return object()
def _services_with_artifact_transport(
tmp_path: Path,
) -> tuple[TestClient, ObservatoryRecordedJobQueue]:
@@ -107,6 +146,30 @@ def _services_with_artifact_transport(
return TestClient(app), queue
def _services_with_transient_publisher(
tmp_path: Path,
) -> tuple[TestClient, ObservatoryRecordedJobQueue, _TransientResultPublisher]:
definition = _definition()
queue = ObservatoryRecordedJobQueue(
tmp_path,
definitions=RecordedRunDefinitionRegistry((definition,)),
)
publisher = _TransientResultPublisher()
app = FastAPI()
app.include_router(
build_observatory_worker_router(
queue,
authentication=ObservatoryWorkerAuthentication(
bearer_token_sha256=WORKER_TOKEN_SHA256,
contour_id="worker-006",
),
artifact_transport=_CompletedArtifactTransport(tmp_path / "package"), # type: ignore[arg-type]
result_publisher=publisher, # type: ignore[arg-type]
)
)
return TestClient(app), queue, publisher
def _enqueue(
queue: ObservatoryRecordedJobQueue,
*,
@@ -137,10 +200,7 @@ def _claim(
response = client.post(
"/api/v1/worker/observatory/recorded-jobs/claims",
headers=WORKER_HEADERS,
json={
"schema_version": CLAIM_SCHEMA,
"claim_request_id": claim_request_id,
},
json=_claim_request(claim_request_id),
)
assert response.status_code == 200
return cast(dict[str, Any], response.json())
@@ -151,10 +211,7 @@ def test_worker_authentication_requires_digest_and_configured_contour(
) -> None:
client, queue = _services(tmp_path)
_enqueue(queue)
request = {
"schema_version": CLAIM_SCHEMA,
"claim_request_id": "worker-006:auth-claim",
}
request = _claim_request("worker-006:auth-claim")
missing = client.post(
"/api/v1/worker/observatory/recorded-jobs/claims",
@@ -198,10 +255,7 @@ def test_claim_is_idempotent_and_request_schema_rejects_execution_inputs(
) -> None:
client, queue = _services(tmp_path)
job_id = _enqueue(queue)
request = {
"schema_version": CLAIM_SCHEMA,
"claim_request_id": "worker-006:stable-claim",
}
request = _claim_request("worker-006:stable-claim")
first = client.post(
"/api/v1/worker/observatory/recorded-jobs/claims",
@@ -235,14 +289,30 @@ def test_claim_is_idempotent_and_request_schema_rejects_execution_inputs(
assert queue.get(job_id).state == "claimed"
def test_legacy_claim_without_capability_snapshot_cannot_take_another_profile(
tmp_path: Path,
) -> None:
client, queue = _services(tmp_path)
job_id = _enqueue(queue)
response = client.post(
"/api/v1/worker/observatory/recorded-jobs/claims",
headers=WORKER_HEADERS,
json={
"schema_version": "missioncore.observatory-worker-claim-request/v1",
"claim_request_id": "worker-006:legacy-unbounded-claim",
},
)
assert response.status_code == 422
assert queue.get(job_id).state == "queued"
def test_empty_claim_is_204_and_empty_receipt_remains_idempotent(
tmp_path: Path,
) -> None:
client, queue = _services(tmp_path)
request = {
"schema_version": CLAIM_SCHEMA,
"claim_request_id": "worker-006:empty-poll",
}
request = _claim_request("worker-006:empty-poll")
first = client.post(
"/api/v1/worker/observatory/recorded-jobs/claims",
@@ -258,10 +328,7 @@ def test_empty_claim_is_204_and_empty_receipt_remains_idempotent(
fresh = client.post(
"/api/v1/worker/observatory/recorded-jobs/claims",
headers=WORKER_HEADERS,
json={
"schema_version": CLAIM_SCHEMA,
"claim_request_id": "worker-006:fresh-poll",
},
json=_claim_request("worker-006:fresh-poll"),
)
assert first.status_code == 204
@@ -456,6 +523,57 @@ def test_artifact_transport_blocks_success_without_completed_package(
assert queue.get(job_id).state == "running"
def test_publication_failure_is_durable_and_retry_does_not_rerun_execution(
tmp_path: Path,
) -> None:
client, queue, publisher = _services_with_transient_publisher(tmp_path)
job_id = _enqueue(queue)
claim_token = _claim(client)["claim_token"]
client.post(
f"/api/v1/worker/observatory/recorded-jobs/{job_id}/start",
headers=WORKER_HEADERS,
json={"schema_version": START_SCHEMA, "claim_token": claim_token},
)
sealed = client.post(
f"/api/v1/worker/observatory/recorded-jobs/{job_id}/succeed",
headers=WORKER_HEADERS,
json={
"schema_version": SUCCEED_SCHEMA,
"claim_token": claim_token,
"result_id": "portable-result-publication-retry",
"result_sha256": "b" * 64,
},
)
assert sealed.status_code == 202
assert sealed.json()["state"] == "succeeded"
assert sealed.json()["publication"] == {
"state": "failed",
"attempts": 1,
"error": "catalog temporarily unavailable",
"published_at_utc": None,
}
assert queue.get(job_id).claim_generation == 1
retried = client.post(
f"/api/v1/worker/observatory/recorded-jobs/{job_id}/publication/retry",
headers=WORKER_HEADERS,
)
repeated = client.post(
f"/api/v1/worker/observatory/recorded-jobs/{job_id}/publication/retry",
headers=WORKER_HEADERS,
)
assert retried.status_code == 200
assert retried.json()["publication"]["state"] == "published"
assert retried.json()["publication"]["attempts"] == 2
assert repeated.status_code == 200
assert repeated.json()["publication"]["attempts"] == 2
assert queue.get(job_id).claim_generation == 1
assert publisher.attempts == 2
def test_worker_can_fail_claimed_job_idempotently(tmp_path: Path) -> None:
client, queue = _services(tmp_path)
job_id = _enqueue(queue)
@@ -99,6 +99,7 @@ def _cache_claim(gateway: ObservatoryWorkerHttpGateway) -> None:
payload = gateway.claim_next(
claimant_id=WORKER_006_CONTOUR_ID,
claim_request_id="worker-006:http-transport-test",
supported_executor_identities=(),
)
assert payload is not None