perf(renderer): scope runtime connection and parking subscriptions (#32199)

This commit is contained in:
Neil
2026-10-23 00:36:08 -07:00
committed by GitHub
parent 758b9c2b71
commit a3512bcd47
11 changed files with 759 additions and 70 deletions
@@ -14,12 +14,12 @@ import es from '../../i18n/locales/es.json'
import ja from '../../i18n/locales/ja.json'
import ko from '../../i18n/locales/ko.json'
import zh from '../../i18n/locales/zh.json'
import { useAppStore } from '@/store'
import type { RuntimeEnvironmentStatus } from '../../../../shared/runtime-host-status'
import { createCompatibleRuntimeStatusResponse } from '@/runtime/runtime-compatibility-test-fixture'
const runtimeStatusByEnvironmentId = new Map<string, { status?: unknown }>()
vi.mock('@/store', () => ({
useAppStore: (selector: (state: unknown) => unknown) => selector({ runtimeStatusByEnvironmentId })
}))
const original = useAppStore.getState()
let runtimeStatusByEnvironmentId = new Map<string, RuntimeEnvironmentStatus>()
vi.mock('@/components/ui/tooltip', () => ({
Tooltip: ({ children }: { children: ReactNode }) => <>{children}</>,
@@ -56,12 +56,14 @@ async function render(
describe('NoticeHostGlyph', () => {
beforeEach(() => {
globalThis.IS_REACT_ACT_ENVIRONMENT = true
runtimeStatusByEnvironmentId.clear()
runtimeStatusByEnvironmentId = new Map()
useAppStore.setState({ runtimeStatusByEnvironmentId })
})
afterEach(() => {
roots.splice(0).forEach((root) => act(() => root.unmount()))
document.body.replaceChildren()
useAppStore.setState(original, true)
})
it('names the SSH host it would act on', async () => {
@@ -74,7 +76,11 @@ describe('NoticeHostGlyph', () => {
})
it('names the paired runtime separately from the SSH host of the same name', async () => {
runtimeStatusByEnvironmentId.set('openclaw-env', { status: 'ready' })
const response = createCompatibleRuntimeStatusResponse()
if (!response.ok) {
throw new Error('expected a compatible status')
}
runtimeStatusByEnvironmentId.set('openclaw-env', { status: response.result, checkedAt: 1 })
const container = await render('runtime:openclaw-env')
expect(container.querySelector('[data-notice-host-kind="runtime"]')).not.toBeNull()
@@ -84,7 +90,7 @@ describe('NoticeHostGlyph', () => {
})
it('marks a paired runtime a probe found unreachable as disconnected', async () => {
runtimeStatusByEnvironmentId.set('openclaw-env', { status: null })
runtimeStatusByEnvironmentId.set('openclaw-env', { status: null, checkedAt: 1 })
const container = await render('runtime:openclaw-env')
expect(container.querySelector('[data-testid="tooltip"]')?.textContent).toBe(
@@ -2,13 +2,9 @@ import React from 'react'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { HostRowIcon } from '../host-row-icon'
import { useAppStore } from '@/store'
import { useRuntimeHostDisconnected } from '@/store/use-runtime-status-view'
import { parseExecutionHostId, type ExecutionHostId } from '../../../../shared/execution-host'
import { translate } from '@/i18n/i18n'
import {
isDisconnectedRuntimeHostState,
runtimeHostConnectionStateForEntry
} from '@/runtime/runtime-host-connection-state'
type NoticeHostGlyphProps = {
hostId: ExecutionHostId
@@ -32,14 +28,9 @@ export default function NoticeHostGlyph({
const host = parseExecutionHostId(hostId)
// Why the shared derivation, not raw truthiness: an absent entry means "not probed yet",
// which is not the same verdict as a probe that came back unreachable.
const isDisconnected = useAppStore((s) => {
if (host?.kind !== 'runtime') {
return false
}
return isDisconnectedRuntimeHostState(
runtimeHostConnectionStateForEntry(s.runtimeStatusByEnvironmentId.get(host.environmentId))
)
})
const isDisconnected = useRuntimeHostDisconnected(
host?.kind === 'runtime' ? host.environmentId : null
)
if (!host) {
return null
@@ -11,10 +11,7 @@ import { getHostDisplayLabelOverrides } from '../../../../shared/host-setting-ov
import { getWorkspacePortsByWorktreeId } from '@/lib/workspace-port-groups'
import { runtimeEnvironmentIdUnlessTwin } from '@/lib/resolve-owner'
import { hydrateRuntimeEnvironmentSshState } from '@/runtime/runtime-environment-ssh-state'
import {
isDisconnectedRuntimeHostState,
runtimeHostConnectionStateForEntry
} from '@/runtime/runtime-host-connection-state'
import { useRuntimeHostDisconnected } from '@/store/use-runtime-status-view'
import { useAppStore } from '@/store'
import {
selectRuntimeAwareSshStatus,
@@ -207,16 +204,7 @@ export function useWorktreeCardFoundation({
: null
// Why the shared derivation, not raw truthiness: an absent entry means "not probed yet",
// which is not the same verdict as a probe that came back unreachable.
const isRuntimeDisconnected = useAppStore((s) => {
if (!runtimeOwnerEnvironmentId) {
return false
}
return isDisconnectedRuntimeHostState(
runtimeHostConnectionStateForEntry(
s.runtimeStatusByEnvironmentId.get(runtimeOwnerEnvironmentId)
)
)
})
const isRuntimeDisconnected = useRuntimeHostDisconnected(runtimeOwnerEnvironmentId || null)
const [titleRenaming, setTitleRenaming] = useState(false)
const [showRenameErrorDialog, setShowRenameErrorDialog] = useState(false)
// Why: read the target label from its owning host's store instead of exposing HUB-private SSH metadata as client-local state.
@@ -25,8 +25,6 @@ type PairedRuntimeParkingEnvironmentIdsCache = {
environmentIds: ReadonlySet<string>
}
let pairedRuntimeParkingEnvironmentIdsCache: PairedRuntimeParkingEnvironmentIdsCache | null = null
function haveSameEnvironmentIds(left: ReadonlySet<string>, right: ReadonlySet<string>): boolean {
if (left.size !== right.size) {
return false
@@ -39,34 +37,43 @@ function haveSameEnvironmentIds(left: ReadonlySet<string>, right: ReadonlySet<st
return true
}
export function createPairedRuntimeParkingEnvironmentIdsSelector() {
let cache: PairedRuntimeParkingEnvironmentIdsCache | null = null
return (statuses: PairedRuntimeParkingCapabilityStatuses): ReadonlySet<string> => {
const cached = cache
if (cached?.statuses === statuses) {
return cached.environmentIds
}
const capable = new Set<string>()
for (const [environmentId, entry] of statuses) {
// Why last-verified: a capability is a fact about the host's build, so an unverifiable
// probe must not unpark its terminals. See docs/reference/ssh-execution-boundary.md.
// Why the revoked gate: parking discards the client's only copy of the scrollback against
// a host-side restore, so a host that refused us must not keep promising one.
if (isRuntimeHostContactRevoked(entry)) {
continue
}
const status = lastVerifiedRuntimeStatus<PairedRuntimeParkingCapability>(entry)
if (status?.capabilities?.includes(TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY)) {
capable.add(environmentId)
}
}
const environmentIds =
cached && haveSameEnvironmentIds(cached.environmentIds, capable)
? cached.environmentIds
: capable
cache = { statuses, environmentIds }
return environmentIds
}
}
let selectParkingEnvironmentIds = createPairedRuntimeParkingEnvironmentIdsSelector()
export function selectPairedRuntimeParkingEnvironmentIds(
statuses: PairedRuntimeParkingCapabilityStatuses
): ReadonlySet<string> {
const cached = pairedRuntimeParkingEnvironmentIdsCache
if (cached?.statuses === statuses) {
return cached.environmentIds
}
const capable = new Set<string>()
for (const [environmentId, entry] of statuses) {
// Why last-verified: a capability is a fact about the host's build, so an unverifiable
// probe must not unpark its terminals. See docs/reference/ssh-execution-boundary.md.
// Why the revoked gate: parking discards the client's only copy of the scrollback against
// a host-side restore, so a host that refused us must not keep promising one.
if (isRuntimeHostContactRevoked(entry)) {
continue
}
const status = lastVerifiedRuntimeStatus<PairedRuntimeParkingCapability>(entry)
if (status?.capabilities?.includes(TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY)) {
capable.add(environmentId)
}
}
const environmentIds =
cached && haveSameEnvironmentIds(cached.environmentIds, capable)
? cached.environmentIds
: capable
pairedRuntimeParkingEnvironmentIdsCache = { statuses, environmentIds }
return environmentIds
return selectParkingEnvironmentIds(statuses)
}
export function selectPairedRuntimeParkingEnvironmentIdsFromState(state: {
@@ -76,5 +83,5 @@ export function selectPairedRuntimeParkingEnvironmentIdsFromState(state: {
}
export function resetPairedRuntimeParkingEnvironmentIdsCacheForTest(): void {
pairedRuntimeParkingEnvironmentIdsCache = null
selectParkingEnvironmentIds = createPairedRuntimeParkingEnvironmentIdsSelector()
}
@@ -0,0 +1,75 @@
// @vitest-environment happy-dom
import { act } from 'react'
import { createRoot } from 'react-dom/client'
import { afterEach, expect, it } from 'vitest'
import { useAppStore } from '@/store'
import { useTerminalWorkspaceFoundation } from './use-terminal-workspace-foundation'
import type { RuntimeHostStatusSnapshot } from '../../../shared/runtime-host-status'
import { TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true)
const original = useAppStore.getState()
const container = document.createElement('div')
const root = createRoot(container)
afterEach(() => {
act(() => root.unmount())
useAppStore.setState(original, true)
})
it('does not render the workspace root for fresh evidence with unchanged parking', () => {
let renders = 0
function Probe() {
const { pairedRuntimeParkingEnvironmentIds } = useTerminalWorkspaceFoundation()
renders += 1
return <output>{[...pairedRuntimeParkingEnvironmentIds].join(',')}</output>
}
act(() => root.render(<Probe />))
const mountedRenders = renders
for (let checkedAt = 1; checkedAt <= 20; checkedAt += 1) {
const snapshot: RuntimeHostStatusSnapshot = {
environmentId: 'env-a',
pairingRevision: 1,
sequence: checkedAt,
checkedAt,
status: null,
verification: 'unavailable',
transport: 'ready'
}
act(() =>
useAppStore.setState({
runtimeStatusByEnvironmentId: new Map([['env-a', { status: null, checkedAt, snapshot }]])
})
)
}
expect(renders - mountedRenders).toBe(0)
const snapshot: RuntimeHostStatusSnapshot = {
environmentId: 'env-a',
pairingRevision: 1,
sequence: 21,
checkedAt: 21,
verification: 'verified',
transport: 'ready',
status: {
runtimeId: 'a',
rendererGraphEpoch: 1,
graphStatus: 'ready',
authoritativeWindowId: 1,
liveTabCount: 0,
liveLeafCount: 0,
capabilities: [TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY]
}
}
act(() =>
useAppStore.setState({
runtimeStatusByEnvironmentId: new Map([
['env-a', { status: snapshot.status, snapshot, checkedAt: 21 }]
])
})
)
expect(container.textContent).toBe('env-a')
expect(renders - mountedRenders).toBe(1)
act(() => useAppStore.getState().clearRuntimeEnvironmentStatus('env-a'))
expect(container.textContent).toBe('')
expect(renders - mountedRenders).toBe(2)
})
@@ -7,7 +7,7 @@ import { getResolvedExecutionHostIdForWorktree } from '@/lib/resolved-worktree-e
import type { WorktreeTabBucketProjection } from '@/lib/worktree-tab-bucket-projection'
import { projectWorkspaceSurfaces } from './workspace-surface-projection'
import { useReusedArrayIdentity } from './sidebar/worktree-list/listing/use-reused-array-identity'
import { selectPairedRuntimeParkingEnvironmentIds } from './terminal-pane/terminal-hidden-view-parking'
import { usePairedRuntimeParkingEnvironmentIds } from '@/store/use-runtime-status-view'
import { createTerminalWorktreeTopologyProjection } from './terminal-pane/terminal-hidden-worktree-retention'
import { isMainTerminalSideEffectAuthorityForPty } from './terminal-pane/terminal-side-effect-facts-handler'
import { selectFolderWorkspaces } from '@/store/workspace-record-selectors'
@@ -77,11 +77,7 @@ export function useTerminalWorkspaceFoundation() {
const terminalSshParkingEnabled = useAppStore(
(state) => state.settings?.terminalSshViewParking !== false
)
const runtimeStatusByEnvironmentId = useAppStore((state) => state.runtimeStatusByEnvironmentId)
const pairedRuntimeParkingEnvironmentIds = useMemo(
() => selectPairedRuntimeParkingEnvironmentIds(runtimeStatusByEnvironmentId),
[runtimeStatusByEnvironmentId]
)
const pairedRuntimeParkingEnvironmentIds = usePairedRuntimeParkingEnvironmentIds()
const terminalRetentionBudgetEnabled = useAppStore(
(state) => state.settings?.terminalHiddenWorktreeRetentionBudget !== false
)
@@ -115,7 +111,6 @@ export function useTerminalWorkspaceFoundation() {
pendingStartupByTabId,
terminalParkingEnabled,
terminalSshParkingEnabled,
runtimeStatusByEnvironmentId,
pairedRuntimeParkingEnvironmentIds,
terminalRetentionBudgetEnabled,
browserGuestRetentionBudgetEnabled,
@@ -0,0 +1,204 @@
import { createStore } from 'zustand/vanilla'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type {
RuntimeEnvironmentStatus,
RuntimeHostStatusSnapshot
} from '../../../shared/runtime-host-status'
import { TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
import * as connectionState from '@/runtime/runtime-host-connection-state'
import { createRuntimeStatusSubscriptions } from './runtime-status-subscriptions'
function entry(patch: Partial<RuntimeHostStatusSnapshot> = {}): RuntimeEnvironmentStatus {
const snapshot: RuntimeHostStatusSnapshot = {
environmentId: 'a',
pairingRevision: 1,
sequence: 1,
checkedAt: 1,
status: {
runtimeId: 'runtime-a',
rendererGraphEpoch: 1,
graphStatus: 'ready',
authoritativeWindowId: 1,
liveTabCount: 0,
liveLeafCount: 0,
capabilities: [TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY]
},
verification: 'verified',
transport: 'ready',
...patch
}
return {
snapshot,
status: snapshot.verification === 'verified' ? snapshot.status : null,
checkedAt: snapshot.checkedAt
}
}
function setup() {
const store = createStore(() => ({
runtimeStatusByEnvironmentId: new Map<string, RuntimeEnvironmentStatus>([
['a', entry()],
['b', entry({ environmentId: 'b' })]
]),
unrelated: 0
}))
const subscriptions = createRuntimeStatusSubscriptions(store)
function publish(environmentId: string, value: RuntimeEnvironmentStatus) {
store.setState({
runtimeStatusByEnvironmentId: new Map(store.getState().runtimeStatusByEnvironmentId).set(
environmentId,
value
)
})
}
return { store, subscriptions, publish }
}
afterEach(() => vi.restoreAllMocks())
describe('runtime status notification isolation', () => {
it.each([100, 1000])(
'derives once per affected host with %i readers and skips unrelated writes',
(readers) => {
const { store, subscriptions, publish } = setup()
const rootSubscribe = vi.spyOn(store, 'subscribe')
const derive = vi.spyOn(connectionState, 'runtimeHostConnectionStateForEntry')
const listeners = Array.from({ length: readers }, () => vi.fn())
const releases = listeners.map((listener) => subscriptions.subscribeConnection('a', listener))
const b = vi.fn()
releases.push(subscriptions.subscribeConnection('b', b))
const parking = vi.fn()
releases.push(subscriptions.subscribeParking(parking))
expect(rootSubscribe).toHaveBeenCalledTimes(1)
const oldPath = vi.fn((state) =>
connectionState.runtimeHostConnectionStateForEntry(
state.runtimeStatusByEnvironmentId.get('a')
)
)
const releaseControl = store.subscribe(oldPath)
derive.mockClear()
store.setState({ unrelated: 1 })
expect(derive).toHaveBeenCalledTimes(1)
expect(oldPath).toHaveBeenCalledTimes(1)
releaseControl()
derive.mockClear()
publish('b', entry({ environmentId: 'b', sequence: 2, checkedAt: 2 }))
expect(derive).toHaveBeenCalledTimes(1)
expect(b).not.toHaveBeenCalled()
expect(parking).not.toHaveBeenCalled()
expect(listeners.every((listener) => listener.mock.calls.length === 0)).toBe(true)
publish('a', entry({ verification: 'blocked' }))
expect(derive).toHaveBeenCalledTimes(2)
expect(listeners.every((listener) => listener.mock.calls.length === 1)).toBe(true)
expect(subscriptions.getParkingSnapshot()).toEqual(new Set(['b']))
expect(parking).toHaveBeenCalledTimes(1)
releases.forEach((release) => release())
}
)
it('retains parking through lost contact, withdraws it on refusal, and handles deletion/reset', () => {
const { store, subscriptions, publish } = setup()
const parking = vi.fn()
const connection = vi.fn()
const releaseParking = subscriptions.subscribeParking(parking)
const releaseConnection = subscriptions.subscribeConnection('a', connection)
const initialParking = subscriptions.getParkingSnapshot()
publish('a', entry({ verification: 'unavailable', transport: 'disconnected' }))
expect(subscriptions.getConnectionSnapshot('a')).toBe('reconnecting')
expect(subscriptions.getParkingSnapshot()).toBe(initialParking)
expect(parking).not.toHaveBeenCalled()
publish('a', entry({ verification: 'blocked', blockedReason: 'access-revoked' }))
expect(subscriptions.getConnectionSnapshot('a')).toBe('disconnected')
expect(subscriptions.getParkingSnapshot()).toEqual(new Set(['b']))
store.setState({ runtimeStatusByEnvironmentId: new Map() })
expect(subscriptions.getConnectionSnapshot('a')).toBe('checking')
expect(subscriptions.getParkingSnapshot()).toEqual(new Set())
publish('a', entry())
expect(subscriptions.getConnectionSnapshot('a')).toBe('connected')
expect(subscriptions.getParkingSnapshot()).toEqual(new Set(['a']))
expect(connection).toHaveBeenCalledTimes(4)
expect(parking).toHaveBeenCalledTimes(3)
releaseParking()
releaseConnection()
})
it('reads current authoritative state before subscription delivery and after a late subscription', () => {
const { store, subscriptions, publish } = setup()
const seen: string[] = []
const earlier = store.subscribe(() => {
seen.push(subscriptions.getConnectionSnapshot('a'))
expect(subscriptions.getParkingSnapshot()).toEqual(new Set(['b']))
})
const release = subscriptions.subscribeConnection('a', () => {})
publish('a', entry({ retired: true }))
expect(seen).toEqual(['disconnected'])
earlier()
release()
publish('a', entry())
expect(subscriptions.getConnectionSnapshot('a')).toBe('connected')
const notified = vi.fn()
const lateRelease = subscriptions.subscribeConnection('a', notified)
publish('a', entry({ verification: 'blocked' }))
expect(notified).toHaveBeenCalledTimes(1)
lateRelease()
})
it('keeps baselines current through nested commits and listener removal', () => {
const { store, subscriptions, publish } = setup()
const observed: string[] = []
let nested = false
const first = subscriptions.subscribeConnection('a', () => {
if (!nested) {
nested = true
publish('a', entry())
}
})
const second = subscriptions.subscribeConnection('a', () =>
observed.push(subscriptions.getConnectionSnapshot('a'))
)
publish('a', entry({ verification: 'blocked' }))
expect(observed).toEqual(['connected', 'connected'])
const length = observed.length
store.setState({ unrelated: 2 })
publish('a', entry({ sequence: 3 }))
expect(observed).toHaveLength(length)
first()
second()
const removed = vi.fn()
let remove = () => {}
const remover = subscriptions.subscribeConnection('a', () => remove())
remove = subscriptions.subscribeConnection('a', removed)
publish('a', entry({ verification: 'blocked' }))
expect(removed).not.toHaveBeenCalled()
remover()
})
it('releases the shared root listener and supports repeated cleanup and store isolation', () => {
const { store, subscriptions } = setup()
const unsubscribe = vi.fn()
const originalSubscribe = store.subscribe
vi.spyOn(store, 'subscribe').mockImplementation((listener) => {
const release = originalSubscribe(listener)
return () => {
unsubscribe()
release()
}
})
const release = subscriptions.subscribeConnection('a', () => {})
release()
release()
expect(unsubscribe).toHaveBeenCalledTimes(1)
const listener = vi.fn()
const next = subscriptions.subscribeConnection('a', listener)
release()
const other = setup()
other.publish('a', entry({ retired: true }))
expect(listener).not.toHaveBeenCalled()
expect(subscriptions.getConnectionSnapshot('a')).toBe('connected')
expect(other.subscriptions.getConnectionSnapshot('a')).toBe('disconnected')
store.setState({ runtimeStatusByEnvironmentId: new Map() })
expect(listener).toHaveBeenCalledTimes(1)
next()
expect(unsubscribe).toHaveBeenCalledTimes(2)
})
})
@@ -0,0 +1,141 @@
import type { RuntimeEnvironmentStatus } from '../../../shared/runtime-host-status'
import {
runtimeHostConnectionStateForEntry,
type RuntimeHostConnectionState
} from '@/runtime/runtime-host-connection-state'
import { createPairedRuntimeParkingEnvironmentIdsSelector } from '@/components/terminal-pane/paired-runtime-parking-capabilities'
type RuntimeStatusState = {
runtimeStatusByEnvironmentId: ReadonlyMap<string, RuntimeEnvironmentStatus>
}
type RuntimeStatusStore = {
getState: () => RuntimeStatusState
subscribe: (listener: () => void) => () => void
}
type Listener = () => void
type ConnectionSubscription = {
snapshot: RuntimeHostConnectionState
listeners: Set<Listener>
}
export function createRuntimeStatusSubscriptions(store: RuntimeStatusStore) {
const connections = new Map<string, ConnectionSubscription>()
const parkingListeners = new Set<Listener>()
let selectParking = createPairedRuntimeParkingEnvironmentIdsSelector()
let parkingSnapshot: ReadonlySet<string> | null = null
let previousStatuses: RuntimeStatusState['runtimeStatusByEnvironmentId'] | null = null
let unsubscribeStore: (() => void) | null = null
function getConnectionSnapshot(environmentId: string | null): RuntimeHostConnectionState {
return runtimeHostConnectionStateForEntry(
environmentId === null
? undefined
: store.getState().runtimeStatusByEnvironmentId.get(environmentId)
)
}
function getParkingSnapshot(): ReadonlySet<string> {
return selectParking(store.getState().runtimeStatusByEnvironmentId)
}
function ensureSubscription(): void {
if (unsubscribeStore) {
return
}
previousStatuses = store.getState().runtimeStatusByEnvironmentId
unsubscribeStore = store.subscribe(() => {
const statuses = store.getState().runtimeStatusByEnvironmentId
const previous = previousStatuses
if (statuses === previous) {
return
}
previousStatuses = statuses
const pending: { listeners: Set<Listener>; listener: Listener }[] = []
for (const [environmentId, subscription] of connections) {
if (statuses.get(environmentId) === previous?.get(environmentId)) {
continue
}
const snapshot = runtimeHostConnectionStateForEntry(statuses.get(environmentId))
if (snapshot === subscription.snapshot) {
continue
}
subscription.snapshot = snapshot
for (const listener of subscription.listeners) {
pending.push({ listeners: subscription.listeners, listener })
}
}
if (parkingListeners.size > 0) {
const snapshot = getParkingSnapshot()
if (snapshot !== parkingSnapshot) {
parkingSnapshot = snapshot
for (const listener of parkingListeners) {
pending.push({ listeners: parkingListeners, listener })
}
}
}
// Establish every comparison baseline before callbacks can commit nested writes.
for (const { listeners, listener } of pending) {
if (listeners.has(listener)) {
listener()
}
}
})
}
function releaseIfUnused(): void {
if (connections.size > 0 || parkingListeners.size > 0) {
return
}
unsubscribeStore?.()
unsubscribeStore = null
previousStatuses = null
parkingSnapshot = null
selectParking = createPairedRuntimeParkingEnvironmentIdsSelector()
}
function subscribeConnection(environmentId: string | null, listener: Listener): () => void {
if (environmentId === null) {
return () => {}
}
ensureSubscription()
let subscription = connections.get(environmentId)
if (!subscription) {
subscription = { snapshot: getConnectionSnapshot(environmentId), listeners: new Set() }
connections.set(environmentId, subscription)
}
const registered = () => listener()
subscription.listeners.add(registered)
let released = false
return () => {
if (released) {
return
}
released = true
subscription.listeners.delete(registered)
if (subscription.listeners.size === 0) {
connections.delete(environmentId)
}
releaseIfUnused()
}
}
function subscribeParking(listener: Listener): () => void {
ensureSubscription()
if (parkingListeners.size === 0) {
parkingSnapshot = getParkingSnapshot()
}
const registered = () => listener()
parkingListeners.add(registered)
let released = false
return () => {
if (released) {
return
}
released = true
parkingListeners.delete(registered)
releaseIfUnused()
}
}
return { getConnectionSnapshot, subscribeConnection, getParkingSnapshot, subscribeParking }
}
@@ -0,0 +1,116 @@
// @vitest-environment happy-dom
import { act, StrictMode, useLayoutEffect } from 'react'
import { createRoot } from 'react-dom/client'
import { expect, it } from 'vitest'
import { useAppStore } from './index'
import { readStoreListenerCount } from './store-listener-census'
import {
usePairedRuntimeParkingEnvironmentIds,
useRuntimeHostDisconnected
} from './use-runtime-status-view'
Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true)
it('shares one root subscription, rebinds host scopes, and releases Strict Mode remounts', () => {
const original = useAppStore.getState()
useAppStore.setState({ runtimeStatusByEnvironmentId: new Map() })
const baseline = readStoreListenerCount()
const container = document.createElement('div')
const root = createRoot(container)
let renders = 0
function Probe({ environmentId }: { environmentId: string | null }) {
renders += 1
const disconnected = useRuntimeHostDisconnected(environmentId)
const parking = usePairedRuntimeParkingEnvironmentIds()
return (
<span>
{String(disconnected)}:{parking.size}
</span>
)
}
try {
for (const environmentId of ['a', 'b', null, 'a']) {
act(() =>
root.render(
<StrictMode>
<Probe environmentId={environmentId} />
<Probe environmentId={environmentId} />
</StrictMode>
)
)
expect(readStoreListenerCount()).toBe((baseline ?? 0) + 1)
expect(container.textContent).toBe('false:0false:0')
if (environmentId !== null) {
const mountedRenders = renders
act(() =>
useAppStore.getState().setRuntimeEnvironmentStatus(environmentId, {
status: null,
checkedAt: 1,
snapshot: {
environmentId,
pairingRevision: 1,
checkedAt: 1,
sequence: 1,
status: null,
verification: 'unavailable',
transport: 'ready'
}
})
)
expect(renders).toBe(mountedRenders)
act(() =>
useAppStore.getState().setRuntimeEnvironmentStatus(environmentId, {
status: null,
checkedAt: 2,
snapshot: {
environmentId,
pairingRevision: 1,
checkedAt: 2,
sequence: 2,
status: null,
verification: 'blocked',
transport: 'ready'
}
})
)
expect(container.textContent).toBe('true:0true:0')
act(() => useAppStore.getState().clearRuntimeEnvironmentStatus(environmentId))
expect(container.textContent).toBe('false:0false:0')
}
}
} finally {
act(() => root.unmount())
expect(readStoreListenerCount()).toBe(baseline)
useAppStore.setState(original, true)
}
})
it('observes a commit between render and subscription without waiting for another publication', () => {
const original = useAppStore.getState()
useAppStore.setState({ runtimeStatusByEnvironmentId: new Map() })
const container = document.createElement('div')
const root = createRoot(container)
function Probe() {
return <output>{String(useRuntimeHostDisconnected('race'))}</output>
}
function CommitBeforeSubscription() {
useLayoutEffect(() => {
useAppStore.getState().setRuntimeEnvironmentStatus('race', { status: null, checkedAt: 1 })
}, [])
return null
}
try {
act(() =>
root.render(
<>
<Probe />
<CommitBeforeSubscription />
</>
)
)
expect(container.textContent).toBe('true')
} finally {
act(() => root.unmount())
useAppStore.setState(original, true)
}
})
@@ -0,0 +1,30 @@
import { useCallback, useSyncExternalStore } from 'react'
import { useAppStore } from './index'
import { createRuntimeStatusSubscriptions } from './runtime-status-subscriptions'
import { isDisconnectedRuntimeHostState } from '@/runtime/runtime-host-connection-state'
const runtimeStatusSubscriptions = createRuntimeStatusSubscriptions(useAppStore)
export function useRuntimeHostDisconnected(environmentId: string | null) {
const subscribe = useCallback(
(listener: () => void) =>
runtimeStatusSubscriptions.subscribeConnection(environmentId, listener),
[environmentId]
)
const getSnapshot = useCallback(
() =>
isDisconnectedRuntimeHostState(
runtimeStatusSubscriptions.getConnectionSnapshot(environmentId)
),
[environmentId]
)
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
}
export function usePairedRuntimeParkingEnvironmentIds() {
return useSyncExternalStore(
runtimeStatusSubscriptions.subscribeParking,
runtimeStatusSubscriptions.getParkingSnapshot,
runtimeStatusSubscriptions.getParkingSnapshot
)
}
@@ -0,0 +1,136 @@
import { test, expect } from './helpers/orca-app'
import {
seedAccumulatedWorkspaceFixture,
cleanupAccumulatedWorkspaceFixture
} from './accumulated-workspace-fixture'
import {
ensureTerminalVisible,
getAllWorktreeIds,
switchToWorktree,
waitForActiveWorktree,
waitForSessionReady
} from './helpers/store'
import { waitForActiveTerminalManager, waitForTerminalOutput } from './helpers/terminal'
test('runtime probe evidence preserves palette switching and terminal output at workspace scale', async ({
orcaPage,
electronApp
}, testInfo) => {
await waitForSessionReady(orcaPage)
const source = await waitForActiveWorktree(orcaPage)
const target = (await getAllWorktreeIds(orcaPage)).find((id) => id !== source)
if (!target) {
throw new Error('Expected a second seeded workspace')
}
await switchToWorktree(orcaPage, target)
await ensureTerminalVisible(orcaPage)
await waitForActiveTerminalManager(orcaPage)
const targetName = await orcaPage.evaluate(
(id) =>
window
.__store!.getState()
.getKnownWorktreeById(id)
?.branch.replace(/^refs\/heads\//, ''),
target
)
if (!targetName) {
throw new Error('Missing workspace name')
}
await switchToWorktree(orcaPage, source)
const fixture = await seedAccumulatedWorkspaceFixture(orcaPage)
const cdp = await orcaPage.context().newCDPSession(orcaPage)
await cdp.send('Performance.enable')
const beforeMetrics = await cdp.send('Performance.getMetrics')
const publications: number[] = []
const gaps: number[] = []
const traffic = orcaPage.evaluate(async () => {
const store = window.__store!
const durations: number[] = []
const gaps: number[] = []
let last = performance.now()
for (let sequence = 1; sequence <= 120; sequence += 1) {
await new Promise((resolve) => setTimeout(resolve, 16))
const now = performance.now()
gaps.push(now - last)
last = now
const start = performance.now()
store.getState().setRuntimeEnvironmentStatus(
'probe-fixture',
{
checkedAt: sequence,
status: null,
snapshot: {
environmentId: 'probe-fixture',
pairingRevision: 1,
sequence,
checkedAt: sequence,
status: null,
verification: 'unavailable',
transport: 'ready'
}
},
{ suppressDisconnectToast: true }
)
durations.push(performance.now() - start)
}
store.getState().clearRuntimeEnvironmentStatus('probe-fixture')
return { durations, gaps }
})
try {
await orcaPage.evaluate(() => window.__store!.getState().openModal('worktree-palette'))
const palette = orcaPage.getByRole('dialog', { name: 'Jump to...' })
await expect(palette).toBeVisible()
await palette.getByRole('combobox').fill(targetName)
await expect(palette.locator('[cmdk-item][data-selected="true"]')).toContainText(targetName)
await palette.screenshot({ path: testInfo.outputPath('palette-during-probes.png') })
await palette.getByRole('combobox').press('Enter')
await expect(palette).toBeHidden()
const row = orcaPage.locator(
`[data-worktree-sidebar] [role="option"][data-worktree-id=${JSON.stringify(target)}]`
)
await expect(row).toHaveAttribute('aria-current', 'page')
await waitForActiveTerminalManager(orcaPage)
const terminal = orcaPage
.locator('[data-terminal-tab-id]:visible .xterm-helper-textarea')
.first()
await terminal.fill('echo RUNTIME_PROBE_TYPING_OK')
await terminal.press('Enter')
await waitForTerminalOutput(orcaPage, 'RUNTIME_PROBE_TYPING_OK')
await orcaPage.screenshot({ path: testInfo.outputPath('terminal-after-switch.png') })
const measured = await traffic
publications.push(...measured.durations)
gaps.push(...measured.gaps)
expect(publications).toHaveLength(120)
expect(
await electronApp.evaluate(({ BrowserWindow }) =>
BrowserWindow.getAllWindows().every((window) => !window.isVisible())
)
).toBe(true)
const afterMetrics = await cdp.send('Performance.getMetrics')
const metric = (metrics: { metrics: { name: string; value: number }[] }, name: string) =>
metrics.metrics.find((metric) => metric.name === name)?.value ?? 0
const sorted = [...publications].sort((a, b) => a - b)
const report = {
fixture,
publications: publications.length,
publicationMs: {
median: sorted[Math.floor(sorted.length / 2)],
max: Math.max(...publications)
},
timerGapMs: { max: Math.max(...gaps) },
rendererTaskMs:
(metric(afterMetrics, 'TaskDuration') - metric(beforeMetrics, 'TaskDuration')) * 1000,
heapUsedDelta:
metric(afterMetrics, 'JSHeapUsedSize') - metric(beforeMetrics, 'JSHeapUsedSize')
}
console.log('[runtime-probe-workspace]', JSON.stringify(report))
await testInfo.attach('runtime-probe-workspace', {
body: JSON.stringify(report, null, 2),
contentType: 'application/json'
})
} finally {
await traffic
await cdp.detach()
await cleanupAccumulatedWorkspaceFixture(orcaPage)
}
})