fix(k1): allow explicit reset after terminal start fault

This commit is contained in:
DCCONSTRUCTIONS
2026-08-14 21:01:52 +03:00
parent 72a2f37c03
commit 986ea69610
2 changed files with 86 additions and 0 deletions
@@ -8477,6 +8477,7 @@ class XgridsK1CompatibilityService:
acquisition_id=(
acquisition.acquisition_id if acquisition is not None else None
),
allow_terminal_failure_retirement=True,
)
)
self._supersede_control_bootstrap_continuation()
@@ -27415,6 +27416,7 @@ class XgridsK1CompatibilityService:
self,
*,
acquisition_id: str | None,
allow_terminal_failure_retirement: bool = False,
) -> tuple[_LocalStopRetirementDisposition, str | None]:
"""Cancel the local worker under the exact publish fence.
@@ -27450,6 +27452,24 @@ class XgridsK1CompatibilityService:
)
self._application_control_session.close()
retired_control = dict(self._application_control_session.snapshot())
if (
allow_terminal_failure_retirement
and retired_control.get("state") == "failed"
):
# ``can_open`` deliberately remains false after an ambiguous
# START/STOP failure even when the socket worker has already
# exited. That is the correct admission policy for an
# ordinary retry, but an explicit scenario reset is a
# different operation: retire only the dead local owner while
# the durable physical ledger below preserves the ambiguous
# device outcome. ``retire_for_network_change`` still checks
# that the thread and transport are actually gone, so a live
# publisher cannot be detached by this path.
with suppress(AttributeError, RuntimeError):
self._retire_application_control_for_network_change(
allow_terminal_failure=True,
)
retired_control = dict(self._application_control_session.snapshot())
if (
retired_control.get("state")
not in {"idle", "completed", "closed", "failed"}
@@ -1722,6 +1722,72 @@ def test_terminal_cleanup_pending_is_locally_sealed_by_mode_reset(
assert reset["connection_scenario_reset"]["network_write_performed"] is False
def test_terminal_ambiguous_start_failure_can_be_locally_reset_after_worker_exit(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service = _service(monkeypatch, tmp_path)
_seed_resettable_physical_record(service)
acquisition = AcquisitionRecord(
acquisition_id="reset-preflight-acquisition-0001",
device_id="device-reset-terminal-start-fault",
device_session_id="device-session-reset-terminal-start-fault",
compatibility_profile_id=("xgrids.lixelkity-k1.fw-3.0.2.local-network.v2"),
control_mode="plugin-commanded",
requested_streams=("points",),
target_host="192.168.68.51",
duration_seconds=None,
evidence_policy="required",
)
acquisition.transition(
"failed",
message_code="acquisition.start.device_outcome_unknown",
result={
"receiver_stopped": False,
"device_start": "outcome-unknown",
},
)
control = service._application_control_session # noqa: SLF001
with control._lock: # noqa: SLF001
control._set_phase_locked("failed") # noqa: SLF001
control._failure = { # noqa: SLF001
"reason_code": "device_status_fault",
"safe_to_retry": False,
"modeling_command_attempted": True,
"outcome_unknown": True,
}
control._thread = None # noqa: SLF001
control._transport = None # noqa: SLF001
with service._lock: # noqa: SLF001
service._acquisition = acquisition # noqa: SLF001
before = control.snapshot()
assert before["state"] == "failed"
assert before["can_open"] is False
reset = service.select_connection_mode(
_reset(
mode="bridge",
revision=0,
reset_id="op-reset-terminal-ambiguous-start-01",
)
)
assert reset["desired_connection_mode_revision"] == 1
assert reset["connection_scenario_reset"]["local_session_closed"] is True
assert reset["connection_scenario_reset"]["physical_disposition"] == (
"operator-retired-outcome-unknown"
)
assert reset["connection_scenario_reset"]["device_command_performed"] is False
assert reset["connection_scenario_reset"]["network_write_performed"] is False
assert reset["application_control_session"]["state"] == "idle"
assert reset["acquisition"]["state"] == "failed"
assert reset["acquisition"]["cleanup_pending"] is False
physical = service._physical_command_ledger.snapshot().record # noqa: SLF001
assert physical is not None
assert physical.resolution == "operator-retired-outcome-unknown"
def test_terminal_cleanup_failure_stays_visible_and_exact_reset_can_retry(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,