fix(k1): record redacted acceptance failures
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import math
|
||||
import threading
|
||||
import time
|
||||
@@ -8,6 +9,7 @@ from dataclasses import dataclass
|
||||
from typing import Literal, Protocol
|
||||
|
||||
from k1link.device_plugins.xgrids_k1.protocol.application_bootstrap import (
|
||||
ApplicationBootstrapError,
|
||||
LiveDeviceControlBinding,
|
||||
ShadowApplicationBootstrapOrchestrator,
|
||||
)
|
||||
@@ -19,6 +21,7 @@ from k1link.device_plugins.xgrids_k1.protocol.application_publish import (
|
||||
)
|
||||
from k1link.device_plugins.xgrids_k1.protocol.modeling_control import (
|
||||
ModelingAction,
|
||||
ModelingProtocolError,
|
||||
ModelingResponse,
|
||||
correlate_modeling_response,
|
||||
)
|
||||
@@ -130,6 +133,8 @@ class PhysicalAcceptanceDialogueExecutor:
|
||||
self._permit = permit
|
||||
self._bootstrap_complete = False
|
||||
self._command_complete = False
|
||||
self._response_evidence: list[dict[str, object]] = []
|
||||
self._correlation_failure: dict[str, object] | None = None
|
||||
|
||||
def run_bootstrap(
|
||||
self,
|
||||
@@ -151,10 +156,30 @@ class PhysicalAcceptanceDialogueExecutor:
|
||||
)
|
||||
for request in batch:
|
||||
if request.response_required:
|
||||
orchestrator.accept_response(
|
||||
request.response_topic,
|
||||
responses[request.response_topic],
|
||||
payload = responses[request.response_topic]
|
||||
self._record_response_evidence(
|
||||
phase="bootstrap",
|
||||
operation_key=(
|
||||
f"bootstrap:{request.ordinal}:{request.message_type}"
|
||||
),
|
||||
response_topic=request.response_topic,
|
||||
payload=payload,
|
||||
)
|
||||
try:
|
||||
orchestrator.accept_response(request.response_topic, payload)
|
||||
except ApplicationBootstrapError as exc:
|
||||
self._record_correlation_failure(
|
||||
phase="bootstrap",
|
||||
operation_key=(
|
||||
f"bootstrap:{request.ordinal}:{request.message_type}"
|
||||
),
|
||||
response_topic=request.response_topic,
|
||||
reason=str(exc),
|
||||
)
|
||||
raise ApplicationAcceptanceError(
|
||||
"bootstrap response correlation failed for "
|
||||
f"ordinal {request.ordinal} ({request.message_type}): {exc}"
|
||||
) from exc
|
||||
|
||||
binding = orchestrator.binding
|
||||
if binding is None:
|
||||
@@ -173,10 +198,25 @@ class PhysicalAcceptanceDialogueExecutor:
|
||||
[OneShotPublishEnvelope.from_modeling_command(command)],
|
||||
required_response_topics={MODELING_RESPONSE_TOPIC},
|
||||
)
|
||||
return correlate_modeling_response(
|
||||
responses[MODELING_RESPONSE_TOPIC],
|
||||
command.command,
|
||||
payload = responses[MODELING_RESPONSE_TOPIC]
|
||||
self._record_response_evidence(
|
||||
phase="modeling",
|
||||
operation_key=f"modeling:{command.action.name.casefold()}",
|
||||
response_topic=MODELING_RESPONSE_TOPIC,
|
||||
payload=payload,
|
||||
)
|
||||
try:
|
||||
return correlate_modeling_response(payload, command.command)
|
||||
except ModelingProtocolError as exc:
|
||||
self._record_correlation_failure(
|
||||
phase="modeling",
|
||||
operation_key=f"modeling:{command.action.name.casefold()}",
|
||||
response_topic=MODELING_RESPONSE_TOPIC,
|
||||
reason=str(exc),
|
||||
)
|
||||
raise ApplicationAcceptanceError(
|
||||
f"modeling response correlation failed for {command.action.name}: {exc}"
|
||||
) from exc
|
||||
|
||||
def snapshot(self) -> dict[str, object]:
|
||||
return {
|
||||
@@ -184,5 +224,44 @@ class PhysicalAcceptanceDialogueExecutor:
|
||||
"bootstrap_complete": self._bootstrap_complete,
|
||||
"command_complete": self._command_complete,
|
||||
"permit": self._permit.snapshot(),
|
||||
"response_evidence": tuple(dict(item) for item in self._response_evidence),
|
||||
"correlation_failure": (
|
||||
dict(self._correlation_failure)
|
||||
if self._correlation_failure is not None
|
||||
else None
|
||||
),
|
||||
"automatic_retry": False,
|
||||
}
|
||||
|
||||
def _record_response_evidence(
|
||||
self,
|
||||
*,
|
||||
phase: Literal["bootstrap", "modeling"],
|
||||
operation_key: str,
|
||||
response_topic: str,
|
||||
payload: bytes,
|
||||
) -> None:
|
||||
self._response_evidence.append(
|
||||
{
|
||||
"phase": phase,
|
||||
"operation_key": operation_key,
|
||||
"response_topic": response_topic,
|
||||
"payload_bytes": len(payload),
|
||||
"payload_sha256": hashlib.sha256(payload).hexdigest(),
|
||||
}
|
||||
)
|
||||
|
||||
def _record_correlation_failure(
|
||||
self,
|
||||
*,
|
||||
phase: Literal["bootstrap", "modeling"],
|
||||
operation_key: str,
|
||||
response_topic: str,
|
||||
reason: str,
|
||||
) -> None:
|
||||
self._correlation_failure = {
|
||||
"phase": phase,
|
||||
"operation_key": operation_key,
|
||||
"response_topic": response_topic,
|
||||
"reason": reason,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user