From 171e2134fa97432081c4418e5698ed8ff12e0794 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:20:14 -0700 Subject: [PATCH] fix(runtime-status): re-list host catalog when the listing goes stale Coverage matching cannot observe catalog edits made by another client or the orca CLI, so gate hydration on catalog listing age too. Co-authored-by: Orca --- .../store/slices/runtime-status-hydration.ts | 12 ++++++++++ .../src/store/slices/settings.test.ts | 22 +++++++++++++++++++ src/renderer/src/store/slices/settings.ts | 5 ++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/store/slices/runtime-status-hydration.ts b/src/renderer/src/store/slices/runtime-status-hydration.ts index b42f05bf535..00f7429645b 100644 --- a/src/renderer/src/store/slices/runtime-status-hydration.ts +++ b/src/renderer/src/store/slices/runtime-status-hydration.ts @@ -1,5 +1,16 @@ import type { PublicKnownRuntimeEnvironment } from '../../../../shared/runtime-environments' +/** Matches the renderer's other catalog-style cache TTLs (checks, work items, Jira). */ +export const RUNTIME_CATALOG_STALE_MS = 60_000 + +let lastCatalogListedAt = 0 + +/** Why: status coverage cannot observe catalog edits made by another client or the + * orca CLI, so an old-enough listing must be re-read even when coverage looks complete. */ +export function isRuntimeCatalogListingStale(): boolean { + return Date.now() - lastCatalogListedAt > RUNTIME_CATALOG_STALE_MS +} + type RuntimeStatusHydrationDependencies = { listEnvironments: () => Promise getCurrentEnvironments: () => PublicKnownRuntimeEnvironment[] @@ -67,6 +78,7 @@ export function createRuntimeStatusHydration({ markCatalogSettled() return } + lastCatalogListedAt = Date.now() if (!revisionsMatch(getCurrentEnvironments(), revisionsAtListStart)) { rerunRequested = true continue diff --git a/src/renderer/src/store/slices/settings.test.ts b/src/renderer/src/store/slices/settings.test.ts index d0397d194de..32082e91e9b 100644 --- a/src/renderer/src/store/slices/settings.test.ts +++ b/src/renderer/src/store/slices/settings.test.ts @@ -10,6 +10,7 @@ import { RUNTIME_PROTOCOL_VERSION } from '../../../../shared/protocol-version' import { clearRuntimeCompatibilityCacheForTests } from '../../runtime/runtime-rpc-client' +import { RUNTIME_CATALOG_STALE_MS } from './runtime-status-hydration' vi.mock('sonner', () => ({ toast: { error: vi.fn(), success: vi.fn(), info: vi.fn() } })) vi.mock('@/lib/agent-status', async (importOriginal) => { @@ -828,4 +829,25 @@ describe('fetchSettings runtime catalog probe', () => { expect(runtimeEnvironmentList).toHaveBeenCalledTimes(1) }) + + it('re-lists the catalog once the listing goes stale even though coverage still matches', async () => { + const environments = [makeRuntimeEnvironment('env-a')] + runtimeEnvironmentList.mockResolvedValue(environments) + const store = createTestStore() + const now = vi.spyOn(Date, 'now').mockReturnValue(1_000_000) + + try { + await store.getState().fetchSettings() + await vi.waitFor(() => expect(store.getState().runtimeStatusByEnvironmentId.size).toBe(1)) + + await store.getState().fetchSettings() + expect(runtimeEnvironmentList).toHaveBeenCalledTimes(1) + + now.mockReturnValue(1_000_000 + RUNTIME_CATALOG_STALE_MS + 1) + await store.getState().fetchSettings() + await vi.waitFor(() => expect(runtimeEnvironmentList).toHaveBeenCalledTimes(2)) + } finally { + now.mockRestore() + } + }) }) diff --git a/src/renderer/src/store/slices/settings.ts b/src/renderer/src/store/slices/settings.ts index 45d3dbc8cd3..fe4d7fcfe38 100644 --- a/src/renderer/src/store/slices/settings.ts +++ b/src/renderer/src/store/slices/settings.ts @@ -14,6 +14,7 @@ import { normalizeTerminalCustomThemes } from '../../../../shared/terminal-custo import { normalizeTaskProviderSettings } from '../../../../shared/task-providers' import { normalizeOpenInApplications } from '../../../../shared/open-in-applications' import { createSettingsSearchState, type SettingsSearchState } from './settings-search-state' +import { isRuntimeCatalogListingStale } from './runtime-status-hydration' import { normalizeDisabledTuiAgents } from '../../../../shared/tui-agent-selection' import { normalizeTuiAgentArgsRecord, @@ -169,7 +170,9 @@ export const createSettingsSlice: StateCreator if ( !runtimeEnvironmentCatalogHydrated || runtimeEnvironmentIds.size !== runtimeStatusByEnvironmentId.size || - runtimeEnvironments.some(({ id }) => !runtimeStatusByEnvironmentId.has(id)) + runtimeEnvironments.some(({ id }) => !runtimeStatusByEnvironmentId.has(id)) || + // Why: coverage is blind to catalog edits from another client or the orca CLI. + isRuntimeCatalogListingStale() ) { void get().hydrateRuntimeEnvironmentStatuses() }