wip(k1): checkpoint connection recovery rewrite

Capture the current unreleased K1 connection, recovery, lifecycle, viewer, and test work as a single known-bad baseline for subsequent fixes.
This commit is contained in:
DCCONSTRUCTIONS
2026-08-14 14:57:50 +03:00
parent aff331082f
commit 0ca7316a24
157 changed files with 152962 additions and 4036 deletions
+124 -1
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
import hashlib
from collections.abc import Collection, Sequence
from collections.abc import Callable, Collection, Sequence
import pytest
from pydantic import JsonValue, TypeAdapter
@@ -20,6 +20,9 @@ from k1link.device_plugins.xgrids_k1.protocol.application_bootstrap import (
ShadowApplicationBootstrapOrchestrator,
build_canonical_post_start_observation,
)
from k1link.device_plugins.xgrids_k1.protocol.application_mqtt import (
ApplicationMqttTransportError,
)
from k1link.device_plugins.xgrids_k1.protocol.application_publish import (
OneShotPublishEnvelope,
)
@@ -120,7 +123,19 @@ class SyntheticAcceptanceTransport:
envelopes: Sequence[OneShotPublishEnvelope],
*,
required_response_operation_keys: Collection[str],
dispatch_admission_deadline_reached: Callable[[], bool] | None = None,
dispatch_admission_commit: Callable[[], None] | None = None,
) -> dict[str, bytes]:
if (
dispatch_admission_deadline_reached is not None
and dispatch_admission_deadline_reached()
):
raise ApplicationMqttTransportError(
"control command dispatch deadline expired before publish admission",
reason_code="physical-command-dispatch-deadline-expired",
)
if dispatch_admission_commit is not None:
dispatch_admission_commit()
self.batches.append(tuple(envelope.operation_key for envelope in envelopes))
responses: dict[str, bytes] = {}
modeling_operation = next(
@@ -357,6 +372,114 @@ def test_canonical_session_owns_start_active_scan_stop_and_save_boundary() -> No
TypeAdapter(JsonValue).validate_python(executor.snapshot())
def test_stop_deadline_expiring_during_dispatch_validation_consumes_no_permit_or_publish() -> None:
transport = SyntheticAcceptanceTransport()
executor = PhysicalAcceptanceDialogueExecutor(transport)
authority = ApplicationControlAuthority(openapi_key=APPLICATION_KEY)
orchestrator = ShadowApplicationBootstrapOrchestrator(
authority,
epoch_seconds=1_752_680_000,
timezone_name="Europe/Moscow",
)
binding = executor.run_connection_stage(orchestrator)
executor.run_workspace_entry_stage(
orchestrator,
executor.wait_for_operator_checkpoint("workspace-entered", lambda: True),
)
executor.run_project_prompt_stage(
orchestrator,
executor.wait_for_operator_checkpoint("project-prompt-opened", lambda: True),
)
start_command = ShadowModelingCommand.from_command(
encode_modeling_start(
CommandHeaderIdentity(
device_id=binding.vendor_device_id,
openapi_key=APPLICATION_KEY,
),
project_name="SAFE_PROJECT",
record_mode=RecordMode.RECORD_AND_CALCULATE,
scan_mode=ScanMode.LCC,
mount_type=MountType.HANDHELD,
)
)
executor.execute_canonical_start(
start_command,
build_canonical_post_start_observation(authority, binding),
authority=authority,
binding=binding,
permit=PhysicalAcceptancePermit(_checklist(ModelingAction.START)),
checkpoint=executor.wait_for_operator_checkpoint(
"start-confirmed",
lambda: True,
),
)
executor.maintain_active_until_stop_requested(lambda: True)
stop_command = ShadowModelingCommand.from_command(
encode_modeling_stop(
CommandHeaderIdentity(
device_id=binding.vendor_device_id,
openapi_key=APPLICATION_KEY,
)
)
)
stop_permit = PhysicalAcceptancePermit(_checklist(ModelingAction.STOP))
expired = False
def slow_dispatch_validation() -> None:
nonlocal expired
expired = True
with pytest.raises(ApplicationMqttTransportError) as raised:
executor.execute_canonical_stop(
stop_command,
stop_permit,
dispatch_guard=slow_dispatch_validation,
dispatch_admission_deadline_reached=lambda: expired,
)
assert raised.value.reason_code == "physical-command-dispatch-deadline-expired"
assert stop_permit.snapshot()["consumed"] is False
assert transport.stop_emitted is False
assert executor.snapshot()["stop_attempted"] is False
assert executor.snapshot()["dialogue_stage"] == "stop-requested"
def test_read_only_inspection_publishes_only_ordinal_one_device_info() -> None:
transport = SyntheticAcceptanceTransport()
executor = PhysicalAcceptanceDialogueExecutor(transport)
orchestrator = ShadowApplicationBootstrapOrchestrator(
ApplicationControlAuthority(openapi_key=APPLICATION_KEY),
epoch_seconds=1_752_680_000,
timezone_name="Europe/Moscow",
)
binding = executor.run_read_only_inspection_stage(orchestrator)
assert binding.vendor_device_id == VENDOR_DEVICE_ID
assert transport.batches == [("bootstrap:1:DeviceInfoRequest",)]
outbound = {operation for batch in transport.batches for operation in batch}
assert outbound == {"bootstrap:1:DeviceInfoRequest"}
assert not any("DeviceConfig" in operation for operation in outbound)
assert not any("ModelingStatus" in operation for operation in outbound)
assert not any(operation.startswith("modeling:") for operation in outbound)
assert executor.snapshot()["dialogue_stage"] == "inspection-ready"
completed = executor.complete_connection_stage(
orchestrator,
expected_binding=binding,
)
assert completed == binding
assert [len(batch) for batch in transport.batches] == [1, 5]
assert transport.batches[1] == (
"bootstrap:2:ModelingStatusRequest",
"bootstrap:3:GetRtkAdvanceRequest",
"bootstrap:4:DeviceConfigRequest",
"bootstrap:5:DeviceInfoRequest",
"bootstrap:6:GetRtkAdvanceRequest",
)
def test_start_initialization_wait_has_fail_closed_watchdog() -> None:
class NeverInitializedTransport(SyntheticAcceptanceTransport):
def scan_initialization_complete(self, _binding: object) -> bool: