feat(simulation): add provider-neutral worker profile

This commit is contained in:
DCCONSTRUCTIONS
2026-07-25 00:29:00 +03:00
parent 5abd07d2bf
commit 37c24b5fa8
21 changed files with 1275 additions and 121 deletions
+15 -9
View File
@@ -18,6 +18,7 @@ from k1link.simulation import (
RunKind,
RunState,
)
from k1link.simulation.stock_rover import STOCK_ROVER_PROVIDER_PROFILE
from k1link.web.polygon_api import (
AckermannCommandRequest,
StartStockRoverRequest,
@@ -256,7 +257,7 @@ class _FakeWorkerGateway:
self.calls.append(("command", idempotency_key))
assert run_id == self.active_run_id
return {
"schema_version": "missioncore.command-acceptance/v1",
"schema_version": "missioncore.command-acceptance/v2",
"run_id": run_id,
"command_id": "cmd-test",
"sequence": 1,
@@ -266,17 +267,18 @@ class _FakeWorkerGateway:
"steering_normalized": steering_normalized,
"authority_scope": "virtual-only",
"delivery": {
"provider": "px4-ros2-offboard",
"mode": "speed-steering",
"armed": True,
"offboard": True,
"provider_id": "px4-ros2-offboard",
"control_profile": "rover-speed-steering/v1",
"accepted": True,
"controller_ready": True,
"ttl_expired_count": 0,
"diagnostics": {"armed": True, "offboard": True},
},
}
def _status(self) -> dict[str, Any]:
return {
"schema_version": "missioncore.simulation-worker-status/v1",
"schema_version": "missioncore.simulation-worker-status/v2",
"worker_id": "mission-gpu-s1",
"transport": "unix",
"mode": "simulation",
@@ -284,7 +286,10 @@ class _FakeWorkerGateway:
"control_available": True,
"active_run_id": self.active_run_id,
"run_state": "running" if self.active_run_id else None,
"provider_ids": ["px4-gazebo-stock-rover"] if self.active_run_id else [],
"active_provider_ids": (
["px4-gazebo-stock-rover"] if self.active_run_id else []
),
"provider_profile": STOCK_ROVER_PROVIDER_PROFILE.to_dict(),
"isolation": {
"network": "loopback-only-netns",
"process_identity": "missioncore",
@@ -340,7 +345,7 @@ def test_polygon_worker_api_fails_closed_then_proxies_virtual_only_lifecycle() -
idempotency_key="command-001",
)
assert command["authority_scope"] == "virtual-only"
assert command["delivery"]["offboard"] is True
assert command["delivery"]["controller_ready"] is True
stopped = _endpoint(enabled, "/api/v1/polygon/worker/runs/{run_id}/stop", "POST")(
run_id=running["active_run_id"],
idempotency_key="stop-001",
@@ -366,7 +371,8 @@ def test_polygon_worker_status_is_explicitly_unavailable_when_not_registered() -
worker_provider=lambda: None,
)
status = _endpoint(router, "/api/v1/polygon/worker", "GET")()
assert status["schema_version"] == "missioncore.simulation-worker-status/v1"
assert status["schema_version"] == "missioncore.simulation-worker-status/v2"
assert status["available"] is False
assert status["control_available"] is False
assert status["provider_profile"] is None
assert status["authority"]["scope"] == "virtual-only"
@@ -0,0 +1,99 @@
from __future__ import annotations
from dataclasses import replace
import pytest
from k1link.simulation.contracts import ControlProfile
from k1link.simulation.provider_contract import (
ProviderRole,
SimulationClockDescriptor,
SimulationProviderContractError,
SimulationProviderDescriptor,
SimulationProviderProfile,
)
from k1link.simulation.stock_rover import STOCK_ROVER_PROVIDER_PROFILE
def _unreal_profile() -> SimulationProviderProfile:
return SimulationProviderProfile(
profile_id="unreal-native-rover-v1",
providers=(
SimulationProviderDescriptor(
provider_id="unreal-native",
roles=(
ProviderRole.WORLD,
ProviderRole.PHYSICS,
ProviderRole.STATE,
ProviderRole.SENSOR,
),
capabilities=(
"clock.simulation",
"state.vehicle-pose",
"truth.ground-truth",
"sensor.virtual",
),
),
SimulationProviderDescriptor(
provider_id="unreal-direct-control",
roles=(ProviderRole.CONTROLLER,),
capabilities=("command.rover-speed-steering/v1",),
),
),
clock=SimulationClockDescriptor(
provider_id="unreal-native",
domain="unreal:fixed-step",
),
control_profiles=(ControlProfile.ROVER_SPEED_STEERING_V1,),
)
def test_stock_rover_provider_profile_round_trips_exactly() -> None:
restored = SimulationProviderProfile.from_dict(STOCK_ROVER_PROVIDER_PROFILE.to_dict())
assert restored == STOCK_ROVER_PROVIDER_PROFILE
assert restored.clock.domain == "gazebo:/clock"
assert any(ProviderRole.CONTROLLER in provider.roles for provider in restored.providers)
def test_provider_contract_accepts_unreal_without_changing_canonical_frames() -> None:
profile = SimulationProviderProfile.from_dict(_unreal_profile().to_dict())
assert profile.clock.domain == "unreal:fixed-step"
assert profile.world_frame == "map_enu"
assert profile.body_frame == "base_link_flu"
def test_provider_contract_rejects_missing_clock_capability() -> None:
profile = _unreal_profile()
world = profile.providers[0]
with pytest.raises(SimulationProviderContractError, match="clock.simulation"):
replace(
profile,
providers=(
replace(
world,
capabilities=tuple(
capability
for capability in world.capabilities
if capability != "clock.simulation"
),
),
profile.providers[1],
),
)
def test_provider_contract_rejects_undeclared_command_capability() -> None:
profile = _unreal_profile()
controller = profile.providers[1]
with pytest.raises(SimulationProviderContractError, match="control profiles"):
replace(
profile,
providers=(
profile.providers[0],
replace(controller, capabilities=("transport.custom",)),
),
)
+8
View File
@@ -89,6 +89,14 @@ def _start(store: QualificationRunStore, run_id: str = "run-s1-001") -> Qualific
)
def test_closed_loop_run_accepts_provider_neutral_simulation_clock() -> None:
run = replace(_run(), clock_domain="unreal:fixed-step")
assert run.clock_domain == "unreal:fixed-step"
with pytest.raises(SimulationContractError, match="clock domain"):
replace(run, clock_domain="../../host-clock")
def test_run_manifest_is_immutable_and_runtime_replays_from_events(tmp_path: Path) -> None:
store = QualificationRunStore(tmp_path / "runs")
created = store.create(_run())
@@ -144,8 +144,9 @@ def test_worker_agent_journals_and_idempotently_delivers_virtual_command(
assert accepted == repeated
assert accepted["authority_scope"] == "virtual-only"
assert accepted["delivery"]["provider"] == "px4-ros2-offboard"
assert accepted["delivery"]["armed"] is True
assert accepted["delivery"]["provider_id"] == "px4-ros2-offboard"
assert accepted["delivery"]["controller_ready"] is True
assert accepted["delivery"]["diagnostics"]["armed"] is True
commands = agent.store.list_commands("s1c-command-test")
assert len(commands) == 1
assert commands[0].valid_until_sim_ns - commands[0].issued_at_sim_ns == 250_000_000
+55 -8
View File
@@ -9,6 +9,7 @@ from uuid import uuid4
import pytest
from k1link.simulation.stock_rover import STOCK_ROVER_PROVIDER_PROFILE
from k1link.simulation.worker_gateway import (
REQUEST_SCHEMA,
RESPONSE_SCHEMA,
@@ -20,7 +21,7 @@ from k1link.simulation.worker_gateway import (
def _status() -> dict[str, Any]:
return {
"schema_version": "missioncore.simulation-worker-status/v1",
"schema_version": "missioncore.simulation-worker-status/v2",
"worker_id": "mission-gpu-s1",
"transport": "unix",
"mode": "simulation",
@@ -28,7 +29,8 @@ def _status() -> dict[str, Any]:
"control_available": True,
"active_run_id": None,
"run_state": None,
"provider_ids": [],
"active_provider_ids": [],
"provider_profile": STOCK_ROVER_PROVIDER_PROFILE.to_dict(),
"isolation": {
"network": "loopback-only-netns",
"process_identity": "missioncore",
@@ -44,7 +46,7 @@ def _status() -> dict[str, Any]:
def _command_acceptance() -> dict[str, Any]:
return {
"schema_version": "missioncore.command-acceptance/v1",
"schema_version": "missioncore.command-acceptance/v2",
"run_id": "s1c-command-test",
"command_id": "cmd-001",
"sequence": 1,
@@ -54,11 +56,40 @@ def _command_acceptance() -> dict[str, Any]:
"steering_normalized": -0.25,
"authority_scope": "virtual-only",
"delivery": {
"provider": "px4-ros2-offboard",
"mode": "speed-steering",
"armed": True,
"offboard": True,
"provider_id": "unreal-direct-control",
"control_profile": "rover-speed-steering/v1",
"accepted": True,
"controller_ready": True,
"ttl_expired_count": 0,
"diagnostics": {"armed": True, "offboard": True},
},
}
def _vehicle_state() -> dict[str, Any]:
return {
"schema_version": "missioncore.vehicle-state/v1",
"run_id": "s1c-command-test",
"sequence": 1,
"observed_at_utc": "2026-07-25T10:00:00Z",
"host_monotonic_ns": 1_000_000,
"sim_time_ns": 2_000_000,
"frame_id": "map_enu",
"child_frame_id": "base_link_flu",
"pose": {
"position_m": {"x": 1.0, "y": 2.0, "z": 0.0},
"orientation_xyzw": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0},
},
"source": {
"provider": "unreal-native",
"topic": "missioncore/vehicle-state",
"signal": "ground-truth",
"quality": "diagnostic",
},
"safety": {
"scope": "virtual-only",
"actuator_authority": False,
"navigation_or_safety_accepted": False,
},
}
@@ -158,7 +189,8 @@ def test_unix_worker_gateway_sends_bounded_virtual_rover_command() -> None:
)
thread.join(timeout=2)
assert accepted["delivery"]["offboard"] is True
assert accepted["delivery"]["provider_id"] == "unreal-direct-control"
assert accepted["delivery"]["controller_ready"] is True
assert requests[0]["operation"] == "command"
assert requests[0]["payload"] == {
"run_id": "s1c-command-test",
@@ -168,3 +200,18 @@ def test_unix_worker_gateway_sends_bounded_virtual_rover_command() -> None:
}
finally:
socket_path.unlink(missing_ok=True)
def test_unix_worker_gateway_accepts_provider_neutral_diagnostic_state() -> None:
socket_path = Path(f"/tmp/mc-{uuid4().hex}.sock")
thread, _ = _serve_once(socket_path, _vehicle_state())
gateway = UnixSocketWorkerGateway(socket_path)
try:
state = gateway.live()
thread.join(timeout=2)
assert state["source"]["provider"] == "unreal-native"
assert state["frame_id"] == "map_enu"
finally:
socket_path.unlink(missing_ok=True)