feat(observatory): install local M49 worker path

This commit is contained in:
DCCONSTRUCTIONS
2026-08-31 16:51:10 +03:00
parent 2f6e45bc96
commit 9c69d81296
19 changed files with 2252 additions and 212 deletions
+66
View File
@@ -65,6 +65,72 @@ def test_launch_agent_plan_disables_sync_and_enables_watchdog(tmp_path: Path) ->
assert "MISSIONCORE_DATA_DIR" not in desired["EnvironmentVariables"]
assert plan.to_dict()["changes"]["repository_migration"] is False
assert plan.to_dict()["changes"]["data_directory_preserved"] is False
assert plan.to_dict()["changes"]["local_observatory_worker_enabled"] is False
def test_launch_agent_plan_explicitly_enables_local_observatory_worker(
tmp_path: Path,
) -> None:
repository = tmp_path / "repo"
repository.mkdir()
data_directory = repository / ".runtime" / "mission-core"
data_directory.mkdir(parents=True, mode=0o700)
for name in (
"observatory-artifact-store",
"observatory-worker-source-cas",
"observatory-worker-result-staging",
):
(data_directory / name).mkdir(mode=0o700)
uv_entrypoint = tmp_path / "uv"
uv_entrypoint.write_text("#!/bin/sh\n")
uv_entrypoint.chmod(0o700)
agent = tmp_path / "agent.plist"
_write_agent(path=agent, repository=repository, uv_entrypoint=uv_entrypoint)
plan = plan_mission_core_launch_agent(
repository_root=repository,
agent_path=agent,
enable_local_observatory_worker=True,
)
desired = plistlib.loads(plan.desired_payload)
environment = desired["EnvironmentVariables"]
assert environment["MISSIONCORE_DATA_DIR"] == str(data_directory)
assert environment["MISSIONCORE_OBSERVATORY_WORKER_LOCAL_ENABLED"] == "1"
assert environment["MISSIONCORE_ARTIFACT_STORE_ROOT"] == str(
data_directory / "observatory-artifact-store"
)
assert environment["MISSIONCORE_OBSERVATORY_WORKER_SOURCE_CAS_ROOT"] == str(
data_directory / "observatory-worker-source-cas"
)
assert environment["MISSIONCORE_OBSERVATORY_WORKER_RESULT_STAGING_ROOT"] == str(
data_directory / "observatory-worker-result-staging"
)
assert plan.local_observatory_worker_enabled is True
assert plan.to_dict()["changes"]["local_observatory_worker_enabled"] is True
def test_launch_agent_plan_rejects_missing_local_observatory_roots(
tmp_path: Path,
) -> None:
repository = tmp_path / "repo"
repository.mkdir()
(repository / ".runtime" / "mission-core").mkdir(parents=True, mode=0o700)
uv_entrypoint = tmp_path / "uv"
uv_entrypoint.write_text("#!/bin/sh\n")
uv_entrypoint.chmod(0o700)
agent = tmp_path / "agent.plist"
_write_agent(path=agent, repository=repository, uv_entrypoint=uv_entrypoint)
with pytest.raises(
MissionCoreLaunchAgentError,
match="local Observatory artifact store is unavailable",
):
plan_mission_core_launch_agent(
repository_root=repository,
agent_path=agent,
enable_local_observatory_worker=True,
)
def test_launch_agent_plan_rejects_cross_repository_migration_by_default(
@@ -0,0 +1,160 @@
from __future__ import annotations
import hashlib
import json
import re
from pathlib import Path
from typing import cast
from k1link.observatory.m49_portable_executor import (
M49_COMPILED_RUNNER_BUILD_SCHEMA,
M49_PORTABLE_RUNTIME_PHASES,
)
from k1link.observatory.portable_result_contract import canonical_json
from k1link.observatory.portable_run_definitions import PortableRunDefinitionRegistry
from k1link.observatory.portable_worker_runtime import PortableWorkerRuntimeRegistry
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
PROMOTION_SCRIPT = (
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "observatory_portable"
/ "Invoke-M49PortableExecutorPromotion.ps1"
)
DEFINITION_REGISTRY = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
RUNTIME_REGISTRY = REPOSITORY_ROOT / "config" / "observatory-worker-runtime-candidates.json"
DEFINITION_SHA256 = "f56d6321bd794ccdfb7d2e3b05d044b11f616ffb81ee29517386cc253046d4eb"
CANDIDATE_SHA256 = "65cd2063146a1dd320e30d5f4e21e4bf0aab1ff683e846926cbbfbe25a9f8a5e"
RUNNER_SHA256 = "7be449392ef161fb8713b4c984705d2373bc3cd05645332d92b88a8bff0c7db3"
BUILD_SEAL_SHA256 = "e3bb2e91c70712eff74e8e69718075a407e042fb494616d65984500da42b21a9"
RELEASE_SHA256 = "c5b0670d943fe0452ef4bbfbc144ab2439a1a674f9ef164798ad9f8b1ecc29fa"
RECEIPT_SHA256 = "b560ff9e02746cbb760502f2a3b4b7bd564f95ab52d1149d189927a326b1645a"
EXECUTOR_IMAGE_SHA256 = "f9278ab21aa65045be993dd19bffc25f49955e19598893ac78cc4761ca63ecf3"
def _script() -> str:
return PROMOTION_SCRIPT.read_text(encoding="utf-8")
def _payload(name: str) -> bytes:
matched = re.search(
rf"\${name}Payload = @'\n(.*?)\n'@\.Trim\(\)",
_script(),
re.DOTALL,
)
assert matched is not None
return matched.group(1).encode("utf-8")
def test_promotion_payloads_are_canonical_and_exact() -> None:
expected = {
"RuntimeBuildSeal": (1014, BUILD_SEAL_SHA256),
"InstalledRelease": (1693, RELEASE_SHA256),
"ReadyReceipt": (2672, RECEIPT_SHA256),
}
for name, (byte_length, sha256) in expected.items():
payload = _payload(name)
document = json.loads(payload)
assert payload == canonical_json(document)
assert len(payload) == byte_length
assert hashlib.sha256(payload).hexdigest() == sha256
build_seal = cast(dict[str, object], json.loads(_payload("RuntimeBuildSeal")))
assert build_seal["schema_version"] == M49_COMPILED_RUNNER_BUILD_SCHEMA
assert cast(dict[str, object], build_seal["binary"]) == {
"byte_length": 274168,
"file_name": "run_m49_tgs_portable",
"format": "elf",
"sha256": RUNNER_SHA256,
}
def test_ready_receipt_closes_the_candidate_without_self_hashing() -> None:
release = cast(dict[str, object], json.loads(_payload("InstalledRelease")))
receipt = cast(dict[str, object], json.loads(_payload("ReadyReceipt")))
assert release["state"] == "ready"
assert release["blockers"] == []
assert cast(dict[str, object], release["executor_image"])["sha256"] == (EXECUTOR_IMAGE_SHA256)
assert receipt["receipt_state"] == "installed-ready"
assert receipt["release_sha256"] == RELEASE_SHA256
assert receipt["runtime_release_root"] == "/release"
assert receipt["blockers"] == []
files = cast(list[dict[str, object]], receipt["files"])
asset_ids = [cast(str, row["asset_id"]) for row in files]
assert asset_ids == sorted(asset_ids)
assert {row["asset_id"] for row in files} == {
"m49-portable-compiled-runner",
"m49-portable-compiled-runner-build-seal",
"m49-portable-executor-release",
"m49-portable-profile",
}
assert not any(row["asset_id"] == "m49-portable-worker-installation-receipt" for row in files)
def test_promotion_is_one_exact_additive_transition() -> None:
script = _script()
lowered = script.lower()
assert "param()" in script
assert '$ExpectedComputer = "DESKTOP-OPJ8J04"' in script
assert (
'$CandidateId = "beef090b79ab0e019cff976033d0446c45d4746ee921e02e8076a5a67f21817e"'
in script
)
assert '$RuntimeReleaseRoot = "/release"' in script
assert "Assert-ProtectedRuntime $protectedBefore" in script
assert "Assert-ProtectedRuntime $protectedAfter" in script
assert "Assert-LegacyTaskReady" in script
assert "requires reconciliation" in script
assert "collided during promotion" in script
assert "Move-Item -LiteralPath $stagingRoot -Destination $readyRoot" in script
for forbidden in (
"docker build",
"docker create",
"docker run",
"docker tag",
"docker restart",
"docker stop",
"invoke-webrequest",
"invoke-restmethod",
"start-process",
"invoke-expression",
"robocopy",
"scp ",
"ssh ",
):
assert forbidden not in lowered
def test_promoted_v3_definition_and_runtime_registry_bind_exactly() -> None:
definitions = PortableRunDefinitionRegistry.from_file(DEFINITION_REGISTRY)
definition = definitions.resolve_setup("m49-tgs-portable-v2")
assert definition.version == 3
assert definition.definition_sha256 == DEFINITION_SHA256
assert definition.executor.ready is True
assert definition.executor.release_sha256 == RELEASE_SHA256
assert definition.executor.image_sha256 == EXECUTOR_IMAGE_SHA256
assert definition.components[-1].component_id == "m49-tgs-portable-runner-v1"
assert definition.components[-1].sha256 == BUILD_SEAL_SHA256
runtime = PortableWorkerRuntimeRegistry.from_file(
RUNTIME_REGISTRY,
definitions=definitions,
)
candidate = runtime.resolve("m49-tgs-portable-v2", DEFINITION_SHA256)
assert candidate.ready is True
assert candidate.candidate_sha256 == CANDIDATE_SHA256
assert candidate.executor is not None
assert candidate.executor.release_sha256 == RELEASE_SHA256
assert candidate.executor.image_sha256 == EXECUTOR_IMAGE_SHA256
assert tuple(phase.phase_id for phase in candidate.phases) == M49_PORTABLE_RUNTIME_PHASES
assert all(phase.state == "implemented" for phase in candidate.phases)
assets = {asset.asset_id: asset for asset in candidate.reusable_assets}
assert assets["m49-portable-compiled-runner"].sha256 == RUNNER_SHA256
assert assets["m49-portable-compiled-runner-build-seal"].sha256 == (BUILD_SEAL_SHA256)
assert assets["m49-portable-executor-release"].sha256 == RELEASE_SHA256
assert assets["m49-portable-worker-installation-receipt"].sha256 == (RECEIPT_SHA256)
+6 -1
View File
@@ -296,6 +296,11 @@ def _ready_m49_registry(tmp_path: Path) -> PortableRunDefinitionRegistry:
"sha256": _sha256(RUNNER_PATH),
}
components = cast(list[object], selected["components"])
components[:] = [
component
for component in components
if cast(dict[str, object], component)["component_id"] != "m49-tgs-portable-runner-v1"
]
components.append(runner)
components.sort(key=lambda value: cast(str, cast(dict[str, object], value)["component_id"]))
executor = {
@@ -818,7 +823,7 @@ def test_executor_release_candidate_is_deterministic_blocked_and_tamper_evident(
assert "FROM ndc/mission-core-m49-t3-travel:20260826" in dockerfile
assert builder.M49_TRAVEL_IMAGE_SHA256 in dockerfile
assert "--network" not in dockerfile
assert "com.nodedc.authority=\"observation-only\"" in dockerfile
assert 'com.nodedc.authority="observation-only"' in dockerfile
installer = INSTALLER_PATH.read_text(encoding="utf-8")
assert "--pull=false --no-cache --network none" in installer
assert "--network none --read-only" in installer
@@ -0,0 +1,449 @@
from __future__ import annotations
import copy
import hashlib
import json
from pathlib import Path
from threading import Event
from typing import cast
import httpx
import pytest
import k1link.observatory.m49_worker_service as service_module
from k1link.observatory.m49_portable_executor import (
M49_COMPILED_RUNNER_BUILD_SCHEMA,
M49_PORTABLE_COMPILED_RUNNER_ASSET_ID,
M49_PORTABLE_COMPILED_RUNNER_BUILD_SEAL_ASSET_ID,
M49_PORTABLE_COMPILER_CONTRACT,
M49_PORTABLE_PROFILE_ASSET_ID,
M49_PORTABLE_RUNNER_SOURCE_SHA256,
M49_PORTABLE_RUNNER_WRAPPER_SHA256,
M49_PORTABLE_RUNTIME_PHASES,
M49_PORTABLE_TRAVEL_BUILD_IMAGE_SHA256,
M49_PORTABLE_TRAVEL_IMAGE_ASSET_ID,
M49PortableSourceMaterializerAdapter,
)
from k1link.observatory.portable_result_contract import (
OBSERVATION_ONLY_AUTHORITY,
canonical_json,
)
from k1link.observatory.portable_run_definitions import (
PortableRunDefinitionRegistry,
canonical_sha256,
)
from k1link.observatory.portable_worker_runtime import PortableWorkerExecutorAdapter
from k1link.observatory.worker_http_transport import ObservatoryWorkerHttpGateway
from k1link.observatory.worker_service import ObservatoryWorkerServiceConfiguration
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
DEFINITIONS_PATH = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
PROFILE_PATH = REPOSITORY_ROOT / "config" / "perception" / "m49-tgs-portable-v2.json"
SOURCE_REVISION = "f" * 40
SOURCE_CANDIDATE_SHA256 = "a" * 64
EXECUTOR_IMAGE_SHA256 = "b" * 64
def _sha256(payload: bytes) -> str:
return hashlib.sha256(payload).hexdigest()
def _write(path: Path, payload: bytes, *, executable: bool = False) -> Path:
path.write_bytes(payload)
if executable:
path.chmod(0o755)
return path
def _release_files(release_root: Path) -> dict[str, tuple[Path, bytes]]:
binary_payload = b"\x7fELFfixed-m49-worker-test"
binary = _write(
release_root / "run_m49_tgs_portable",
binary_payload,
executable=True,
)
profile_payload = PROFILE_PATH.read_bytes()
profile = _write(release_root / "m49-tgs-portable-v2.json", profile_payload)
build_seal_payload = canonical_json(
{
"schema_version": M49_COMPILED_RUNNER_BUILD_SCHEMA,
"source_revision": SOURCE_REVISION,
"source_state": "committed-snapshot",
"build_image_sha256": M49_PORTABLE_TRAVEL_BUILD_IMAGE_SHA256,
"profile_sha256": _sha256(profile_payload),
"runner_source_sha256": M49_PORTABLE_RUNNER_SOURCE_SHA256,
"runner_wrapper_sha256": M49_PORTABLE_RUNNER_WRAPPER_SHA256,
"compiler_contract": dict(M49_PORTABLE_COMPILER_CONTRACT),
"binary": {
"file_name": "run_m49_tgs_portable",
"format": "elf",
"byte_length": len(binary_payload),
"sha256": _sha256(binary_payload),
},
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
}
)
build_seal = _write(release_root / "compiled-runner-build.json", build_seal_payload)
release_payload = canonical_json(
{
"schema_version": "missioncore.m49-tgs-portable-executor-installed/v1",
"release_id": service_module.M49_WORKER_RELEASE_ID,
"executor_image_sha256": EXECUTOR_IMAGE_SHA256,
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
}
)
release = _write(release_root / "executor-release.json", release_payload)
return {
M49_PORTABLE_COMPILED_RUNNER_ASSET_ID: (binary, binary_payload),
M49_PORTABLE_COMPILED_RUNNER_BUILD_SEAL_ASSET_ID: (
build_seal,
build_seal_payload,
),
M49_PORTABLE_PROFILE_ASSET_ID: (profile, profile_payload),
service_module.M49_EXECUTOR_RELEASE_ASSET_ID: (release, release_payload),
}
def _ready_definition_registry(
tmp_path: Path,
*,
release_sha256: str,
) -> tuple[Path, PortableRunDefinitionRegistry]:
root = cast(
dict[str, object],
json.loads(DEFINITIONS_PATH.read_text(encoding="utf-8")),
)
selected = copy.deepcopy(
next(
cast(dict[str, object], value)
for value in cast(list[object], root["definitions"])
if cast(dict[str, object], value)["setup_id"] == service_module.M49_WORKER_SETUP_ID
)
)
base = PortableRunDefinitionRegistry.from_file(DEFINITIONS_PATH).resolve_setup(
service_module.M49_WORKER_SETUP_ID
)
components = cast(list[object], selected["components"])
runner_component = {
"component_id": "m49-tgs-portable-runner-v1",
"kind": "runner",
"sha256": "c" * 64,
}
components[:] = [
value
for value in components
if cast(dict[str, object], value)["component_id"] != runner_component["component_id"]
]
components.append(runner_component)
components.sort(key=lambda value: cast(str, cast(dict[str, object], value)["component_id"]))
executor = {
"contour_id": "worker-006",
"state": "ready",
"release_id": service_module.M49_WORKER_RELEASE_ID,
"release_sha256": release_sha256,
"image_sha256": EXECUTOR_IMAGE_SHA256,
"reason_code": None,
"reason": None,
}
selected["executor"] = executor
identity = copy.deepcopy(base.identity_document())
identity["components"] = copy.deepcopy(components)
identity["executor"] = {
key: executor[key]
for key in ("contour_id", "state", "release_id", "release_sha256", "image_sha256")
}
selected["definition_sha256"] = canonical_sha256(identity)
path = tmp_path / "definitions.json"
path.write_bytes(
canonical_json(
{
"schema_version": root["schema_version"],
"definitions": [selected],
}
)
)
return path, PortableRunDefinitionRegistry.from_file(path)
def _installation_receipt(
release_root: Path,
files: dict[str, tuple[Path, bytes]],
) -> tuple[Path, bytes]:
release_sha256 = _sha256(files[service_module.M49_EXECUTOR_RELEASE_ASSET_ID][1])
rows = [
{
"asset_id": asset_id,
"relative_path": path.name,
"byte_length": len(payload),
"sha256": _sha256(payload),
}
for asset_id, (path, payload) in sorted(files.items())
]
document = {
"schema_version": service_module.M49_WORKER_INSTALLATION_RECEIPT_SCHEMA,
"receipt_state": "installed-ready",
"worker_id": "worker-006",
"computer_name": "DESKTOP-OPJ8J04",
"source_revision": SOURCE_REVISION,
"candidate_sha256": SOURCE_CANDIDATE_SHA256,
"candidate_release_sha256": "d" * 64,
"candidate_worker_installation_receipt_sha256": "e" * 64,
"release_id": service_module.M49_WORKER_RELEASE_ID,
"release_sha256": release_sha256,
"release_root": (
"D:\\NDC_MISSIONCORE\\runtime\\releases\\observatory-portable\\"
f"m49-tgs-portable-candidate-{SOURCE_CANDIDATE_SHA256}\\ready"
),
"runtime_release_root": str(release_root),
"base_image_sha256": M49_PORTABLE_TRAVEL_BUILD_IMAGE_SHA256,
"executor_image_sha256": EXECUTOR_IMAGE_SHA256,
"files": rows,
"fixture_smoke": "passed",
"protected_runtime": [
{"name": name, "container_id": f"{index:x}" * 64}
for index, name in enumerate(
service_module._PROTECTED_RUNTIME_NAMES, # noqa: SLF001
start=1,
)
],
"legacy_m49_task_state": None,
"blockers": [],
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
}
payload = canonical_json(document)
path = _write(release_root / "worker-installation-receipt.json", payload)
return path, payload
def _runtime_registry(
tmp_path: Path,
*,
definitions: PortableRunDefinitionRegistry,
files: dict[str, tuple[Path, bytes]],
receipt_payload: bytes,
) -> Path:
definition = definitions.resolve_setup(service_module.M49_WORKER_SETUP_ID)
assets = [
{
"asset_id": M49_PORTABLE_COMPILED_RUNNER_ASSET_ID,
"kind": "local-file",
"sha256": _sha256(files[M49_PORTABLE_COMPILED_RUNNER_ASSET_ID][1]),
"byte_length": len(files[M49_PORTABLE_COMPILED_RUNNER_ASSET_ID][1]),
"component_id": None,
"model_release_id": None,
"model_artifact_role": None,
},
{
"asset_id": M49_PORTABLE_COMPILED_RUNNER_BUILD_SEAL_ASSET_ID,
"kind": "local-file",
"sha256": _sha256(files[M49_PORTABLE_COMPILED_RUNNER_BUILD_SEAL_ASSET_ID][1]),
"byte_length": len(files[M49_PORTABLE_COMPILED_RUNNER_BUILD_SEAL_ASSET_ID][1]),
"component_id": None,
"model_release_id": None,
"model_artifact_role": None,
},
{
"asset_id": service_module.M49_EXECUTOR_RELEASE_ASSET_ID,
"kind": "local-file",
"sha256": _sha256(files[service_module.M49_EXECUTOR_RELEASE_ASSET_ID][1]),
"byte_length": len(files[service_module.M49_EXECUTOR_RELEASE_ASSET_ID][1]),
"component_id": None,
"model_release_id": None,
"model_artifact_role": None,
},
{
"asset_id": M49_PORTABLE_PROFILE_ASSET_ID,
"kind": "definition-component",
"sha256": _sha256(files[M49_PORTABLE_PROFILE_ASSET_ID][1]),
"byte_length": len(files[M49_PORTABLE_PROFILE_ASSET_ID][1]),
"component_id": "m49-tgs-portable-profile-v2",
"model_release_id": None,
"model_artifact_role": None,
},
{
"asset_id": service_module.M49_WORKER_INSTALLATION_RECEIPT_ASSET_ID,
"kind": "local-file",
"sha256": _sha256(receipt_payload),
"byte_length": len(receipt_payload),
"component_id": None,
"model_release_id": None,
"model_artifact_role": None,
},
{
"asset_id": M49_PORTABLE_TRAVEL_IMAGE_ASSET_ID,
"kind": "container-image",
"sha256": M49_PORTABLE_TRAVEL_BUILD_IMAGE_SHA256,
"byte_length": None,
"component_id": None,
"model_release_id": None,
"model_artifact_role": None,
},
]
assets.sort(key=lambda value: cast(str, value["asset_id"]))
identity = {
"schema_version": "missioncore.observatory-portable-worker-runtime-candidate/v1",
"adapter_id": service_module.M49_WORKER_ADAPTER_ID,
"setup_id": definition.setup_id,
"definition_id": definition.definition_id,
"definition_version": definition.version,
"definition_sha256": definition.definition_sha256,
"source_adapter_sha256": definition.source_adapter.contract_sha256,
"model_manifest_sha256": definition.model_manifest_sha256,
"resource_profile_sha256": definition.resource_profile.profile_sha256,
"result_contract_sha256": definition.result_contract.contract_sha256,
"state": "ready",
"executor": {
"release_id": service_module.M49_WORKER_RELEASE_ID,
"release_sha256": definition.executor.release_sha256,
"image_sha256": EXECUTOR_IMAGE_SHA256,
},
"reusable_assets": assets,
"phases": [
{"phase_id": phase_id, "state": "implemented"}
for phase_id in M49_PORTABLE_RUNTIME_PHASES
],
"blockers": [],
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
}
path = tmp_path / "runtime-registry.json"
path.write_bytes(
canonical_json(
{
"schema_version": ("missioncore.observatory-portable-worker-runtime-registry/v1"),
"candidates": [{**identity, "candidate_sha256": canonical_sha256(identity)}],
}
)
)
return path
def _fixture(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> tuple[service_module.M49WorkerEntrypointConfiguration, Path]:
release_root = tmp_path / "release"
release_root.mkdir()
monkeypatch.setattr(service_module, "_FIXED_RUNTIME_RELEASE_ROOT", release_root)
files = _release_files(release_root)
release_sha256 = _sha256(files[service_module.M49_EXECUTOR_RELEASE_ASSET_ID][1])
definitions_path, definitions = _ready_definition_registry(
tmp_path,
release_sha256=release_sha256,
)
receipt_path, receipt_payload = _installation_receipt(release_root, files)
runtime_path = _runtime_registry(
tmp_path,
definitions=definitions,
files=files,
receipt_payload=receipt_payload,
)
token = _write(tmp_path / "worker.token", b"worker-006-test-bearer-token-000001")
token.chmod(0o600)
worker = ObservatoryWorkerServiceConfiguration(
base_url="http://127.0.0.1:18080",
bearer_token_file=token,
work_root=tmp_path / "work",
idle_poll_seconds=0.05,
transport_backoff_seconds=0.05,
max_consecutive_transport_failures=2,
)
return (
service_module.M49WorkerEntrypointConfiguration(
worker=worker,
definitions_file=definitions_path,
runtime_registry_file=runtime_path,
installation_receipt_file=receipt_path,
),
receipt_path,
)
def test_fixed_m49_composition_uses_one_gateway_for_agent_source_and_result(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
configuration, _receipt = _fixture(tmp_path, monkeypatch)
requests: list[httpx.Request] = []
def handle(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(204)
service = service_module.compose_installed_m49_worker_service(
configuration,
http_transport=httpx.MockTransport(handle),
)
gateway = service.gateway
assert isinstance(gateway, ObservatoryWorkerHttpGateway)
assert service.agent._transport is gateway # noqa: SLF001
registration = service.agent._executors.registrations[0] # noqa: SLF001
adapter = cast(PortableWorkerExecutorAdapter, registration.adapter)
materializer = cast(M49PortableSourceMaterializerAdapter, adapter.source_materializer)
assert materializer.upstream is gateway
assert adapter.publisher is gateway
service_module.run_installed_m49_worker(service, stop=Event(), once=True)
assert [request.url.path for request in requests] == [
"/api/v1/worker/observatory/recorded-jobs/claims"
]
assert gateway._client.is_closed # noqa: SLF001
def test_fixed_m49_composition_rejects_receipt_asset_drift_before_gateway(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
configuration, receipt_path = _fixture(tmp_path, monkeypatch)
receipt_path.write_bytes(receipt_path.read_bytes() + b"\n")
with pytest.raises(
service_module.M49WorkerCompositionError,
match="asset admission is not ready",
):
service_module.compose_installed_m49_worker_service(
configuration,
http_transport=httpx.MockTransport(
lambda _request: pytest.fail("gateway must not be reached")
),
)
def test_installation_receipt_rejects_nonfixed_runtime_root_and_links(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
configuration, receipt_path = _fixture(tmp_path, monkeypatch)
monkeypatch.setattr(service_module, "_FIXED_RUNTIME_RELEASE_ROOT", tmp_path / "other")
with pytest.raises(service_module.M49WorkerCompositionError, match="root is not fixed"):
service_module.load_m49_worker_installation_receipt(receipt_path)
monkeypatch.setattr(service_module, "_FIXED_RUNTIME_RELEASE_ROOT", receipt_path.parent)
link = tmp_path / "receipt-link.json"
link.symlink_to(receipt_path)
with pytest.raises(service_module.M49WorkerCompositionError, match="unsafe"):
service_module.load_m49_worker_installation_receipt(link)
def test_entrypoint_environment_requires_all_absolute_fixed_files(tmp_path: Path) -> None:
environment = {
"MISSIONCORE_OBSERVATORY_WORKER_TOKEN_FILE": str(tmp_path / "worker.token"),
"MISSIONCORE_OBSERVATORY_WORKER_WORK_ROOT": str(tmp_path / "work"),
service_module.M49_WORKER_DEFINITIONS_FILE_ENV: str(tmp_path / "definitions.json"),
service_module.M49_WORKER_RUNTIME_REGISTRY_FILE_ENV: str(tmp_path / "runtime.json"),
service_module.M49_WORKER_INSTALLATION_RECEIPT_FILE_ENV: str(tmp_path / "receipt.json"),
}
configuration = service_module.M49WorkerEntrypointConfiguration.from_environment(environment)
assert configuration.worker.base_url == "http://127.0.0.1:18080"
assert configuration.installation_receipt_file == tmp_path / "receipt.json"
with pytest.raises(service_module.M49WorkerCompositionError, match="is required"):
service_module.M49WorkerEntrypointConfiguration.from_environment(
{
key: value
for key, value in environment.items()
if key != service_module.M49_WORKER_RUNTIME_REGISTRY_FILE_ENV
}
)
@@ -21,7 +21,7 @@ REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
REGISTRY_PATH = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
DEFINITION_SHA256 = "3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2"
MODEL_MANIFEST_SHA256 = "3fd2d43af73bd73f89d9ffae95d8770cfdeb46033ec967509124fac6ae4afe56"
M49_DEFINITION_SHA256 = "73611f24d70319ea1edca428726d6538a3cbad012a415cc0c1a7ecb7d9b4d910"
M49_DEFINITION_SHA256 = "f56d6321bd794ccdfb7d2e3b05d044b11f616ffb81ee29517386cc253046d4eb"
M49_MODEL_MANIFEST_SHA256 = "489a43448f720a9b5c7993dc8279d167b77191a586f0d87b6d38b81cf728e2f1"
@@ -99,7 +99,7 @@ def test_source_requirements_map_exactly_to_admission_contract() -> None:
assert admission.adapter_sha256 == definition.source_adapter.contract_sha256
def test_m49_portable_v2_is_model_free_and_contains_no_exact_source_binding() -> None:
def test_m49_portable_v3_is_ready_model_free_and_contains_no_exact_source_binding() -> None:
definition = _registry().resolve_setup("m49-tgs-portable-v2")
assert definition.definition_sha256 == M49_DEFINITION_SHA256
@@ -107,10 +107,15 @@ def test_m49_portable_v2_is_model_free_and_contains_no_exact_source_binding() ->
assert definition.learned_models == ()
assert definition.model_manifest_sha256 == M49_MODEL_MANIFEST_SHA256
assert definition.resource_profile.accelerator_id == "cpu-only"
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
assert definition.version == 3
assert definition.executor.state == "ready"
assert definition.executor.release_id == "m49-tgs-portable-executor-v1"
assert definition.executor.release_sha256 == (
"c5b0670d943fe0452ef4bbfbc144ab2439a1a674f9ef164798ad9f8b1ecc29fa"
)
assert definition.executor.image_sha256 == (
"f9278ab21aa65045be993dd19bffc25f49955e19598893ac78cc4761ca63ecf3"
)
identity = json.dumps(definition.identity_document(), sort_keys=True)
assert "RAVNOVES00" not in identity
assert "20260720T065719Z_viewer_live" not in identity
@@ -123,6 +128,9 @@ def test_m49_portable_v2_is_model_free_and_contains_no_exact_source_binding() ->
assert hashlib.sha256(profile_path.read_bytes()).hexdigest() == (
components["m49-tgs-portable-profile-v2"].sha256
)
assert components["m49-tgs-portable-runner-v1"].sha256 == (
"e3bb2e91c70712eff74e8e69718075a407e042fb494616d65984500da42b21a9"
)
assert profile["source_binding"] == {
"mode": "admitted-k1-recording",
"camera_timeline": "dynamic",
@@ -132,18 +140,15 @@ def test_m49_portable_v2_is_model_free_and_contains_no_exact_source_binding() ->
"filesystem_paths": "executor-resolved",
}
without_runner = tuple(
component
for component in definition.components
if component.component_id != "m49-tgs-portable-runner-v1"
)
with pytest.raises(PortableRunDefinitionRegistryError, match="runner"):
replace(
definition,
executor=PortableExecutorAvailability(
contour_id="worker-006",
state="ready",
release_id="m49-tgs-portable-executor-v2",
release_sha256="1" * 64,
image_sha256="2" * 64,
reason_code=None,
reason=None,
),
components=without_runner,
)
@@ -280,7 +285,7 @@ def test_duplicate_definition_and_incomplete_ready_executor_are_rejected(
PortableRunDefinitionRegistry.from_file(_write(tmp_path, incomplete))
def test_production_definition_is_blocked_until_release_and_image_are_sealed() -> None:
def test_blocked_lab_definition_does_not_hide_ready_m49_definition() -> None:
registry = _registry()
definition = registry.definitions[0]
@@ -293,8 +298,9 @@ def test_production_definition_is_blocked_until_release_and_image_are_sealed() -
match="not sealed or installed",
):
definition.to_recorded_run_definition()
with pytest.raises(PortableRunDefinitionUnavailableError):
registry.to_recorded_registry()
ready = registry.ready_recorded_definitions()
assert tuple(row.setup_id for row in ready) == ("m49-tgs-portable-v2",)
assert registry.to_recorded_registry().definitions == ready
def test_conversion_to_recorded_definition_requires_and_preserves_sealed_identities() -> None:
@@ -332,7 +338,7 @@ 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_m49 = registry.resolve_setup("m49-tgs-portable-v2")
ready_m49 = registry.resolve_setup("m49-tgs-portable-v2")
ready_executor = PortableExecutorAvailability(
contour_id="worker-006",
state="ready",
@@ -349,11 +355,15 @@ def test_blocked_definition_does_not_hide_an_unrelated_ready_definition() -> Non
executor=ready_executor,
definition_sha256=canonical_sha256(identity),
)
mixed = PortableRunDefinitionRegistry((ready_lab, blocked_m49))
mixed = PortableRunDefinitionRegistry((ready_lab, ready_m49))
assert mixed.ready_recorded_definitions() == (ready_lab.to_recorded_run_definition(),)
assert mixed.to_recorded_registry().definitions == (ready_lab.to_recorded_run_definition(),)
assert mixed.resolve_setup("m49-tgs-portable-v2") is blocked_m49
expected = (
ready_lab.to_recorded_run_definition(),
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
def test_production_lab_v1_model_component_and_result_identities_are_exact() -> None:
+5 -3
View File
@@ -275,7 +275,9 @@ def test_portable_api_check_sha_fences_ready_submission(tmp_path: Path) -> None:
assert binding.submit_count == 1
def test_portable_api_rejects_blocked_executor_before_binding_submit(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)
ready_registry = _ready_lab_registry()
queue = ObservatoryRecordedJobQueue(
@@ -297,12 +299,12 @@ def test_portable_api_rejects_blocked_executor_before_binding_submit(tmp_path: P
recorded_job_queue=queue,
)
)
definition = full_registry.resolve_setup("m49-tgs-portable-v2")
definition = full_registry.resolve_setup("lab-v1-eomt-ddrnet-portable-v1")
response = TestClient(app).post(
"/api/v1/observatory/runs",
json={
"schema_version": "missioncore.observatory-recorded-run-submit/v1",
"idempotency_key": "portable:m49:blocked",
"idempotency_key": "portable:lab-v1:blocked",
"source_session_id": SOURCE_SESSION_ID,
"setup_id": definition.setup_id,
"definition_sha256": definition.definition_sha256,
@@ -156,7 +156,8 @@ def test_generic_catalog_projects_lab_v1_and_model_free_m49_independently() -> N
assert m49["display_name"] == PORTABLE_M49_DISPLAY_NAME
assert m49["run_definition"]["models"] == []
assert m49["source_compatibility"]["outcome"] == "pass"
assert m49["executor"]["state"] == "not-installed"
assert m49["executor"]["state"] == "ready"
assert m49["executor"]["ready"] is True
assert m49["preflight"]["submission_allowed"] is False
@@ -165,14 +166,10 @@ def test_calculation_profile_policies_cover_both_exact_portable_definitions() ->
profiles = portable_calculation_profile_registry(registry)
resolved = {
definition.setup_id: profiles.resolve(definition)
for definition in registry.definitions
definition.setup_id: profiles.resolve(definition) for definition in registry.definitions
}
assert resolved["lab-v1-eomt-ddrnet-portable-v1"].lab_id == "LAB V1"
assert (
resolved["lab-v1-eomt-ddrnet-portable-v1"].display_name
== PORTABLE_LAB_V1_DISPLAY_NAME
)
assert resolved["lab-v1-eomt-ddrnet-portable-v1"].display_name == PORTABLE_LAB_V1_DISPLAY_NAME
assert resolved["m49-tgs-portable-v2"].lab_id == "LAB M4.9T5"
assert resolved["m49-tgs-portable-v2"].display_name == PORTABLE_M49_DISPLAY_NAME
@@ -6,6 +6,7 @@ from typing import cast
import pytest
from k1link.artifact_gateway import CentralArtifactStore
from k1link.observatory import portable_worker_integration as integration_module
from k1link.observatory.m49_portable_result import (
M49_PORTABLE_RESULT_CONTRACT_SHA256,
validate_m49_portable_result,
@@ -31,12 +32,14 @@ from k1link.observatory.portable_setup_projection import (
PORTABLE_M49_SETUP_ID,
)
from k1link.observatory.portable_worker_integration import (
OBSERVATORY_WORKER_LOCAL_ENABLED_ENV,
OBSERVATORY_WORKER_RESULT_STAGING_ROOT_ENV,
OBSERVATORY_WORKER_SOURCE_CAS_ROOT_ENV,
PORTABLE_LAB_V1_RESULT_CONTRACT_SHA256,
PortableWorkerIntegrationError,
PortableWorkerStorageRoots,
build_portable_observatory_worker_integration,
observatory_worker_local_enabled,
portable_result_validator_registry,
)
from k1link.observatory.recorded_jobs import ObservatoryRecordedJobQueue
@@ -59,6 +62,23 @@ def test_exact_validator_registry_covers_both_portable_profiles() -> None:
assert validators.resolve(M49_PORTABLE_RESULT_CONTRACT_SHA256) is validate_m49_portable_result
def test_local_worker_gate_is_fail_closed_and_accepts_only_exact_one() -> None:
assert observatory_worker_local_enabled({}) is False
assert observatory_worker_local_enabled({OBSERVATORY_WORKER_LOCAL_ENABLED_ENV: ""}) is False
assert observatory_worker_local_enabled(
{OBSERVATORY_WORKER_LOCAL_ENABLED_ENV: "1"}
) is True
for value in ("0", "true", " 1", "1 "):
with pytest.raises(
PortableWorkerIntegrationError,
match="must be exactly 1 when enabled",
):
observatory_worker_local_enabled(
{OBSERVATORY_WORKER_LOCAL_ENABLED_ENV: value}
)
def test_validator_registry_fails_when_one_required_profile_is_absent() -> None:
definitions = _definitions()
only_lab_v1 = PortableRunDefinitionRegistry((definitions.definitions[0],))
@@ -173,6 +193,58 @@ def test_storage_roots_load_only_from_existing_disjoint_central_directories(
)
def test_storage_roots_accept_canonical_local_directories_under_data_dir(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
data_dir = tmp_path / "missioncore-data"
boundary = data_dir / "observatory-worker-local"
artifact_store = boundary / "artifact-store"
source_cas = boundary / "source-cas"
result_staging = boundary / "result-staging"
for path in (artifact_store, source_cas, result_staging):
path.mkdir(parents=True, exist_ok=True)
mount_checks: list[Path] = []
monkeypatch.setattr(
integration_module.os.path,
"ismount",
lambda path: mount_checks.append(Path(path)) or False,
)
roots = PortableWorkerStorageRoots.from_paths(
artifact_store_root=artifact_store,
source_cas_root=source_cas,
result_staging_root=result_staging,
)
assert roots == PortableWorkerStorageRoots(
source_cas_root=source_cas,
result_staging_root=result_staging,
)
assert mount_checks == []
def test_explicit_volumes_storage_boundary_still_requires_a_mount(
monkeypatch: pytest.MonkeyPatch,
) -> None:
mount_checks: list[Path] = []
monkeypatch.setattr(
integration_module.os.path,
"ismount",
lambda path: mount_checks.append(Path(path)) or False,
)
with pytest.raises(
PortableWorkerIntegrationError,
match="central artifact volume is not mounted: /Volumes/nodedc",
):
integration_module._require_mounted_volume(
Path("/Volumes/nodedc/mission-core")
)
assert mount_checks == [Path("/Volumes/nodedc")]
def test_server_integration_has_no_data_directory_storage_fallback(
tmp_path: Path,
) -> None:
@@ -34,12 +34,8 @@ from k1link.observatory.worker_agent import (
)
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
DEFINITION_REGISTRY = (
REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
)
RUNTIME_REGISTRY = (
REPOSITORY_ROOT / "config" / "observatory-worker-runtime-candidates.json"
)
DEFINITION_REGISTRY = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
RUNTIME_REGISTRY = REPOSITORY_ROOT / "config" / "observatory-worker-runtime-candidates.json"
def _definitions() -> PortableRunDefinitionRegistry:
@@ -55,33 +51,39 @@ def _runtime() -> PortableWorkerRuntimeRegistry:
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)
}
return set(value) | {nested for child in value.values() for nested in _all_keys(child)}
if isinstance(value, list):
return {nested for child in value for nested in _all_keys(child)}
return set()
def test_production_candidates_bind_exact_definitions_but_remain_blocked() -> None:
def test_production_candidates_bind_exact_definitions_and_only_m49_is_ready() -> None:
registry = _runtime()
assert {candidate.setup_id for candidate in registry.candidates} == {
"lab-v1-eomt-ddrnet-portable-v1",
"m49-tgs-portable-v2",
}
for candidate in registry.candidates:
assert candidate.ready is False
assert candidate.executor is None
assert "executor-release-unsealed" in candidate.blockers
assert any(phase.state == "missing" for phase in candidate.phases)
with pytest.raises(
PortableWorkerRuntimeUnavailableError,
match="no executor identity",
):
candidate.executor_identity()
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()
m49 = by_setup["m49-tgs-portable-v2"]
assert m49.ready is True
assert m49.executor is not None
assert m49.blockers == ()
assert all(phase.state == "implemented" for phase in m49.phases)
assert m49.executor_identity().release_sha256 == (
"c5b0670d943fe0452ef4bbfbc144ab2439a1a674f9ef164798ad9f8b1ecc29fa"
)
def test_candidate_contract_is_source_independent_and_instruction_free() -> None:
@@ -96,75 +98,34 @@ def test_candidate_contract_is_source_independent_and_instruction_free() -> None
assert not any("priority" in key for key in keys)
def test_m49_reuses_portable_profile_generic_runner_and_travel_image() -> None:
def test_m49_ready_candidate_requires_promoted_runner_receipts_and_travel_image() -> None:
candidate = _runtime().resolve(
"m49-tgs-portable-v2",
"73611f24d70319ea1edca428726d6538a3cbad012a415cc0c1a7ecb7d9b4d910",
"f56d6321bd794ccdfb7d2e3b05d044b11f616ffb81ee29517386cc253046d4eb",
)
bindings = {
"m49-portable-profile": PortableWorkerLocalAssetBinding(
asset_id="m49-portable-profile",
file_path=REPOSITORY_ROOT / "config" / "perception" / "m49-tgs-portable-v2.json",
),
"m49-portable-runner-source": PortableWorkerLocalAssetBinding(
asset_id="m49-portable-runner-source",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "observatory_portable"
/ "run_m49_tgs_portable.cpp"
),
),
"m49-portable-runner-manifest": PortableWorkerLocalAssetBinding(
asset_id="m49-portable-runner-manifest",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "observatory_portable"
/ "m49-tgs-portable-runner-source.json"
),
),
"m49-portable-runner-wrapper": PortableWorkerLocalAssetBinding(
asset_id="m49-portable-runner-wrapper",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "observatory_portable"
/ "run_m49_tgs_portable.sh"
),
),
"m49-portable-smoke": PortableWorkerLocalAssetBinding(
asset_id="m49-portable-smoke",
file_path=(
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "observatory_portable"
/ "smoke_m49_tgs_portable.sh"
),
),
"travel-tgs-image": PortableWorkerLocalAssetBinding(
asset_id="travel-tgs-image",
image_sha256=(
"7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
),
image_sha256=("7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"),
),
}
admission = inspect_runtime_candidate(candidate, bindings)
assert {item.state for item in admission.assets} == {"matched"}
states = {item.asset_id: item.state for item in admission.assets}
assert states["m49-portable-profile"] == "matched"
assert states["travel-tgs-image"] == "matched"
assert states["m49-portable-compiled-runner"] == "missing"
assert states["m49-portable-compiled-runner-build-seal"] == "missing"
assert states["m49-portable-executor-release"] == "missing"
assert states["m49-portable-worker-installation-receipt"] == "missing"
assert admission.ready is False
assert "portable-tgs-runner-unsealed" in admission.blockers
assert "portable-camera-lidar-timeline-unimplemented" in admission.blockers
assert "portable-result-assembler-unimplemented" in admission.blockers
assert "asset-m49-portable-compiled-runner-missing" in admission.blockers
assert "asset-m49-portable-worker-installation-receipt-missing" in admission.blockers
def test_m49_portable_runner_has_no_exact_source_or_frame_count_binding() -> None:
@@ -195,13 +156,7 @@ def test_m49_portable_runner_has_no_exact_source_or_frame_count_binding() -> Non
def test_m49_portable_runner_source_release_is_content_addressed() -> None:
root = (
REPOSITORY_ROOT
/ "experiments"
/ "perception"
/ "worker"
/ "observatory_portable"
)
root = REPOSITORY_ROOT / "experiments" / "perception" / "worker" / "observatory_portable"
manifest = json.loads(
(root / "m49-tgs-portable-runner-source.json").read_text(encoding="utf-8")
)
@@ -233,9 +188,7 @@ def test_lab_candidate_verifies_reusable_repository_assets_without_claiming_exec
bindings = {
"ddrnet-goose-image": PortableWorkerLocalAssetBinding(
asset_id="ddrnet-goose-image",
image_sha256=(
"591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"
),
image_sha256=("591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"),
),
"ddrnet-goose-runner": PortableWorkerLocalAssetBinding(
asset_id="ddrnet-goose-runner",
@@ -250,9 +203,7 @@ def test_lab_candidate_verifies_reusable_repository_assets_without_claiming_exec
),
"eomt-image": PortableWorkerLocalAssetBinding(
asset_id="eomt-image",
image_sha256=(
"58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
),
image_sha256=("58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"),
),
"eomt-orchestrator": PortableWorkerLocalAssetBinding(
asset_id="eomt-orchestrator",
@@ -324,7 +275,7 @@ def test_lab_candidate_verifies_reusable_repository_assets_without_claiming_exec
def test_local_asset_tampering_is_reported_without_execution(tmp_path: Path) -> None:
candidate = _runtime().resolve(
"m49-tgs-portable-v2",
"73611f24d70319ea1edca428726d6538a3cbad012a415cc0c1a7ecb7d9b4d910",
"f56d6321bd794ccdfb7d2e3b05d044b11f616ffb81ee29517386cc253046d4eb",
)
tampered = tmp_path / "m49-profile.json"
tampered.write_text("{}\n", encoding="utf-8")
@@ -346,9 +297,10 @@ def test_local_asset_tampering_is_reported_without_execution(tmp_path: Path) ->
states = {item.asset_id: item.state for item in admission.assets}
assert states["m49-portable-profile"] == "mismatched"
assert states["travel-tgs-image"] == "mismatched"
assert states["m49-portable-runner-source"] == "missing"
assert states["m49-portable-runner-manifest"] == "missing"
assert states["m49-portable-runner-wrapper"] == "missing"
assert states["m49-portable-compiled-runner"] == "missing"
assert states["m49-portable-compiled-runner-build-seal"] == "missing"
assert states["m49-portable-executor-release"] == "missing"
assert states["m49-portable-worker-installation-receipt"] == "missing"
assert "asset-m49-portable-profile-mismatched" in admission.blockers
assert "asset-travel-tgs-image-mismatched" in admission.blockers
@@ -431,8 +383,7 @@ def test_ready_local_adapter_composes_only_local_ports_and_exact_job(
image_sha256="2" * 64,
)
phases = tuple(
PortableWorkerRuntimePhase(phase.phase_id, "implemented")
for phase in blocked.phases
PortableWorkerRuntimePhase(phase.phase_id, "implemented") for phase in blocked.phases
)
candidate_identity = blocked.identity_document()
candidate_identity["definition_sha256"] = definition.definition_sha256
@@ -470,9 +421,7 @@ def test_ready_local_adapter_composes_only_local_ports_and_exact_job(
return PortableWorkerSourceStage(
root=source_root,
source_bundle_sha256=job.source_bundle_sha256,
source_capability_manifest_sha256=(
job.source_capability_manifest_sha256
),
source_capability_manifest_sha256=(job.source_capability_manifest_sha256),
source_adapter_sha256=job.source_adapter_sha256,
)
+81 -2
View File
@@ -1,5 +1,9 @@
from __future__ import annotations
import json
import os
import subprocess
import sys
from pathlib import Path
from k1link.observatory.m49_portable_result import (
@@ -8,6 +12,7 @@ from k1link.observatory.m49_portable_result import (
)
from k1link.observatory.portable_lab_v1_executor import validate_lab_v1_result_v2
from k1link.observatory.portable_worker_integration import (
OBSERVATORY_WORKER_LOCAL_ENABLED_ENV,
PORTABLE_LAB_V1_RESULT_CONTRACT_SHA256,
)
from k1link.web import app as app_module
@@ -15,7 +20,7 @@ from k1link.web import app as app_module
WORKER_ROUTE_PREFIX = "/api/v1/worker/observatory"
def test_worker_router_is_hard_disabled_until_lease_and_publisher_exist() -> None:
def test_worker_router_is_fail_closed_without_explicit_local_gate() -> None:
assert app_module.OBSERVATORY_RECORDED_JOB_QUEUE is not None
assert app_module.OBSERVATORY_PORTABLE_RESULT_VALIDATORS is not None
assert (
@@ -32,12 +37,15 @@ def test_worker_router_is_hard_disabled_until_lease_and_publisher_exist() -> Non
)
assert app_module.OBSERVATORY_WORKER_CLAIM_LEASE_READY is False
assert app_module.OBSERVATORY_WORKER_VERIFIED_RESULT_PUBLISHER_READY is False
assert app_module.OBSERVATORY_WORKER_LOCAL_ENABLED is False
assert app_module.OBSERVATORY_WORKER_API_GATE_ENABLED is False
assert app_module.OBSERVATORY_WORKER_PRODUCTION_API_ENABLED is False
assert app_module.OBSERVATORY_WORKER_DISPATCH_READY is False
assert app_module.OBSERVATORY_WORKER_AUTHENTICATION is None
assert app_module.OBSERVATORY_WORKER_AUTHENTICATION_ERROR is not None
assert app_module.OBSERVATORY_WORKER_API_ERROR is not None
assert "hard-disabled" in app_module.OBSERVATORY_WORKER_API_ERROR
assert "local-only Worker API gate is disabled" in app_module.OBSERVATORY_WORKER_API_ERROR
assert OBSERVATORY_WORKER_LOCAL_ENABLED_ENV in app_module.OBSERVATORY_WORKER_API_ERROR
if app_module.session_artifact_gateway is None:
assert app_module.OBSERVATORY_PORTABLE_WORKER_INTEGRATION is None
assert app_module.OBSERVATORY_PORTABLE_WORKER_INTEGRATION_ERROR is not None
@@ -87,3 +95,74 @@ def test_valid_worker_credential_cannot_enable_production_router(
getattr(route, "path", "").startswith(WORKER_ROUTE_PREFIX)
for route in app_module.app.routes
)
def test_explicit_local_gate_enables_router_with_local_storage_and_authentication(
tmp_path: Path,
) -> None:
data_dir = tmp_path / "data"
evidence_dir = tmp_path / "evidence"
legacy_dir = tmp_path / "legacy"
boundary = data_dir / "observatory-worker-local"
artifact_store = boundary / "artifact-store"
source_cas = boundary / "source-cas"
result_staging = boundary / "result-staging"
token_path = data_dir / "worker-auth" / "observatory-worker.token"
for path in (
data_dir,
evidence_dir,
legacy_dir,
artifact_store,
source_cas,
result_staging,
token_path.parent,
):
path.mkdir(mode=0o700, parents=True, exist_ok=True)
token_path.write_text("worker-006-test-bearer-secret-32bytes", encoding="ascii")
token_path.chmod(0o600)
environment = os.environ.copy()
environment.update(
{
"MISSIONCORE_DATA_DIR": str(data_dir),
"MISSIONCORE_EVIDENCE_DIR": str(evidence_dir),
"MISSIONCORE_LEGACY_SESSIONS_DIR": str(legacy_dir),
"MISSIONCORE_ARTIFACT_STORE_ROOT": str(artifact_store),
"MISSIONCORE_OBSERVATORY_WORKER_SOURCE_CAS_ROOT": str(source_cas),
"MISSIONCORE_OBSERVATORY_WORKER_RESULT_STAGING_ROOT": str(result_staging),
OBSERVATORY_WORKER_LOCAL_ENABLED_ENV: "1",
}
)
probe = """
import json
from k1link.web import app as app_module
prefix = "/api/v1/worker/observatory"
paths = app_module.app.openapi().get("paths", {})
print(json.dumps({
"local_enabled": app_module.OBSERVATORY_WORKER_LOCAL_ENABLED,
"api_gate_enabled": app_module.OBSERVATORY_WORKER_API_GATE_ENABLED,
"claim_ready": app_module.OBSERVATORY_WORKER_CLAIM_LEASE_READY,
"publisher_ready": app_module.OBSERVATORY_WORKER_VERIFIED_RESULT_PUBLISHER_READY,
"dispatch_ready": app_module.OBSERVATORY_WORKER_DISPATCH_READY,
"api_error": app_module.OBSERVATORY_WORKER_API_ERROR,
"worker_route": any(path.startswith(prefix) for path in paths),
}))
"""
completed = subprocess.run(
[sys.executable, "-c", probe],
check=True,
capture_output=True,
text=True,
env=environment,
)
state = json.loads(completed.stdout)
assert state == {
"local_enabled": True,
"api_gate_enabled": True,
"claim_ready": True,
"publisher_ready": True,
"dispatch_ready": True,
"api_error": None,
"worker_route": True,
}