From dd185cf7e4b18d4e08e42f721ce736f4b90919ca Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Mon, 31 Aug 2026 11:27:09 -0700 Subject: [PATCH] fix(runtime): preserve diagnostic overlay lifecycle --- .../settings/runtime-server-row.tsx | 2 +- .../runtime-status-diagnostics-generation.ts | 19 ++--- .../runtime-status-diagnostics-publish.ts | 69 +++++++++++++++++++ .../src/store/slices/runtime-status.ts | 23 +++---- 4 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 src/renderer/src/store/slices/runtime-status-diagnostics-publish.ts diff --git a/src/renderer/src/components/settings/runtime-server-row.tsx b/src/renderer/src/components/settings/runtime-server-row.tsx index f8b117ca043..eba38caf763 100644 --- a/src/renderer/src/components/settings/runtime-server-row.tsx +++ b/src/renderer/src/components/settings/runtime-server-row.tsx @@ -56,7 +56,7 @@ export function RuntimeServerRow({ state.runtimeStatusByEnvironmentId.get(environment.id) ) const connectionState = - details?.status === 'loading' + details?.status === 'loading' && !runtimeStatusEntry?.status ? 'checking' : runtimeStatusEntry ? getRuntimeServerConnectionState({ diff --git a/src/renderer/src/store/slices/runtime-status-diagnostics-generation.ts b/src/renderer/src/store/slices/runtime-status-diagnostics-generation.ts index db859f5d198..22ba23cbbb1 100644 --- a/src/renderer/src/store/slices/runtime-status-diagnostics-generation.ts +++ b/src/renderer/src/store/slices/runtime-status-diagnostics-generation.ts @@ -4,25 +4,16 @@ import type { RuntimeEnvironmentStatus } from './runtime-status' const diagnosticsGenerationByEnvironment = new Map() -export function updateRuntimeEnvironmentStatusOverlay< - T extends { - runtimeStatusByEnvironmentId: ReadonlyMap - } ->( - state: T, +export function updateRuntimeEnvironmentStatusOverlay( + state: Map, environmentId: string, status: RuntimeEnvironmentStatus -): T | { runtimeStatusByEnvironmentId: ReadonlyMap } { - const current = state.runtimeStatusByEnvironmentId.get(environmentId) +): Map { + const current = state.get(environmentId) if (!current || current.status?.runtimeId !== status.status?.runtimeId) { return state } - return { - runtimeStatusByEnvironmentId: new Map(state.runtimeStatusByEnvironmentId).set( - environmentId, - status - ) - } + return new Map(state).set(environmentId, status) } export function acceptRuntimeEnvironmentDiagnosticsGeneration( diff --git a/src/renderer/src/store/slices/runtime-status-diagnostics-publish.ts b/src/renderer/src/store/slices/runtime-status-diagnostics-publish.ts new file mode 100644 index 00000000000..09d66607280 --- /dev/null +++ b/src/renderer/src/store/slices/runtime-status-diagnostics-publish.ts @@ -0,0 +1,69 @@ +import type { RemoteRuntimeSharedConnectionDiagnostics } from '../../../../shared/remote-runtime-shared-control-types' +import type { RuntimeEnvironmentStatus } from './runtime-status' +import * as diagnosticsGeneration from './runtime-status-diagnostics-generation' + +export function publishRuntimeEnvironmentDiagnostics(args: { + environmentId: string + transportGeneration: number + diagnostics: RemoteRuntimeSharedConnectionDiagnostics + getCurrent: () => RuntimeEnvironmentStatus | undefined + updateState: (status: RuntimeEnvironmentStatus) => boolean + afterPublish?: (status: RuntimeEnvironmentStatus) => void +}): void { + diagnosticsGeneration.mergePushedRuntimeEnvironmentDiagnostics({ + environmentId: args.environmentId, + transportGeneration: args.transportGeneration, + diagnostics: args.diagnostics, + current: args.getCurrent(), + publish: (status) => { + if (args.updateState(status)) { + args.afterPublish?.(status) + } + } + }) +} + +export function applyRuntimeEnvironmentStatusOverlay(args: { + environmentId: string + status: RuntimeEnvironmentStatus + setState: ( + updater: (state: Map) => Map + ) => void +}): boolean { + let updated = false + args.setState((state) => { + const next = diagnosticsGeneration.updateRuntimeEnvironmentStatusOverlay( + state, + args.environmentId, + args.status + ) + updated = next !== state + return next + }) + return updated +} + +export function createRuntimeEnvironmentDiagnosticsPublisher(args: { + getCurrent: (environmentId: string) => RuntimeEnvironmentStatus | undefined + setState: ( + updater: (state: Map) => Map + ) => void + afterPublish: (environmentId: string, status: RuntimeEnvironmentStatus) => void +}): (event: { + environmentId: string + transportGeneration: number + diagnostics: RemoteRuntimeSharedConnectionDiagnostics +}) => void { + return (event) => + publishRuntimeEnvironmentDiagnostics({ + ...event, + getCurrent: () => args.getCurrent(event.environmentId), + updateState: (status) => + applyRuntimeEnvironmentStatusOverlay({ + environmentId: event.environmentId, + status, + setState: args.setState + }), + afterPublish: (status) => args.afterPublish(event.environmentId, status) + }) +} diff --git a/src/renderer/src/store/slices/runtime-status.ts b/src/renderer/src/store/slices/runtime-status.ts index a8c25a27698..efd0cfec52a 100644 --- a/src/renderer/src/store/slices/runtime-status.ts +++ b/src/renderer/src/store/slices/runtime-status.ts @@ -18,6 +18,7 @@ import { reconcileCatalogRows } from './repo-identity-reconcile' import { createRuntimeStatusHydration } from './runtime-status-hydration' import { refreshRuntimeEnvironmentStatus } from './runtime-status-refresh' import * as runtimeStatusDiagnostics from './runtime-status-diagnostics-generation' +import * as runtimeStatusDiagnosticsPublish from './runtime-status-diagnostics-publish' import { advanceRuntimeEnvironmentConnectionGeneration, clearRuntimeEnvironmentConnectionGenerations, @@ -300,20 +301,16 @@ export const createRuntimeStatusSlice: StateCreator { - // Diagnostics are an overlay, not a replacement status response. Without a complete - // status snapshot there is no runtime identity or graph evidence to which they belong. - runtimeStatusDiagnostics.mergePushedRuntimeEnvironmentDiagnostics({ - environmentId, - transportGeneration, - diagnostics, - current: get().runtimeStatusByEnvironmentId.get(environmentId), - publish: (status) => - set((s) => - runtimeStatusDiagnostics.updateRuntimeEnvironmentStatusOverlay(s, environmentId, status) + publishRuntimeEnvironmentDiagnostics: + runtimeStatusDiagnosticsPublish.createRuntimeEnvironmentDiagnosticsPublisher({ + getCurrent: (environmentId) => get().runtimeStatusByEnvironmentId.get(environmentId), + setState: (updater) => + set((s) => ({ runtimeStatusByEnvironmentId: updater(s.runtimeStatusByEnvironmentId) })), + afterPublish: (environmentId, status) => + runtimeStatusRecheck.reconcileRuntimeStatusForSlice(environmentId, status.status, get, () => + getRuntimeEnvironmentConnectionGeneration(environmentId) ) - }) - }, + }), clearRuntimeEnvironmentStatus: (environmentId) => { runtimeStatusRecheck.cancelRuntimeStatusRecheck(environmentId)