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
@@ -0,0 +1,278 @@
import {
isXgridsActiveStreamRecovery,
type XgridsActiveStreamRecovery,
type XgridsK1State,
} from "./api";
export interface ActiveStreamRecoveryLineage {
snapshotRuntimeId: string;
acquisitionId: string;
acquisitionStateRevision: number;
recoveryGeneration: number;
runtimeProducerGeneration: number;
recovery: XgridsActiveStreamRecovery;
}
export type ActiveStreamForceFinishAuthority = ActiveStreamRecoveryLineage;
export type ActiveStreamRecoveryPresentationAuthority = ActiveStreamRecoveryLineage;
export type ActiveStreamRecoveryVisibleState =
| "reconnecting"
| "blocked"
| "standby"
| "fault";
export interface ActiveStreamRecoveryPresentation {
state: ActiveStreamRecoveryVisibleState;
eyebrow: string;
title: string;
statusLabel: string;
tone: "neutral" | "warning" | "danger";
detail: string;
progressLabel: string | null;
showSpinner: boolean;
forceFinishAvailable: boolean;
}
function positiveInteger(value: unknown): value is number {
return Number.isInteger(value) && (value as number) > 0;
}
/**
* Resolve one exact active-stream lineage from the public runtime snapshot.
*
* A recovery-shaped object alone is not authority. The browser also requires
* the current runtime id, the same acquisition id and the exact producer
* generation on both sides of the projection. This keeps a late recovery
* update from an older producer out of both presentation and mutation gates.
*/
export function exactActiveStreamRecoveryLineage(
state: XgridsK1State | null | undefined,
): ActiveStreamRecoveryLineage | null {
const recovery = state?.connection_recovery;
const acquisition = state?.acquisition;
const snapshotRuntimeId = state?.snapshot_runtime_id?.trim() || null;
const producerGeneration = state?.producer_generation;
const acquisitionId = acquisition?.acquisition_id?.trim() || null;
const recoveryAcquisitionId = recovery?.acquisition_id?.trim() || null;
if (
!snapshotRuntimeId
|| !isXgridsActiveStreamRecovery(recovery)
|| !acquisition
|| !acquisitionId
|| recoveryAcquisitionId !== acquisitionId
|| !positiveInteger(acquisition.state_revision)
|| !positiveInteger(recovery.generation)
|| !positiveInteger(producerGeneration)
|| recovery.runtime_producer_generation !== producerGeneration
|| recovery.automatic_read_only_rebind !== true
) return null;
return {
snapshotRuntimeId,
acquisitionId,
acquisitionStateRevision: acquisition.state_revision,
recoveryGeneration: recovery.generation,
runtimeProducerGeneration: producerGeneration,
recovery,
};
}
/** Exact, current and backend-policy-admitted authority for local-only finish. */
export function activeStreamForceFinishAuthority(
state: XgridsK1State | null | undefined,
): ActiveStreamForceFinishAuthority | null {
const lineage = exactActiveStreamRecoveryLineage(state);
if (
!lineage
|| !["reconnecting", "blocked"].includes(lineage.recovery.state)
|| lineage.recovery.force_finish_allowed !== true
|| state?.phase !== "reconnecting"
|| state.source_mode !== "live"
|| ![
"starting",
"awaiting_external_start",
"acquiring",
].includes(state.acquisition?.state ?? "")
) return null;
return lineage;
}
/**
* Exact authority for retaining browser presentation while the backend owns a
* read-only reconnect. This is deliberately narrower than the recovery card:
* terminal/blocked recovery states and an inactive acquisition cannot retain
* a prior spatial or camera transport.
*/
export function activeStreamRecoveryPresentationAuthority(
state: XgridsK1State | null | undefined,
): ActiveStreamRecoveryPresentationAuthority | null {
const lineage = exactActiveStreamRecoveryLineage(state);
if (
!lineage
|| lineage.recovery.state !== "reconnecting"
|| state?.phase !== "reconnecting"
|| state.source_mode !== "live"
|| ![
"starting",
"awaiting_external_start",
"acquiring",
].includes(state.acquisition?.state ?? "")
) return null;
return lineage;
}
/**
* Keep the exact recovered lineage available to disposable browser receivers
* after the recovery card has disappeared. Spatial admission can complete on
* the first authoritative PCL before the acquisition-owned camera produces
* its first playable frame. This authority carries only the no-write
* presentation lease: it grants neither force-finish nor START/STOP policy.
*/
export function activeStreamRecoveredBrowserAuthority(
state: XgridsK1State | null | undefined,
): ActiveStreamRecoveryPresentationAuthority | null {
const lineage = exactActiveStreamRecoveryLineage(state);
if (
!lineage
|| lineage.recovery.state !== "recovered"
|| lineage.recovery.camera_recovery !== "owned"
|| state?.phase !== "live"
|| state.source_mode !== "live"
|| state.acquisition?.state !== "acquiring"
) return null;
return lineage;
}
/**
* While a validated recovery contract is active it owns the presentation
* decision. Ordinary supervisor data flags may be stale across the network
* gap, so only an exact reconnect lease can retain browser transports.
*/
export function activeStreamRecoveryOwnsPresentationDecision(
state: XgridsK1State | null | undefined,
): boolean {
const recovery = state?.connection_recovery;
return Boolean(
isXgridsActiveStreamRecovery(recovery)
&& !["inactive", "recovered"].includes(recovery.state),
);
}
export function activeStreamForceFinishAuthorityMatches(
expected: ActiveStreamForceFinishAuthority,
state: XgridsK1State | null | undefined,
): boolean {
const current = activeStreamForceFinishAuthority(state);
return Boolean(
current
&& current.snapshotRuntimeId === expected.snapshotRuntimeId
&& current.acquisitionId === expected.acquisitionId
&& current.acquisitionStateRevision === expected.acquisitionStateRevision
&& current.recoveryGeneration === expected.recoveryGeneration
&& current.runtimeProducerGeneration === expected.runtimeProducerGeneration,
);
}
export function formatActiveStreamRecoveryElapsed(
elapsedMs: number | null,
): string | null {
if (!Number.isFinite(elapsedMs) || elapsedMs === null || elapsedMs < 0) return null;
const elapsedSeconds = Math.floor(elapsedMs / 1_000);
if (elapsedSeconds < 60) return `${elapsedSeconds} с`;
const minutes = Math.floor(elapsedSeconds / 60);
const seconds = elapsedSeconds % 60;
return seconds > 0 ? `${minutes} мин ${seconds} с` : `${minutes} мин`;
}
function recoveryProgressLabel(
recovery: XgridsActiveStreamRecovery,
): string | null {
const elapsed = formatActiveStreamRecoveryElapsed(recovery.elapsed_ms);
const attempt = recovery.attempt > 0
? `Попытка ${recovery.attempt}`
: "Подготовка проверки";
return elapsed ? `${attempt} · ${elapsed}` : attempt;
}
/**
* Present only an exact current lineage. `recovered` deliberately returns
* null so the ordinary confirmed live UI resumes without a transitional card.
*/
export function activeStreamRecoveryPresentation(
state: XgridsK1State | null | undefined,
): ActiveStreamRecoveryPresentation | null {
const lineage = exactActiveStreamRecoveryLineage(state);
if (!lineage) return null;
const recovery = lineage.recovery;
if (recovery.state === "reconnecting") {
return {
state: "reconnecting",
eyebrow: "СВЯЗЬ · АКТИВНЫЙ ПРИЁМ",
title: "Восстанавливаем соединение",
statusLabel: "Восстановление связи",
tone: "neutral",
detail: "Проверяем прежний активный контур только для чтения. START, STOP и настройки сети не отправляются.",
progressLabel: recoveryProgressLabel(recovery),
showSpinner: true,
forceFinishAvailable: activeStreamForceFinishAuthority(state) !== null,
};
}
if (recovery.state === "blocked") {
return {
state: "blocked",
eyebrow: "СВЯЗЬ · ТРЕБУЕТСЯ ДЕЙСТВИЕ",
title: recovery.camera_recovery === "blocked"
? "Видеопоток не восстановлен"
: "Связь не восстановлена",
statusLabel: "Восстановление остановлено",
tone: "warning",
detail: recovery.camera_recovery === "blocked"
? "Связь с K1 проверена, но камера не возобновила передачу. Можно завершить только локальный приём."
: "Автоматическая проверка остановлена. Можно завершить только локальный приём; команда устройству не отправится.",
progressLabel: recoveryProgressLabel(recovery),
showSpinner: false,
forceFinishAvailable: activeStreamForceFinishAuthority(state) !== null,
};
}
if (recovery.state === "standby") {
return {
state: "standby",
eyebrow: "СВЯЗЬ · СОСТОЯНИЕ ПРОВЕРЕНО",
title: "Устройство перешло в ожидание",
statusLabel: "Приём завершён",
tone: "neutral",
detail: "K1 сообщил, что активное сканирование уже завершено. Локальный приём закрывается без команды STOP.",
progressLabel: recoveryProgressLabel(recovery),
showSpinner: false,
forceFinishAvailable: false,
};
}
if (recovery.state === "fault") {
return {
state: "fault",
eyebrow: "СВЯЗЬ · СОСТОЯНИЕ ПРОВЕРЕНО",
title: "K1 сообщил об ошибке",
statusLabel: "Восстановление невозможно",
tone: "danger",
detail: "Безопасная проверка обнаружила ошибку устройства. Автоматических команд и повторов нет.",
progressLabel: recoveryProgressLabel(recovery),
showSpinner: false,
forceFinishAvailable: false,
};
}
return null;
}
/**
* Only an exact, still-active background reconnect may hide the generic red
* error banner. A failed explicit local finish is operator-facing evidence and
* must remain visible even while the last accepted snapshot says reconnecting.
*/
export function suppressGenericErrorDuringActiveStreamRecovery(
state: XgridsK1State | null | undefined,
errorAction?: string | null,
): boolean {
if (errorAction === "force-finish") return false;
return activeStreamRecoveryPresentationAuthority(state) !== null;
}