mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
perf(dashboard): ignore unrelated store churn
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import type { DashboardBucket } from '../../../../shared/dashboard-snapshot'
|
||||
import { buildDashboardSnapshot } from './build-dashboard-snapshot'
|
||||
|
||||
@@ -13,19 +14,33 @@ const EMPTY_COUNTS: AgentBucketCounts = { attention: 0, working: 0, idle: 0 }
|
||||
* only when an input slice changes (mirrors useDashboardData's cost profile).
|
||||
*/
|
||||
export function useAgentBucketCounts(): AgentBucketCounts {
|
||||
const repos = useAppStore((s) => s.repos)
|
||||
const worktreesByRepo = useAppStore((s) => s.worktreesByRepo)
|
||||
const tabsByWorktree = useAppStore((s) => s.tabsByWorktree)
|
||||
const agentStatusByPaneKey = useAppStore((s) => s.agentStatusByPaneKey)
|
||||
const retainedAgentsByPaneKey = useAppStore((s) => s.retainedAgentsByPaneKey)
|
||||
const migrationUnsupportedByPtyId = useAppStore((s) => s.migrationUnsupportedByPtyId)
|
||||
const runtimeAgentOrchestrationByPaneKey = useAppStore(
|
||||
(s) => s.runtimeAgentOrchestrationByPaneKey
|
||||
const {
|
||||
repos,
|
||||
worktreesByRepo,
|
||||
tabsByWorktree,
|
||||
agentStatusByPaneKey,
|
||||
retainedAgentsByPaneKey,
|
||||
migrationUnsupportedByPtyId,
|
||||
runtimeAgentOrchestrationByPaneKey,
|
||||
terminalLayoutsByTabId,
|
||||
ptyIdsByTabId,
|
||||
runtimePaneTitlesByTabId,
|
||||
agentStatusEpoch
|
||||
} = useAppStore(
|
||||
useShallow((s) => ({
|
||||
repos: s.repos,
|
||||
worktreesByRepo: s.worktreesByRepo,
|
||||
tabsByWorktree: s.tabsByWorktree,
|
||||
agentStatusByPaneKey: s.agentStatusByPaneKey,
|
||||
retainedAgentsByPaneKey: s.retainedAgentsByPaneKey,
|
||||
migrationUnsupportedByPtyId: s.migrationUnsupportedByPtyId,
|
||||
runtimeAgentOrchestrationByPaneKey: s.runtimeAgentOrchestrationByPaneKey,
|
||||
terminalLayoutsByTabId: s.terminalLayoutsByTabId,
|
||||
ptyIdsByTabId: s.ptyIdsByTabId,
|
||||
runtimePaneTitlesByTabId: s.runtimePaneTitlesByTabId,
|
||||
agentStatusEpoch: s.agentStatusEpoch
|
||||
}))
|
||||
)
|
||||
const terminalLayoutsByTabId = useAppStore((s) => s.terminalLayoutsByTabId)
|
||||
const ptyIdsByTabId = useAppStore((s) => s.ptyIdsByTabId)
|
||||
const runtimePaneTitlesByTabId = useAppStore((s) => s.runtimePaneTitlesByTabId)
|
||||
const agentStatusEpoch = useAppStore((s) => s.agentStatusEpoch)
|
||||
|
||||
return useMemo(() => {
|
||||
const snapshot = buildDashboardSnapshot(
|
||||
|
||||
@@ -34,7 +34,31 @@ vi.mock('@/lib/activate-tab-and-focus-pane', () => ({
|
||||
activateTabAndFocusPane: vi.fn()
|
||||
}))
|
||||
|
||||
import { useDashboardPopoutBridge } from './useDashboardPopoutBridge'
|
||||
import {
|
||||
dashboardSnapshotInputsChanged,
|
||||
useDashboardPopoutBridge
|
||||
} from './useDashboardPopoutBridge'
|
||||
import type { DashboardSnapshotState } from './build-dashboard-snapshot'
|
||||
import type { AppState } from '@/store/types'
|
||||
|
||||
type DashboardSnapshotWatchState = DashboardSnapshotState & Pick<AppState, 'agentStatusEpoch'>
|
||||
|
||||
function makeSnapshotWatchState(): DashboardSnapshotWatchState {
|
||||
return {
|
||||
repos: [],
|
||||
worktreesByRepo: {},
|
||||
tabsByWorktree: {},
|
||||
agentStatusByPaneKey: {},
|
||||
retainedAgentsByPaneKey: {},
|
||||
migrationUnsupportedByPtyId: {},
|
||||
runtimeAgentOrchestrationByPaneKey: {},
|
||||
terminalLayoutsByTabId: {},
|
||||
ptyIdsByTabId: {},
|
||||
runtimePaneTitlesByTabId: {},
|
||||
acknowledgedAgentsByPaneKey: {},
|
||||
agentStatusEpoch: 0
|
||||
}
|
||||
}
|
||||
|
||||
function Harness({ enabled }: { enabled: boolean }): null {
|
||||
useDashboardPopoutBridge(enabled)
|
||||
@@ -80,6 +104,34 @@ describe('useDashboardPopoutBridge', () => {
|
||||
expect(mocks.subscribeStore).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('ignores unrelated store writes while retaining every snapshot input', () => {
|
||||
const previousState = makeSnapshotWatchState()
|
||||
expect(dashboardSnapshotInputsChanged({ ...previousState }, previousState)).toBe(false)
|
||||
|
||||
const referenceInputs = [
|
||||
'repos',
|
||||
'worktreesByRepo',
|
||||
'tabsByWorktree',
|
||||
'agentStatusByPaneKey',
|
||||
'retainedAgentsByPaneKey',
|
||||
'migrationUnsupportedByPtyId',
|
||||
'runtimeAgentOrchestrationByPaneKey',
|
||||
'terminalLayoutsByTabId',
|
||||
'ptyIdsByTabId',
|
||||
'runtimePaneTitlesByTabId',
|
||||
'acknowledgedAgentsByPaneKey'
|
||||
] as const
|
||||
for (const key of referenceInputs) {
|
||||
expect(
|
||||
dashboardSnapshotInputsChanged({ ...previousState, [key]: {} }, previousState),
|
||||
key
|
||||
).toBe(true)
|
||||
}
|
||||
expect(
|
||||
dashboardSnapshotInputsChanged({ ...previousState, agentStatusEpoch: 1 }, previousState)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('releases every dashboard listener when the experiment is disabled', async () => {
|
||||
await act(async () => root.render(<Harness enabled />))
|
||||
|
||||
|
||||
@@ -1,13 +1,36 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useAppStore, type AppState } from '@/store'
|
||||
import { activateTabAndFocusPane } from '@/lib/activate-tab-and-focus-pane'
|
||||
import { buildDashboardSnapshot } from './build-dashboard-snapshot'
|
||||
import { buildDashboardSnapshot, type DashboardSnapshotState } from './build-dashboard-snapshot'
|
||||
|
||||
// Why: cap snapshot rebuilds during bursts of agent-status pings. The board is a
|
||||
// glanceable surface, so ~4 updates/sec is plenty and keeps the cross-worktree
|
||||
// rebuild off the hot path.
|
||||
const PUBLISH_THROTTLE_MS = 250
|
||||
|
||||
type DashboardSnapshotWatchState = DashboardSnapshotState & Pick<AppState, 'agentStatusEpoch'>
|
||||
|
||||
export function dashboardSnapshotInputsChanged(
|
||||
state: DashboardSnapshotWatchState,
|
||||
previousState: DashboardSnapshotWatchState
|
||||
): boolean {
|
||||
return (
|
||||
state.repos !== previousState.repos ||
|
||||
state.worktreesByRepo !== previousState.worktreesByRepo ||
|
||||
state.tabsByWorktree !== previousState.tabsByWorktree ||
|
||||
state.agentStatusByPaneKey !== previousState.agentStatusByPaneKey ||
|
||||
state.retainedAgentsByPaneKey !== previousState.retainedAgentsByPaneKey ||
|
||||
state.migrationUnsupportedByPtyId !== previousState.migrationUnsupportedByPtyId ||
|
||||
state.runtimeAgentOrchestrationByPaneKey !== previousState.runtimeAgentOrchestrationByPaneKey ||
|
||||
state.terminalLayoutsByTabId !== previousState.terminalLayoutsByTabId ||
|
||||
state.ptyIdsByTabId !== previousState.ptyIdsByTabId ||
|
||||
state.runtimePaneTitlesByTabId !== previousState.runtimePaneTitlesByTabId ||
|
||||
state.acknowledgedAgentsByPaneKey !== previousState.acknowledgedAgentsByPaneKey ||
|
||||
// Why: freshness can change a bucket without replacing any backing map.
|
||||
state.agentStatusEpoch !== previousState.agentStatusEpoch
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs in the MAIN window (mount once in App). Two responsibilities:
|
||||
* 1. While the pop-out dashboard is open, derive the snapshot from the live
|
||||
@@ -88,7 +111,12 @@ export function useDashboardPopoutBridge(enabled: boolean): void {
|
||||
open = next
|
||||
if (open) {
|
||||
if (!unsubscribeStore) {
|
||||
unsubscribeStore = useAppStore.subscribe(publishThrottled)
|
||||
unsubscribeStore = useAppStore.subscribe((state, previousState) => {
|
||||
// Why: unrelated high-frequency store writes must not rebuild a cross-worktree snapshot.
|
||||
if (dashboardSnapshotInputsChanged(state, previousState)) {
|
||||
publishThrottled()
|
||||
}
|
||||
})
|
||||
}
|
||||
publishNow()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user