Files
NODEDC_MISSION_CORE/plugins/xgrids-k1/frontend/src/lifecycle.ts
T

1819 lines
63 KiB
TypeScript

import type { RuntimePhase, SourceMode as RuntimeSourceMode } from "@mission-core/plugin-sdk";
import type {
AcquisitionState,
BleDevice,
XgridsApplicationControlPhase,
XgridsAcquisition,
XgridsConnectionAttempt,
XgridsK1State,
XgridsOperation,
ReopenRetiredPhysicalReconciliationRequest,
XgridsConnectionMode,
XgridsConnectionReconfiguration,
XgridsConnectionPolicyAction,
XgridsConnectionPolicyDecision,
} from "./api";
const TERMINAL_ACQUISITION_STATES = new Set<AcquisitionState>([
"completed",
"failed",
"aborted",
"interrupted",
]);
const FAILED_OPERATION_STATUSES = new Set([
"failed",
"cancelled",
"timed_out",
"interrupted",
]);
export type LiveStartPlan = "prepare" | "resume-prepared" | "already-running" | "blocked";
export type LocalReceiverStopPlan =
| { kind: "acquisition"; acquisitionId: string }
| { kind: "compatibility" };
export type ControlSessionEntryPlan =
| "open"
| "continue"
| "failed"
| "duplicate-open";
export function controlSessionEntryPlan(
phase: XgridsApplicationControlPhase,
openedByCurrentOperatorAction: boolean,
canOpen: boolean,
): ControlSessionEntryPlan {
if (phase === "failed") {
return !openedByCurrentOperatorAction && canOpen ? "open" : "failed";
}
if (["idle", "closed", "completed"].includes(phase)) {
return openedByCurrentOperatorAction ? "duplicate-open" : "open";
}
return "continue";
}
export function isTerminalAcquisitionState(
state: AcquisitionState | null | undefined,
): boolean {
return state ? TERMINAL_ACQUISITION_STATES.has(state) : false;
}
/**
* Select the exact local runtime cleanup target without inheriting a retained
* terminal acquisition into a replay session. Terminal acquisition state is
* actionable only while its backend-owned local cleanup remains pending.
*/
export function localReceiverStopPlan(
state: XgridsK1State | null | undefined,
): LocalReceiverStopPlan {
const acquisition = state?.acquisition;
const acquisitionId = acquisition?.acquisition_id?.trim();
const liveOrIdleRuntime = state?.source_mode === "live" || state?.source_mode === "idle";
if (
acquisition
&& acquisitionId
&& liveOrIdleRuntime
&& (
!isTerminalAcquisitionState(acquisition.state)
|| acquisition.cleanup_pending === true
)
) {
return { kind: "acquisition", acquisitionId };
}
return { kind: "compatibility" };
}
export function isProvenLocalReceiverInactive(
state: XgridsK1State | null | undefined,
): state is XgridsK1State {
const acquisition = state?.acquisition;
const acquisitionReleased = Boolean(
acquisition
&& isTerminalAcquisitionState(acquisition.state)
&& acquisition.cleanup_pending === false,
);
return Boolean(
state?.source_mode === "idle"
&& (!acquisition || acquisitionReleased),
);
}
export function isReleasedTerminalAcquisitionFailure(
state: XgridsK1State | null | undefined,
): boolean {
const controlState = state?.application_control_session?.state;
const acquisition = state?.acquisition;
return Boolean(
state?.source_mode === "idle"
&& acquisition
&& ["failed", "interrupted"].includes(acquisition.state)
&& acquisition.cleanup_pending === false
&& [
"idle",
"connection-ready",
"active-recovery-requested",
"scanning",
"completed",
"closed",
].includes(controlState ?? ""),
);
}
export function shouldSurfaceRuntimeActionError(
action: string,
state: XgridsK1State | null | undefined,
): boolean {
return !(
["control", "live", "stop", "abort"].includes(action)
&& isReleasedTerminalAcquisitionFailure(state)
);
}
export function shouldRenderSpatialControls(
state: XgridsK1State | null | undefined,
): boolean {
const acquisition = state?.acquisition;
if (!acquisition || state?.source_mode === "replay") return false;
return (
!isTerminalAcquisitionState(acquisition.state) ||
acquisition.cleanup_pending === true ||
requiresCanonicalStopAfterTerminalLocalFailure(state)
);
}
export function requiresCanonicalStopAfterTerminalLocalFailure(
state: XgridsK1State | null | undefined,
): boolean {
const acquisition = state?.acquisition;
const control = state?.application_control_session;
return Boolean(
acquisition
&& isTerminalAcquisitionState(acquisition.state)
&& isSoftwareCommandedAcquisition(state)
&& control?.state === "scanning"
&& control.can_stop === true
);
}
export interface PhysicalStopIntentCheckpoint {
snapshotRuntimeId: string;
acquisitionId: string;
deviceId: string;
deviceSessionId: string;
controlSessionGeneration: number;
controlStateRevision: number;
}
function positiveInteger(value: unknown): value is number {
return Number.isSafeInteger(value) && (value as number) >= 1;
}
/**
* Capture the exact backend authority consumed by one physical STOP intent.
* Snapshot identity is retained when available, while the control-session CAS
* is mandatory: a presentation-only error dismissal must never manufacture a
* fresh command intent against the same control checkpoint.
*/
export function physicalStopIntentCheckpoint(
state: XgridsK1State | null | undefined,
): PhysicalStopIntentCheckpoint | null {
const control = state?.application_control_session;
const acquisition = state?.acquisition;
const acquisitionId = acquisition?.acquisition_id?.trim();
const deviceId = acquisition?.device_id?.trim();
const deviceSessionId = acquisition?.device_session_id?.trim();
const runtimeId = state?.snapshot_runtime_id?.trim();
if (
!control
|| !acquisitionId
|| !deviceId
|| !deviceSessionId
|| !runtimeId
|| !positiveInteger(control.session_generation)
|| !positiveInteger(control.state_revision)
|| control.state !== "scanning"
|| control.can_stop !== true
|| !connectionPolicyAllows(state, "stop-acquisition")
) return null;
return {
snapshotRuntimeId: runtimeId,
acquisitionId,
deviceId,
deviceSessionId,
controlSessionGeneration: control.session_generation,
controlStateRevision: control.state_revision,
};
}
/**
* A spent physical STOP may be released only by an already-accepted exact
* STOP-authoritative runtime replacement, a distinct acquisition target, or
* an exact control-session CAS transition. A same-runtime polling snapshot
* whose target and control CAS stayed fixed is not new physical-command
* authority.
*/
export function authoritativeStateSupersedesPhysicalStopIntent(
spent: PhysicalStopIntentCheckpoint | null | undefined,
state: XgridsK1State | null | undefined,
): boolean {
if (!spent) return false;
const current = physicalStopIntentCheckpoint(state);
if (!current) return false;
const sameRuntime = current.snapshotRuntimeId === spent.snapshotRuntimeId;
if (!sameRuntime) {
// Snapshot ordering is resolved before this helper is called. A different
// accepted runtime with a complete exact STOP gate is fresh authority even
// when its process-local CAS counters restarted.
return true;
}
const sameTarget =
current.acquisitionId === spent.acquisitionId
&& current.deviceId === spent.deviceId
&& current.deviceSessionId === spent.deviceSessionId;
if (!sameTarget) {
// A newly accepted acquisition/device/session tuple is a distinct command
// target. The caller has already admitted this state monotonically.
return true;
}
const controlCasAdvanced =
current.controlSessionGeneration > spent.controlSessionGeneration
|| (
current.controlSessionGeneration === spent.controlSessionGeneration
&& current.controlStateRevision > spent.controlStateRevision
);
if (!controlCasAdvanced) return false;
// Within one runtime the exact control CAS must advance; observation-only
// polls remain locked regardless of their snapshot observation revision.
return true;
}
/**
* Admit one physical STOP button only from the exact current control proof.
* A failed action spends that browser intent independently of its dismissible
* presentation error: the operator may still finish the host receiver
* locally, but the UI must not create a fresh physical STOP mutation from the
* same accepted snapshot/control CAS.
*/
export function canIssueCanonicalStop(
state: XgridsK1State | null | undefined,
physicalStopIntentSpent: boolean | null | undefined,
): boolean {
const control = state?.application_control_session;
return Boolean(
!physicalStopIntentSpent
&& physicalStopIntentCheckpoint(state)
&& control?.state === "scanning"
&& control.can_stop === true
&& connectionPolicyAllows(state, "stop-acquisition"),
);
}
/**
* A successful read-only Verify may truthfully end in SCANNING rather than
* connection-ready. That state grants exactly one explicit STOP, never START
* or provisioning authority.
*/
export function isRecoveredPhysicalScanning(
state: XgridsK1State | null | undefined,
expectedMode?: XgridsConnectionMode,
): boolean {
const control = state?.application_control_session;
const physical = control?.physical_command ?? state?.physical_command;
const mode = state?.active_connection_mode ?? state?.connection_mode;
return Boolean(
control?.state === "scanning"
&& control.can_stop === true
&& physical?.requires_reconciliation !== true
&& physical?.resolved_active_recovery_required === true
&& physical.observed_session_state === "scanning"
&& (!expectedMode || mode === expectedMode)
&& (!expectedMode || currentAppliedConnectionTopology(state, expectedMode)?.status === "active")
);
}
/**
* A STOP that was accepted before the host path disappeared is completed by
* the backend from its durable command ledger. The connection screen must
* wait for that cleanup instead of turning the condition into another device
* action (Scan, START, STOP, or Wi-Fi provisioning).
*/
export function isPhysicalStopRecoverySettling(
state: XgridsK1State | null | undefined,
): boolean {
const control = state?.application_control_session;
const physical = control?.physical_command ?? state?.physical_command;
const record = physical?.record;
const action = record?.action;
const resolution = record?.resolution;
const acquisition = state?.acquisition;
const acquisitionStillSettling = Boolean(
acquisition
&& (
!isTerminalAcquisitionState(acquisition.state)
|| acquisition.cleanup_pending === true
),
);
const stopOperationStillSettling = Boolean(
state?.operations?.some((operation) =>
operation.action === "acquisition.stop"
&& ["accepted", "running", "operator_action_required"].includes(operation.status)
),
);
return Boolean(
action === "stop"
&& resolution !== "stop-standby-observed"
&& (physical?.requires_reconciliation === true || physical?.status === "unresolved")
&& (acquisitionStillSettling || stopOperationStillSettling),
);
}
export function recoverableAcquisition(
state: XgridsK1State | null | undefined,
): XgridsAcquisition | null {
const acquisition = state?.acquisition;
return acquisition && !isTerminalAcquisitionState(acquisition.state) ? acquisition : null;
}
export function isConfirmedLiveState(state: XgridsK1State | null | undefined): boolean {
return Boolean(
state?.source_mode === "live"
&& state.acquisition?.state === "acquiring"
&& hasAuthoritativeData(state),
);
}
export function isSourceRuntimeBusy(state: XgridsK1State | null | undefined): boolean {
return state?.source_mode === "live" || state?.source_mode === "replay";
}
export function isVendorWriteCapable(
state: XgridsK1State | null | undefined,
): boolean {
return (
state?.compatibility?.vendor_writes_enabled === true &&
state.compatibility.permitted_mode === "active-control"
);
}
export function isSoftwareCommandedAcquisition(
state: XgridsK1State | null | undefined,
): boolean {
return isVendorWriteCapable(state) && state?.acquisition?.control_mode === "plugin-commanded";
}
export function confirmedRuntimeSourceMode(
state: XgridsK1State | null | undefined,
): RuntimeSourceMode {
if (state?.source_mode === "replay") return "replay";
if (isConfirmedLiveState(state)) return "live";
return "idle";
}
export function effectiveAcquisition(
state: XgridsK1State | null | undefined,
): XgridsAcquisition | null {
if (state?.source_mode === "replay") return null;
return state?.acquisition ?? null;
}
export function liveStartPlan(state: XgridsK1State | null | undefined): LiveStartPlan {
if (state?.source_mode === "replay") return "blocked";
const acquisition = recoverableAcquisition(state);
if (!acquisition) return state?.source_mode === "live" ? "blocked" : "prepare";
if (acquisition.state === "prepared") return "resume-prepared";
if (
acquisition.state === "starting" ||
acquisition.state === "awaiting_external_start" ||
acquisition.state === "acquiring"
) {
return "already-running";
}
return "blocked";
}
export function normalizeRuntimePhase(
state: XgridsK1State | null | undefined,
): RuntimePhase {
const phase = state?.phase;
const acquisitionState = effectiveAcquisition(state)?.state;
const releasedFailure = isReleasedTerminalAcquisitionFailure(state);
if (
!releasedFailure
&& (acquisitionState === "failed" || acquisitionState === "interrupted")
) return "error";
if (acquisitionState === "awaiting_external_start" || acquisitionState === "starting") {
return "starting";
}
if (acquisitionState === "acquiring") {
return isConfirmedLiveState(state) ? "streaming" : "starting";
}
if (
acquisitionState === "awaiting_external_stop" ||
acquisitionState === "stopping" ||
acquisitionState === "finalizing"
) {
return "stopping";
}
if (acquisitionState === "prepared") {
const topology = currentAppliedConnectionTopology(state);
return topology && topology.status !== "configured-offline"
? "connected"
: "configuring";
}
if (phase === "error") return releasedFailure ? "idle" : "error";
if (phase === "connected") {
const topology = currentAppliedConnectionTopology(state);
return topology && topology.status !== "configured-offline"
? "connected"
: "configuring";
}
if (phase === "starting_live") return "starting";
if (phase === "live") return isConfirmedLiveState(state) ? "streaming" : "starting";
if (phase === "replay") return "replaying";
if (phase === "stopping") return "stopping";
if (["scanning", "device_selected", "provisioning", "connecting"].includes(phase ?? "")) {
return "configuring";
}
return "idle";
}
export function spatialSourceId(
state: XgridsK1State | null | undefined,
sourceUrl: string,
): string | null {
if (!sourceUrl) return null;
if (state?.source_mode === "replay") return `replay:${sourceUrl}`;
return state?.acquisition?.acquisition_id ?? sourceUrl;
}
export function sourceStatusLabel(state: XgridsK1State | null | undefined): string {
if (state?.source_mode === "replay") return "Повтор записи";
if (isConfirmedLiveState(state)) return "Реальное время · данные подтверждены";
if (state?.source_mode === "live") {
if (state.acquisition?.state === "failed" || state.phase === "error") {
return "Ошибка локального приёмника";
}
if (state.connection_supervisor?.observed.data_plane.state === "lost") {
return "Поток данных потерян";
}
if (state.connection_supervisor?.observed.data_plane.state === "stalled") {
return "Поток данных нестабилен";
}
return "Ожидание реальных данных";
}
if (state?.acquisition?.state === "prepared") return "Приём подготовлен";
return "Ожидание";
}
export function operationByIdempotencyKey(
state: XgridsK1State | null | undefined,
action: string,
idempotencyKey: string | null | undefined,
): XgridsOperation | null {
if (!idempotencyKey) return null;
return (
[...(state?.operations ?? [])]
.reverse()
.find(
(operation) =>
operation.action === action && operation.idempotency_key === idempotencyKey,
) ?? null
);
}
export function operationNeedsReconciliation(
operation: XgridsOperation | null | undefined,
): boolean {
if (!operation || !FAILED_OPERATION_STATUSES.has(operation.status)) return false;
return operation.error?.safe_to_retry !== true;
}
export function operationAllowsFreshProvisioningIntent(
operation: XgridsOperation | null | undefined,
): boolean {
return Boolean(
operation
&& FAILED_OPERATION_STATUSES.has(operation.status)
&& operation.error?.safe_to_retry === true
&& operation.error.side_effect_status === "none",
);
}
const FRESH_CANDIDATE_RETRY_REASON_CODES = new Set([
"BleakDeviceNotFoundError",
"network-provision-candidate-not-fresh",
"network-provision-candidate-changed",
"network-provision-discovery-generation-conflict",
]);
/**
* These failures are proven pre-write rejections caused only by an expired
* Bluetooth capture. One explicit network-submit click may refresh discovery
* once and then continue with the exact returned generation. A second failure
* is terminal for that click; this predicate never authorizes an unbounded
* retry or a repeat after an ambiguous/device-write outcome.
*/
export function provisioningFailureRequiresFreshCandidate(
reasonCode: string | null | undefined,
): boolean {
return typeof reasonCode === "string"
&& FRESH_CANDIDATE_RETRY_REASON_CODES.has(reasonCode);
}
export function readOnlyVerificationClearedReconciliation(
previousState: XgridsK1State | null | undefined,
nextState: XgridsK1State | null | undefined,
verifiedDeviceId: string | null | undefined,
): boolean {
const previousFence = previousState?.network_write_reconciliation;
const previousOperationId = previousFence?.operation_id?.trim();
const nextLedger = nextState?.network_mutation_ledger;
if (
!verifiedDeviceId
|| !previousOperationId
|| transportRefEquivalenceKey(previousFence?.transport_ref)
!== transportRefEquivalenceKey(verifiedDeviceId)
// An omitted field or a replacement unresolved fence is never proof that
// this durable operation was reconciled.
|| nextState?.network_write_reconciliation !== null
|| !nextLedger
|| nextLedger.mutation_allowed !== true
) {
return false;
}
const sameOperationResolved = Boolean(
nextLedger.status === "resolved"
&& nextLedger.operation_id === previousOperationId
&& nextLedger.stage === "resolved"
&& nextLedger.resolution !== null,
);
const operationExplicitlyAbsent = Boolean(
nextLedger.status === "empty"
&& nextLedger.operation_id === null
&& nextLedger.stage === null,
);
return sameOperationResolved || operationExplicitlyAbsent;
}
export function provisioningCandidateById(
devices: readonly BleDevice[],
selectedDeviceId: string,
): BleDevice | null {
const selectedKey = transportRefEquivalenceKey(selectedDeviceId);
if (!selectedKey) return null;
return devices.find(
(device) => transportRefEquivalenceKey(device.device_id) === selectedKey,
) ?? null;
}
export function currentDeviceTransportRef(
state: XgridsK1State | null | undefined,
): string | null {
const recoveryRef = state?.current_device_recovery?.transport_ref?.trim();
if (recoveryRef) return recoveryRef;
const selectedRef = state?.selected_device_id?.trim();
return selectedRef || null;
}
export interface LocallyInitiatedBleSessionTarget {
transportRef: string;
connectionMode: XgridsConnectionMode;
deviceSessionId: string;
key: string;
}
export interface LocalBleSessionBindingConstraints {
/** Bind only the exact session accepted by the explicit connect response. */
requiredSessionKey?: string | null;
}
export function bleSessionTargetForTransport(
state: XgridsK1State | null | undefined,
transportRef: string | null | undefined,
selectedConnectionMode: XgridsConnectionMode,
): LocallyInitiatedBleSessionTarget | null {
const expectedTransportRef = transportRef?.trim();
const backendTransportRef = state?.current_device_recovery?.transport_ref?.trim()
|| state?.selected_device_id?.trim();
const connectionMode = state?.current_device_recovery?.connection_mode
?? state?.connection_mode;
const deviceSessionId = state?.device_session?.device_session_id?.trim();
if (
!expectedTransportRef
|| !backendTransportRef
|| expectedTransportRef !== backendTransportRef
|| !deviceSessionId
|| connectionMode !== selectedConnectionMode
) {
return null;
}
return {
transportRef: backendTransportRef,
connectionMode,
deviceSessionId,
key: `${deviceSessionId}:${connectionMode}:${backendTransportRef}`,
};
}
export function acceptedBleSessionKeyAfterConnect(
state: XgridsK1State | null | undefined,
transportRef: string | null | undefined,
selectedConnectionMode: XgridsConnectionMode,
sessionKeyBeforeConnect: string | null | undefined,
): string | null {
const target = bleSessionTargetForTransport(
state,
transportRef,
selectedConnectionMode,
);
if (!target || target.key === sessionKeyBeforeConnect) return null;
return target.key;
}
/**
* Bind backend session state only to a connection initiated by this UI
* instance. A browser refresh has no local device id and therefore never
* turns an existing backend session into an implicit operator selection.
*/
export function locallyInitiatedBleSessionTarget(
state: XgridsK1State | null | undefined,
locallyInitiatedDeviceId: string | null | undefined,
selectedConnectionMode: XgridsConnectionMode,
constraints: LocalBleSessionBindingConstraints = {},
): LocallyInitiatedBleSessionTarget | null {
const localRef = locallyInitiatedDeviceId?.trim();
if (!localRef) return null;
const target = bleSessionTargetForTransport(
state,
localRef,
selectedConnectionMode,
);
if (!target) return null;
if (
constraints.requiredSessionKey
&& constraints.requiredSessionKey !== target.key
) return null;
return target;
}
export interface RetainedBleRecoveryTarget {
transportRef: string;
connectionMode: NonNullable<XgridsK1State["connection_mode"]> | null;
gattValidatedRecently: boolean;
}
export function retainedBleRecoveryTarget(
state: XgridsK1State | null | undefined,
): RetainedBleRecoveryTarget | null {
const recovery = state?.current_device_recovery;
const transportRef = recovery?.transport_ref?.trim();
if (
!transportRef
|| recovery?.handle_retained !== true
|| recovery.advertised_now === true
) {
return null;
}
return {
transportRef,
connectionMode: recovery.connection_mode ?? null,
gattValidatedRecently: recovery.gatt_validated_recently === true,
};
}
export function connectionPolicyDecision(
state: XgridsK1State | null | undefined,
action: XgridsConnectionPolicyAction,
): XgridsConnectionPolicyDecision | null {
const policy = state?.connection_policy;
if (
policy?.schema_version !== "missioncore.xgrids-k1-connection-policy/v1"
|| policy.facts.retained_context_is_presence !== false
) {
return null;
}
return policy.actions[action] ?? null;
}
export function connectionPolicyAllows(
state: XgridsK1State | null | undefined,
action: XgridsConnectionPolicyAction,
): boolean {
const decision = connectionPolicyDecision(state, action);
return Boolean(
decision?.allowed === true
&& decision.automatic_retry === false
&& state?.connection_policy?.allowed_actions.includes(action),
);
}
export interface ProvisioningNetworkStepDisclosure {
/** A concrete K1 has been admitted or selected by this operator flow. */
deviceExplicitlySelectedOrAdmitted: boolean;
/** A network/connection intent, rather than discovery alone, has started. */
networkIntentStarted: boolean;
}
/**
* Keep Bluetooth discovery entirely inside step 02.
*
* Raw controller settlement is deliberately not an input: an old scan,
* reconfiguration, retirement, or physical-cleanup promise may still be
* unwinding after its authority has gone stale, but that does not mean the
* operator has reached the network step for the current device choice.
*/
export function shouldRevealProvisioningNetworkStep({
deviceExplicitlySelectedOrAdmitted,
networkIntentStarted,
}: ProvisioningNetworkStepDisclosure): boolean {
return deviceExplicitlySelectedOrAdmitted || networkIntentStarted;
}
export function activeConnectionReconfiguration(
state: XgridsK1State | null | undefined,
): XgridsConnectionReconfiguration | null {
const reconfiguration = state?.connection_reconfiguration;
if (
!reconfiguration
|| reconfiguration.schema_version
!== "missioncore.xgrids-k1-connection-reconfiguration/v1"
|| reconfiguration.intent === null
|| reconfiguration.status === "idle"
) {
return null;
}
return reconfiguration;
}
/**
* A network-change intent is pinned to the exact device and mode which were
* current when the operator opened it. Selecting another advertisement must
* not turn that intent into a different-device network write.
*/
export function reconfigurationAllowsFreshDevice(
reconfiguration: XgridsConnectionReconfiguration | null,
deviceId: string,
connectionMode: XgridsConnectionMode,
): boolean {
if (!reconfiguration || !deviceId.trim()) return false;
if (
connectionMode !== "bridge"
|| reconfiguration.required_connection_mode !== "bridge"
) return false;
if (reconfiguration.intent === "select-device") return true;
return Boolean(
reconfiguration.intent === "change-network"
&& transportRefEquivalenceKey(reconfiguration.required_transport_ref)
=== transportRefEquivalenceKey(deviceId)
&& reconfiguration.required_connection_mode === connectionMode,
);
}
export function readOnlyObservationShowsNetworkUnavailable(
state: XgridsK1State | null | undefined,
): boolean {
const verification = state?.connection_verification;
if (!verification) return false;
return [
"device-network-applied-host-failed",
"host-route-mismatch",
"endpoint-unreachable",
"unreachable",
].includes(verification.status) || Boolean(
verification.lease_state === "configured-unverified"
&& verification.network_reachability === "unreachable",
) || Boolean(
verification.status === "device-network-applied"
&& verification.lease_state === "configured-unverified"
&& verification.reason_code === "endpoint-target-unconfigured",
);
}
const READ_ONLY_NETWORK_UNAVAILABLE_REASON_CODES = new Set([
"connection-verify-address-unavailable",
"connection-verify-connection-missing",
"connection-verify-route-mismatch",
"connection-verify-mqtt-unreachable",
"configured-endpoint-unavailable",
"endpoint-target-unconfigured",
]);
/**
* Only failures which prove that the selected device cannot use the current
* Bridge topology may lead from read-only adoption to explicit credentials.
* BLE, identity, lifecycle and CAS failures deliberately stay outside this
* allowlist.
*/
export function readOnlyFailureShowsNetworkUnavailable(
reasonCode: string | null | undefined,
): boolean {
return typeof reasonCode === "string"
&& READ_ONLY_NETWORK_UNAVAILABLE_REASON_CODES.has(reasonCode);
}
/**
* Preserve the stronger BLE observation across refreshes and later scan
* failures: this saved Bridge needs network setup, not another old-address
* reconnect attempt.
*/
export function savedBridgeRequiresNetworkSetup(
state: XgridsK1State | null | undefined,
): boolean {
if (
state?.connection_verification?.reason_code
=== "connection-verify-address-unavailable"
) return true;
const operation = state?.last_operation;
return Boolean(
operation?.action === "connection.verify"
&& operation.status === "failed"
&& operation.error?.code === "connection-verify-address-unavailable",
);
}
export function requiresReadOnlyPhysicalRecovery(
state: XgridsK1State | null | undefined,
): boolean {
const control = state?.application_control_session;
const physical = control?.physical_command ?? state?.physical_command;
return Boolean(
// The coordinator folds unresolved commands and resolved SCAN_OVER into
// requires_reconciliation, while resolved active and explicitly reopened
// rows remain read-only recovery through the separate active flag.
physical?.requires_reconciliation === true
|| physical?.resolved_active_recovery_required === true,
);
}
export interface ReadOnlyPhysicalRecoveryBinding {
deviceId: string;
connectionMode: XgridsConnectionMode;
}
export interface TrustedConnectionBinding {
deviceId: string;
connectionMode: XgridsConnectionMode;
}
/**
* CoreBluetooth UUID text is case-insensitive. Keep the original spelling for
* display and exact CAS payloads, but use this key whenever refs are compared.
*/
export function transportRefEquivalenceKey(
transportRef: string | null | undefined,
): string {
return transportRef?.trim().toLowerCase() ?? "";
}
/**
* Resolve the exact K1 remembered by durable/backend-owned state. This is
* selection context only: it can authorize bounded observation, but never a
* device write without the separate connection policy and an explicit click.
*/
export function trustedConnectionBinding(
state: XgridsK1State | null | undefined,
): TrustedConnectionBinding | null {
const physical = readOnlyPhysicalRecoveryBinding(state);
if (physical) return physical;
const retiredTransportRefs = retiredPhysicalTransportRefs(state);
const semanticRecord = state?.semantic_topology_store?.record;
const semanticDeviceId = semanticRecord?.transport_ref?.trim();
const semanticMode = semanticRecord?.connection_mode;
if (
semanticDeviceId
&& !retiredTransportRefs.has(transportRefEquivalenceKey(semanticDeviceId))
&& isConnectionMode(semanticMode)
) {
return { deviceId: semanticDeviceId, connectionMode: semanticMode };
}
const recovery = state?.current_device_recovery;
const recoveryDeviceId = recovery?.transport_ref?.trim();
const recoveryMode = recovery?.connection_mode;
if (
recoveryDeviceId
&& !retiredTransportRefs.has(transportRefEquivalenceKey(recoveryDeviceId))
&& isConnectionMode(recoveryMode)
) {
return { deviceId: recoveryDeviceId, connectionMode: recoveryMode };
}
return null;
}
/** Transport refs explicitly retired by the operator must never rehydrate. */
export function retiredPhysicalTransportRefs(
state: XgridsK1State | null | undefined,
): ReadonlySet<string> {
const policyRetiredRefs = state?.connection_policy?.facts.retired_transport_refs;
if (Array.isArray(policyRetiredRefs)) {
// This backend projection is the authoritative *active* deny-list. The
// durable ledger keeps historical retirement audits even after an explicit
// reconciliation reopen, so unioning every old audit would make a safely
// reopened UUID impossible to use forever.
return new Set(
policyRetiredRefs.map(transportRefEquivalenceKey).filter(Boolean),
);
}
const record = state?.physical_command?.record;
const retired = new Set<string>();
if (!record) return retired;
const retirements = Array.isArray(record.operator_retirements)
? record.operator_retirements
: [];
for (const candidate of retirements) {
if (!candidate || typeof candidate !== "object") continue;
const transportRef = "retired_transport_ref" in candidate
&& typeof candidate.retired_transport_ref === "string"
? candidate.retired_transport_ref.trim()
: "";
if (transportRef) retired.add(transportRefEquivalenceKey(transportRef));
}
if (record.resolution === "operator-retired-outcome-unknown") {
const connection = record.connection;
if (connection && typeof connection === "object") {
const transportRef = "transport_ref" in connection
&& typeof connection.transport_ref === "string"
? connection.transport_ref.trim()
: "";
if (transportRef) retired.add(transportRefEquivalenceKey(transportRef));
}
}
return retired;
}
export interface RetiredPhysicalReopenAuthority {
expectedRevision: number;
expectedRetirementId: string;
expectedTransportRef: string;
expectedDiscoveryGeneration: number;
expectedDesiredMode: XgridsConnectionMode;
expectedDesiredModeRevision: number;
}
/**
* Admit the local-only reopen affordance only for the exact fresh retired row
* and exact backend-projected ledger/discovery CAS. This helper grants no GATT
* or write authority; the separately explicit Verify remains server-fenced.
*/
export function retiredPhysicalReopenAuthority(
state: XgridsK1State | null | undefined,
candidateTransportRef: string,
connectionMode: NonNullable<XgridsK1State["connection_mode"]>,
): RetiredPhysicalReopenAuthority | null {
const projection = state?.physical_command?.operator_reconciliation_reopen;
const expectedRevision = projection?.expected_revision;
const expectedRetirementId = projection?.expected_retirement_id?.trim() ?? "";
const expectedTransportRef = projection?.expected_transport_ref?.trim() ?? "";
const expectedDiscoveryGeneration = projection?.expected_discovery_generation;
const expectedDesiredMode = projection?.expected_desired_mode;
const expectedDesiredModeRevision =
projection?.expected_desired_mode_revision;
const candidateKey = transportRefEquivalenceKey(candidateTransportRef);
const expectedKey = transportRefEquivalenceKey(expectedTransportRef);
const currentGeneration = state?.ble_discovery_generation;
const record = state?.physical_command?.record;
const recordRevision = record && typeof record.revision === "number"
? record.revision
: null;
const retirements = record && Array.isArray(record.operator_retirements)
? record.operator_retirements
: [];
const exactRetirementRecorded = retirements.some((candidate) => Boolean(
candidate
&& typeof candidate === "object"
&& "retirement_id" in candidate
&& candidate.retirement_id === expectedRetirementId
&& "retired_transport_ref" in candidate
&& typeof candidate.retired_transport_ref === "string"
&& transportRefEquivalenceKey(candidate.retired_transport_ref) === expectedKey
));
const freshCandidates = (state?.devices ?? []).filter(
(device) => transportRefEquivalenceKey(device.device_id) === candidateKey,
);
const freshCandidate = freshCandidates.length === 1
? freshCandidates[0]
: null;
const recordConnection = record && typeof record.connection === "object"
&& record.connection !== null
? record.connection
: null;
const recoveryConnectionMode = recordConnection
&& "connection_mode" in recordConnection
&& typeof recordConnection.connection_mode === "string"
? recordConnection.connection_mode
: null;
const recoveryMatchesMode = Boolean(
recordConnection
&& "transport_ref" in recordConnection
&& typeof recordConnection.transport_ref === "string"
&& transportRefEquivalenceKey(recordConnection.transport_ref) === expectedKey
&& recoveryConnectionMode === connectionMode,
);
// CoreBluetooth may advertise the exact returned transport while marking
// the passive scan row non-connectable. The backend's exact reopen CAS is
// the only exception: the explicit click is still server-fenced, and every
// unrelated non-connectable row remains blocked by the presentation layer.
if (
projection?.allowed !== true
|| projection.automatic_retry !== false
|| projection.device_io_performed !== false
|| projection.reason_codes.length !== 0
|| !Number.isInteger(expectedRevision)
|| (expectedRevision ?? 0) < 1
|| recordRevision !== expectedRevision
|| !expectedRetirementId
|| !candidateKey
|| candidateKey !== expectedKey
|| !Number.isInteger(expectedDiscoveryGeneration)
|| expectedDiscoveryGeneration !== currentGeneration
|| expectedDesiredMode !== connectionMode
|| state?.desired_connection_mode !== expectedDesiredMode
|| !Number.isInteger(expectedDesiredModeRevision)
|| expectedDesiredModeRevision !== state?.desired_connection_mode_revision
|| !retiredPhysicalTransportRefs(state).has(candidateKey)
|| !exactRetirementRecorded
|| !freshCandidate
|| !recoveryMatchesMode
) return null;
return {
expectedRevision: expectedRevision as number,
expectedRetirementId,
expectedTransportRef,
expectedDiscoveryGeneration: expectedDiscoveryGeneration as number,
expectedDesiredMode,
expectedDesiredModeRevision: expectedDesiredModeRevision as number,
};
}
/**
* Prove that a lost reopen response actually committed the exact local-only
* ledger transition. Nothing here grants Verify by itself; the caller must
* also retain the original full connection-action authority.
*/
export function reopenedPhysicalReconciliationMatches(
state: XgridsK1State | null | undefined,
request: ReopenRetiredPhysicalReconciliationRequest,
): boolean {
const record = state?.physical_command?.record;
const recordRevision = record && typeof record.revision === "number"
? record.revision
: null;
const reopens = record && Array.isArray(record.operator_reconciliation_reopens)
? record.operator_reconciliation_reopens
: [];
const requestKey = transportRefEquivalenceKey(request.expected_transport_ref);
const exactAudit = reopens.some((candidate) => Boolean(
candidate
&& typeof candidate === "object"
&& "reopening_id" in candidate
&& candidate.reopening_id === request.reopening_id
&& "retirement_id" in candidate
&& candidate.retirement_id === request.expected_retirement_id
&& "retired_record_revision" in candidate
&& candidate.retired_record_revision === request.expected_revision
&& "reopened_transport_ref" in candidate
&& typeof candidate.reopened_transport_ref === "string"
&& transportRefEquivalenceKey(candidate.reopened_transport_ref) === requestKey
&& "discovery_generation" in candidate
&& candidate.discovery_generation === request.expected_discovery_generation
&& "reason" in candidate
&& candidate.reason === request.reason
));
const activeRetiredRefs = state?.connection_policy?.facts.retired_transport_refs;
const activeDenyRemoved = Array.isArray(activeRetiredRefs)
&& !activeRetiredRefs.some(
(value) => transportRefEquivalenceKey(value) === requestKey,
);
const freshCandidates = (state?.devices ?? []).filter(
(device) => transportRefEquivalenceKey(device.device_id) === requestKey,
);
const freshCandidate = freshCandidates.length === 1
? freshCandidates[0]
: null;
return Boolean(
request.expected_desired_mode === state?.desired_connection_mode
&& request.expected_desired_mode_revision
=== state?.desired_connection_mode_revision
&& requestKey
&& recordRevision === request.expected_revision + 1
&& (record?.stage === "dispatching" || record?.stage === "observing")
&& record.resolution === null
&& state?.physical_command?.requires_reconciliation === true
&& state.ble_discovery_generation === request.expected_discovery_generation
&& exactAudit
&& activeDenyRemoved
&& freshCandidate
&& freshCandidate.connectable !== false
);
}
/** The exact durable K1 binding that recovery is allowed to observe. */
export function readOnlyPhysicalRecoveryBinding(
state: XgridsK1State | null | undefined,
): ReadOnlyPhysicalRecoveryBinding | null {
if (!requiresReadOnlyPhysicalRecovery(state)) return null;
const actions: ReadOnlyNetworkObservationAction[] = [
"observe-fresh-device-network",
"observe-current-device-network",
"observe-configured-device-network",
];
for (const action of actions) {
const decision = connectionPolicyDecision(state, action);
const deviceId = decision?.required_transport_ref?.trim();
const connectionMode = decision?.required_connection_mode ?? null;
if (deviceId && isConnectionMode(connectionMode)) {
return { deviceId, connectionMode };
}
}
return null;
}
export function canSelectConnectionMode(
state: XgridsK1State | null | undefined,
): boolean {
const lifecycle = state?.connection_lifecycle;
return Boolean(
lifecycle?.schema_version === "missioncore.xgrids-k1-connection-lifecycle/v1"
&& lifecycle.mode_selection.allowed === true
&& lifecycle.mode_selection.automatic_retry === false
&& lifecycle.allowed_actions.includes("select-connection-mode"),
);
}
type ReadOnlyNetworkObservationAction = Extract<
XgridsConnectionPolicyAction,
| "observe-fresh-device-network"
| "observe-current-device-network"
| "observe-configured-device-network"
>;
export type ReadOnlyConnectionObservationAction =
| ReadOnlyNetworkObservationAction
| Extract<XgridsConnectionPolicyAction, "verify-control-device-info">;
export type ReadOnlyConnectionObservationSource =
| "fresh-scan"
| "retained-current-process"
| "durable-configured-state";
export interface ReadOnlyConnectionObservationTarget {
action: ReadOnlyConnectionObservationAction;
deviceId: string;
connectionMode: XgridsConnectionMode;
source: ReadOnlyConnectionObservationSource;
serverBound: boolean;
expectedDiscoveryGeneration: number | null;
}
function isConnectionMode(value: unknown): value is XgridsConnectionMode {
return value === "bridge"
|| value === "quick-connect"
|| value === "direct-connect";
}
function exactPolicyObservationTarget(
state: XgridsK1State | null | undefined,
action: ReadOnlyNetworkObservationAction,
source: ReadOnlyConnectionObservationSource,
): ReadOnlyConnectionObservationTarget | null {
if (!connectionPolicyAllows(state, action)) return null;
const decision = connectionPolicyDecision(state, action);
const deviceId = decision?.required_transport_ref?.trim();
const connectionMode = decision?.required_connection_mode ?? null;
if (
decision?.target_source !== source
|| !deviceId
|| !isConnectionMode(connectionMode)
) {
return null;
}
if (source === "fresh-scan") {
const freshDevice = provisioningCandidateById(state?.devices ?? [], deviceId);
if (
!freshDevice
|| freshDevice.connectable === false
|| !Number.isInteger(state?.ble_discovery_generation)
|| (state?.ble_discovery_generation ?? -1) < 0
) return null;
}
return {
action,
deviceId,
connectionMode,
source,
serverBound: true,
expectedDiscoveryGeneration: source === "fresh-scan"
? state?.ble_discovery_generation as number
: null,
};
}
const SERVER_BOUND_RECOVERY_OBSERVATION_PRIORITY:
ReadonlyArray<ReadOnlyNetworkObservationAction> = [
"observe-current-device-network",
"observe-configured-device-network",
"observe-fresh-device-network",
];
/**
* Resolve a recovery Verify target from the public policy, never from a
* browser selection. A reconnect must prefer already bound or durable state
* over a projected fresh scan: a browser projection cannot prove that the
* native BLEDevice is still retained by the backend process. The backend
* policy still decides which exact actions are allowed; this helper only
* chooses the safest allowed source in current -> configured -> fresh order.
*/
export function recommendedConnectionRecoveryObservationTarget(
state: XgridsK1State | null | undefined,
): ReadOnlyConnectionObservationTarget | null {
const sources: Record<
ReadOnlyNetworkObservationAction,
ReadOnlyConnectionObservationSource
> = {
"observe-current-device-network": "retained-current-process",
"observe-configured-device-network": "durable-configured-state",
"observe-fresh-device-network": "fresh-scan",
};
for (const action of SERVER_BOUND_RECOVERY_OBSERVATION_PRIORITY) {
const target = exactPolicyObservationTarget(state, action, sources[action]);
if (target?.serverBound) return target;
}
return null;
}
/**
* A terminal control session may block the network-observation actions while
* the supervisor still explicitly allows its narrower DeviceInfo Verify.
* Reuse that permission only when the physical ledger and durable topology
* independently pin the same K1 and mode; browser selection is never used.
*/
function exactControlVerificationTarget(
state: XgridsK1State | null | undefined,
): ReadOnlyConnectionObservationTarget | null {
if (
!requiresReadOnlyPhysicalRecovery(state)
|| !connectionPolicyAllows(state, "verify-control-device-info")
) return null;
const binding = readOnlyPhysicalRecoveryBinding(state);
const semanticStore = state?.semantic_topology_store;
const durable = semanticStore?.record;
if (
!binding
|| semanticStore?.status !== "available"
|| semanticStore.configured_offline_evidence !== true
|| semanticStore.live_connection_authority !== false
|| durable?.schema_version !== "missioncore.xgrids-k1-semantic-topology/v1"
|| transportRefEquivalenceKey(durable.transport_ref)
!== transportRefEquivalenceKey(binding.deviceId)
|| durable.connection_mode !== binding.connectionMode
) return null;
return {
action: "verify-control-device-info",
deviceId: binding.deviceId,
connectionMode: binding.connectionMode,
source: "durable-configured-state",
serverBound: true,
expectedDiscoveryGeneration: null,
};
}
/**
* Resolve the one read-only BLE target authorized by the server policy.
* Unresolved writes never fall back to a browser selection or dropdown mode:
* their UUID, mode and recovery source must be pinned by the same decision.
*/
export function readOnlyConnectionObservationTarget(
state: XgridsK1State | null | undefined,
selectedDeviceId = "",
selectedConnectionMode: XgridsConnectionMode | null = null,
): ReadOnlyConnectionObservationTarget | null {
// An unresolved physical command must first use the exact durable
// DeviceInfo path when the supervisor authorizes it. A projected fresh BLE
// row can outlive the backend's native BLEDevice and therefore cannot
// outrank this read-only reconciliation route.
const exactControl = exactControlVerificationTarget(state);
if (exactControl) return exactControl;
const exactFresh = exactPolicyObservationTarget(
state,
"observe-fresh-device-network",
"fresh-scan",
);
if (exactFresh) return exactFresh;
// Outside reconciliation, the backend deliberately leaves the fresh target
// unpinned because the operator may choose among several current adverts.
// Preserve that reviewed path, but never use it for an unresolved write.
if (
!hasUnresolvedNetworkMutation(state)
&& !requiresReadOnlyPhysicalRecovery(state)
&& selectedConnectionMode
&& connectionPolicyAllows(state, "observe-fresh-device-network")
) {
const freshDevice = provisioningCandidateById(
state?.devices ?? [],
selectedDeviceId,
);
if (
freshDevice
&& freshDevice.connectable !== false
&& Number.isInteger(state?.ble_discovery_generation)
&& (state?.ble_discovery_generation ?? -1) >= 0
) {
return {
action: "observe-fresh-device-network",
deviceId: freshDevice.device_id,
connectionMode: selectedConnectionMode,
source: "fresh-scan",
serverBound: false,
expectedDiscoveryGeneration: state?.ble_discovery_generation as number,
};
}
}
return exactPolicyObservationTarget(
state,
"observe-current-device-network",
"retained-current-process",
) ?? exactPolicyObservationTarget(
state,
"observe-configured-device-network",
"durable-configured-state",
);
}
/**
* Resolve recovery for an already-applied network only from a backend-pinned
* current/configured target. A browser-selected advertisement is never an
* authority for this read-only continuation, even when it happens to carry
* the same UUID.
*/
export function serverBoundAppliedNetworkObservationTarget(
state: XgridsK1State | null | undefined,
connectionMode: XgridsConnectionMode,
): ReadOnlyConnectionObservationTarget | null {
const target = exactPolicyObservationTarget(
state,
"observe-current-device-network",
"retained-current-process",
) ?? exactPolicyObservationTarget(
state,
"observe-configured-device-network",
"durable-configured-state",
);
return target?.serverBound === true
&& target.connectionMode === connectionMode
? target
: null;
}
export interface BackendConnectionTopology {
connectionMode: NonNullable<XgridsK1State["connection_mode"]>;
status: "active" | "configured-unverified" | "configured-offline";
source: "applied" | "durable" | "last-known";
endpoint: string | null;
}
function sameTarget(
left: { ipv4: string; port: number } | null | undefined,
right: { ipv4: string; port: number } | null | undefined,
): boolean {
return Boolean(
left
&& right
&& left.ipv4 === right.ipv4
&& left.port === right.port,
);
}
export function currentAppliedConnectionTopology(
state: XgridsK1State | null | undefined,
connectionMode?: NonNullable<XgridsK1State["connection_mode"]>,
): BackendConnectionTopology | null {
const supervisor = state?.connection_supervisor;
if (!supervisor || supervisor.closed) return null;
const { intent, lease, observed } = supervisor;
const deviceNetwork = observed.device_network;
const mode = deviceNetwork?.connection_mode;
const target = deviceNetwork?.target;
const currentDeviceNetwork = Boolean(
intent
&& mode
&& target
&& (!connectionMode || mode === connectionMode)
&& deviceNetwork.state === "applied"
&& deviceNetwork.intent_id === intent.intent_id
&& Boolean(deviceNetwork.transport_ref)
&& mode === intent.requested_mode
);
if (!currentDeviceNetwork || !mode || !target) return null;
const currentEndpoint = Boolean(
["configured-unverified", "reachable"].includes(lease.state)
&& lease.intent_id === intent?.intent_id
&& lease.connection_mode === mode
&& sameTarget(lease.target, target)
&& observed.host_path.available === true
&& observed.host_path.route_class === "direct"
&& lease.host_path_epoch === observed.host_path.epoch
&& observed.endpoint.intent_id === intent?.intent_id
&& observed.endpoint.host_path_epoch === observed.host_path.epoch
&& observed.endpoint.tcp_state === "reachable"
&& sameTarget(target, observed.endpoint.target)
);
const identity = observed.device_identity;
const controlPlane = observed.control_plane;
const lifecycle = state?.connection_lifecycle;
const activeBinding = lifecycle?.active_binding;
const identityExact = Boolean(
identity.state === "verified"
&& identity.intent_id === intent?.intent_id
&& identity.connection_mode === mode
&& identity.host_path_epoch === observed.host_path.epoch
&& identity.logical_device_id
&& lease.logical_device_id === identity.logical_device_id
&& (!intent?.expected_device_id
|| identity.logical_device_id === intent.expected_device_id),
);
const active = Boolean(
currentEndpoint
&& lease.state === "reachable"
&& supervisor.authority.control_allowed === true
&& identityExact
&& controlPlane.state === "healthy"
&& Boolean(controlPlane.session_id)
&& controlPlane.host_path_epoch === observed.host_path.epoch
&& lifecycle?.schema_version === "missioncore.xgrids-k1-connection-lifecycle/v1"
&& lifecycle.connection_ready === true
&& lifecycle.configured_mode === mode
&& lifecycle.active_mode === mode
&& activeBinding?.connection_mode === mode
&& activeBinding.intent_id === intent?.intent_id
&& transportRefEquivalenceKey(activeBinding.transport_ref)
=== transportRefEquivalenceKey(deviceNetwork.transport_ref)
&& activeBinding.target_ipv4 === target.ipv4
&& activeBinding.target_port === target.port
&& activeBinding.host_path_epoch === observed.host_path.epoch
&& activeBinding.control_session_id === controlPlane.session_id
);
return {
connectionMode: mode,
status: active
? "active"
: currentEndpoint
? "configured-unverified"
: "configured-offline",
source: "applied",
endpoint: target.ipv4,
};
}
export function backendConnectionTopology(
state: XgridsK1State | null | undefined,
connectionMode?: NonNullable<XgridsK1State["connection_mode"]>,
): BackendConnectionTopology | null {
const supervisor = state?.connection_supervisor;
const applied = currentAppliedConnectionTopology(state);
// A current BLE-proved device topology supersedes every persisted or
// historical address, including when the selected UI mode is different.
if (applied) {
return !connectionMode || applied.connectionMode === connectionMode
? applied
: null;
}
const semanticStore = state?.semantic_topology_store;
const durable = semanticStore?.record;
if (
semanticStore?.status === "available"
&& semanticStore.configured_offline_evidence === true
&& semanticStore.live_connection_authority === false
&& durable
&& durable.schema_version === "missioncore.xgrids-k1-semantic-topology/v1"
&& (!connectionMode || durable.connection_mode === connectionMode)
&& Boolean(durable.ipv4.trim())
) {
const endpointProbe = state?.configured_endpoint_probe;
const durableEndpointReachable = Boolean(
endpointProbe?.status === "reachable"
&& endpointProbe.target_source === "durable-semantic-topology"
&& endpointProbe.connection_mode === durable.connection_mode
&& endpointProbe.endpoint === durable.ipv4
&& transportRefEquivalenceKey(endpointProbe.transport_ref)
=== transportRefEquivalenceKey(durable.transport_ref)
&& endpointProbe.semantic_revision === durable.revision
&& endpointProbe.host_route_available === true
&& endpointProbe.host_route_class === "direct"
&& endpointProbe.tcp_reachable === true
&& endpointProbe.identity_validation === "not-performed"
&& endpointProbe.control_authority_granted === false
&& endpointProbe.ble_operation_performed === false
&& endpointProbe.network_mutation_performed === false
&& endpointProbe.automatic_retry === false
);
return {
connectionMode: durable.connection_mode,
status: durableEndpointReachable
? "configured-unverified"
: "configured-offline",
source: "durable",
endpoint: durable.ipv4,
};
}
const lastKnown = supervisor?.last_known;
if (
!lastKnown
|| (connectionMode && lastKnown.connection_mode !== connectionMode)
) return null;
return {
connectionMode: lastKnown.connection_mode,
status: "configured-offline",
source: "last-known",
endpoint: lastKnown.target.ipv4,
};
}
export function hasUnresolvedNetworkMutation(
state: XgridsK1State | null | undefined,
): boolean {
if (state?.network_write_reconciliation) return true;
const ledger = state?.network_mutation_ledger;
return Boolean(
ledger
&& (
ledger.status === "unresolved"
|| ledger.status === "corrupt"
|| ledger.mutation_allowed !== true
),
);
}
export function canSubmitProvisioningMutation({
devices,
selectedDeviceId,
credentialsReady,
isBusy,
}: {
devices: readonly BleDevice[];
selectedDeviceId: string;
credentialsReady: boolean;
isBusy: boolean;
}): boolean {
const candidate = provisioningCandidateById(devices, selectedDeviceId);
return Boolean(
credentialsReady &&
!isBusy &&
candidate &&
candidate.connectable !== false,
);
}
export function canAdmitProvisioningConnection({
policyAllowed,
targetSource,
hasSuccessfulLocalConnect,
localPrerequisitesReady,
}: {
policyAllowed: boolean;
targetSource: "fresh-scan" | null;
hasSuccessfulLocalConnect: boolean;
localPrerequisitesReady: boolean;
}): boolean {
return Boolean(
policyAllowed
&& targetSource === "fresh-scan"
&& !hasSuccessfulLocalConnect
&& localPrerequisitesReady,
);
}
export function isReachableConnectionLease(
state: XgridsK1State | null | undefined,
connectionMode: NonNullable<XgridsK1State["connection_mode"]>,
): boolean {
const supervisor = state?.connection_supervisor;
if (!supervisor || supervisor.closed) return false;
return currentAppliedConnectionTopology(state, connectionMode)?.status === "active";
}
export function isConfiguredConnectionLease(
state: XgridsK1State | null | undefined,
connectionMode: NonNullable<XgridsK1State["connection_mode"]>,
): boolean {
const topology = backendConnectionTopology(state, connectionMode);
return Boolean(topology && topology.source !== "last-known");
}
export function hasControlAuthority(
state: XgridsK1State | null | undefined,
): boolean {
const topology = currentAppliedConnectionTopology(state);
return Boolean(topology?.status === "active");
}
export function hasAuthoritativeData(
state: XgridsK1State | null | undefined,
): boolean {
const supervisor = state?.connection_supervisor;
const dataPlane = supervisor?.observed.data_plane;
return Boolean(
hasControlAuthority(state)
&& supervisor?.authority.data_ingest_authoritative === true
&& dataPlane?.state === "healthy"
&& Boolean(dataPlane.session_id)
&& dataPlane.host_path_epoch === supervisor.observed.host_path.epoch,
);
}
export function canonicalDeviceConnectivity(
state: XgridsK1State | null | undefined,
): "unknown" | "offline" | "connecting" | "connected" | "degraded" {
const topology = currentAppliedConnectionTopology(state);
if (topology?.status === "active") {
const supervisor = state?.connection_supervisor;
return supervisor
&& ["stalled", "lost"].includes(supervisor.observed.data_plane.state)
? "degraded"
: "connected";
}
if (topology?.status === "configured-unverified") return "connecting";
if (topology?.status === "configured-offline") return "offline";
const fallback = backendConnectionTopology(state);
if (fallback?.source === "last-known") return "degraded";
if (fallback?.status === "configured-offline") return "offline";
const supervisor = state?.connection_supervisor;
if (!supervisor || supervisor.closed) return supervisor?.closed ? "offline" : "unknown";
if (supervisor.lease.state === "lost" || supervisor.last_known) return "degraded";
return "offline";
}
export function activeConnectionEndpointLabel(
state: XgridsK1State | null | undefined,
): string | null {
const topology = currentAppliedConnectionTopology(state);
return topology?.status === "active" ? topology.endpoint : null;
}
export interface ReachableConnectionLeaseIdentity {
key: string;
runtimeId: string;
leaseGeneration: number;
intentId: string;
hostPathEpoch: number;
connectionMode: NonNullable<XgridsK1State["connection_mode"]>;
}
export interface RuntimeErrorCorrelation {
action: string;
runtimeId: string | null;
leaseGeneration: number | null;
connectionAttemptId: string | null;
}
/**
* Attach connection-attempt diagnostics only to the exact failed Connect
* action which produced them. A global Scan/Verify/Refresh failure must never
* borrow an older durable attempt merely because it remains in the snapshot.
*/
export function connectionAttemptForRuntimeError(
error: RuntimeErrorCorrelation | null | undefined,
state: XgridsK1State | null | undefined,
): XgridsConnectionAttempt | null {
const runtimeId = state?.snapshot_runtime_id?.trim() || null;
const attempt = state?.connection_attempt;
return error?.action === "connect"
&& typeof error.runtimeId === "string"
&& error.runtimeId === runtimeId
&& typeof error.connectionAttemptId === "string"
&& attempt
&& error.connectionAttemptId === attempt?.attempt_id
&& !["accepted", "running"].includes(attempt.status)
? attempt
: null;
}
export function reachableConnectionLeaseIdentity(
state: XgridsK1State | null | undefined,
): ReachableConnectionLeaseIdentity | null {
const runtimeId = state?.snapshot_runtime_id;
const supervisor = state?.connection_supervisor;
const connectionMode = supervisor?.lease.connection_mode;
const leaseGeneration = supervisor?.lease.generation;
const intentId = supervisor?.intent?.intent_id;
const hostPathEpoch = supervisor?.lease.host_path_epoch;
if (
typeof runtimeId !== "string"
|| !runtimeId.trim()
|| !connectionMode
|| !intentId
|| !Number.isInteger(leaseGeneration)
|| (leaseGeneration ?? -1) < 0
|| !Number.isInteger(hostPathEpoch)
|| (hostPathEpoch ?? 0) < 1
|| !isReachableConnectionLease(state, connectionMode)
) {
return null;
}
return {
key: `${runtimeId}:${intentId}:${hostPathEpoch}:${leaseGeneration}`,
runtimeId,
leaseGeneration: leaseGeneration as number,
intentId,
hostPathEpoch: hostPathEpoch as number,
connectionMode,
};
}
export function authoritativeReachableLeaseSupersedesError(
error: RuntimeErrorCorrelation | null | undefined,
state: XgridsK1State | null | undefined,
): boolean {
if (
!error
|| (error.action !== "connect" && error.action !== "verify")
|| typeof error.runtimeId !== "string"
|| !Number.isInteger(error.leaseGeneration)
// An omitted legacy field is not proof that the process-owned write fence
// was cleared. Only the canonical explicit null may dismiss the banner.
|| hasUnresolvedNetworkMutation(state)
) {
return false;
}
const identity = reachableConnectionLeaseIdentity(state);
return Boolean(
identity
&& identity.runtimeId === error.runtimeId
&& identity.leaseGeneration > (error.leaseGeneration as number),
);
}
/**
* Clear a transient action banner when a later authoritative state proves
* that the failed local acquisition has already been sealed and released.
* Every poll/WebSocket state enters through the same reducer, so recovery
* needs no refresh button or browser-cache reset.
*/
export function authoritativeStateSupersedesRuntimeError(
error: RuntimeErrorCorrelation | null | undefined,
state: XgridsK1State | null | undefined,
): boolean {
return Boolean(
authoritativeReachableLeaseSupersedesError(error, state)
|| (error && !shouldSurfaceRuntimeActionError(error.action, state)),
);
}
function defaultUuid(): string {
const cryptoApi = globalThis.crypto;
if (!cryptoApi) {
throw new Error("Web Crypto недоступен; безопасный идентификатор операции не создан.");
}
if (typeof cryptoApi.randomUUID === "function") return cryptoApi.randomUUID();
const bytes = new Uint8Array(16);
cryptoApi.getRandomValues(bytes);
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = [...bytes].map((value) => value.toString(16).padStart(2, "0"));
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex
.slice(6, 8)
.join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
}
export function newOperationId(): string {
return `op-${defaultUuid()}`;
}
export function newPhysicalRetirementId(): string {
return `retirement-${defaultUuid()}`;
}
export function newPhysicalReopeningId(): string {
return `reopening-${defaultUuid()}`;
}
export function newMutationContext(action: string): {
operation_id: string;
idempotency_key: string;
} {
const normalizedAction = action.trim();
if (!normalizedAction) {
throw new Error("Действие операции не задано; безопасный ключ не создан.");
}
const operationId = newOperationId();
return {
operation_id: operationId,
idempotency_key: `${normalizedAction}:${operationId}`,
};
}
export function provisioningIntentKey(
current: string | null,
createUuid: () => string = defaultUuid,
): string {
return current ?? `network-provision:${createUuid()}`;
}