perf(renderer): memoize the sleeping-record park exemption on slice identity

A pane title update writes runtimePaneTitlesByTabId, but zustand re-runs every
mounted subscriber's selector, so each retained worktree walked the whole
sleeping-agent inventory to conclude nothing changed for it. useShallow
suppressed the re-render, never the scan.

selectSleepingRecordParkExemptTabIds now goes through the existing
createWorktreeRecordSelector generation cache, keyed on the record-map
identity, so the walk happens once per worktree per real inventory change
instead of once per store write. The cache moves from components/sidebar to
store/ now that terminal-pane shares it, and takes an isEmpty override so a
Set-valued selector can use it.

19,711 sleeping-record reads -> 0 for one title update at capture scale.

Refs STA-7552, STA-7551
This commit is contained in:
Jinwoo-H
2026-09-17 16:51:26 -04:00
parent 4a02d97fb7
commit 2ceb85c1c8
7 changed files with 124 additions and 73 deletions
@@ -13,7 +13,7 @@ import {
recordLiveEntriesFullRebuild
} from './worktree-agent-live-index-patch'
import { selectWorktreeAgentOrchestration } from './worktree-agent-orchestration-index'
import { createWorktreeRecordSelector } from './worktree-record-selector-cache'
import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache'
import type { TerminalLayoutSnapshot } from '../../../../shared/terminal-tab-types'
// Why frozen and exported: card hooks return these from their inactive branch,
@@ -1,6 +1,6 @@
import type { AppState } from '@/store/types'
import type { TerminalPaneLayoutNode } from '../../../../shared/terminal-tab-types'
import { createWorktreeRecordSelector } from './worktree-record-selector-cache'
import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache'
// Why: these selectors return fresh maps whose top-level values preserve
// underlying per-tab references, so callers must compare them shallowly.
@@ -33,7 +33,8 @@ import type { AgentStatusEntry } from '../../../../shared/agent-status-types'
import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume'
import type { TerminalTab } from '../../../../shared/terminal-tab-types'
import { useTerminalTabColdParking } from './use-terminal-tab-cold-parking'
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true)
const WORKSPACE_COUNT = 870
const TERMINAL_TAB_COUNT = 1408
@@ -82,15 +83,16 @@ function buildTabsByWorktree(): Record<string, TerminalTab[]> {
for (const [index, worktreeId] of worktreeIds.entries()) {
const count = Math.min(remaining, index < MOUNTED_WORKTREE_COUNT ? 4 : 2)
remaining -= count
tabsByWorktree[worktreeId] = Array.from(
{ length: count },
(_, tabIndex) =>
({
id: `tab-${index}-${tabIndex}`,
title: `tab ${tabIndex}`,
ptyId: `${worktreeId}@@pty-${tabIndex}`
}) as TerminalTab
)
tabsByWorktree[worktreeId] = Array.from({ length: count }, (_, tabIndex) => ({
id: `tab-${index}-${tabIndex}`,
ptyId: `${worktreeId}@@pty-${tabIndex}`,
worktreeId,
title: `tab ${tabIndex}`,
customTitle: null,
color: null,
sortOrder: tabIndex,
createdAt: 0
}))
if (remaining <= 0) {
break
}
@@ -107,15 +109,18 @@ function buildSleepingRecords(): Record<string, SleepingAgentSessionRecord> {
const worktreeId = worktreeIds[index % WORKSPACE_COUNT]
const tabId = tabsByWorktree[worktreeId]?.[0]?.id ?? `tab-${index}-0`
const paneKey = `${tabId}:1`
return [
const record: SleepingAgentSessionRecord = {
paneKey,
{
paneKey,
tabId,
worktreeId,
providerSession: { id: `session-${index}` }
} as unknown as SleepingAgentSessionRecord
] as const
tabId,
worktreeId,
agent: 'claude',
providerSession: { key: 'session_id', id: `session-${index}` },
prompt: 'prompt',
state: 'working',
capturedAt: 1,
updatedAt: 1
}
return [paneKey, record] as const
}),
'sleepingRecords'
)
@@ -127,17 +132,16 @@ function buildAgentStatuses(): Record<string, AgentStatusEntry> {
Array.from({ length: AGENT_STATUS_COUNT }, (_, index) => {
const tabId = seededTerminalTabs[index % seededTerminalTabs.length].id
const paneKey = `${tabId}:1`
return [
const entry: AgentStatusEntry = {
paneKey,
{
paneKey,
state: 'working',
updatedAt: now,
stateStartedAt: now,
stateHistory: [],
agentType: 'claude'
} as unknown as AgentStatusEntry
] as const
state: 'working',
prompt: 'prompt',
updatedAt: now,
stateStartedAt: now,
stateHistory: [],
agentType: 'claude'
}
return [paneKey, entry] as const
}),
'agentStatusRows'
)
@@ -175,7 +179,7 @@ function mountAtCaptureScale(): void {
agentStatusEpoch: 1,
activeWorktreeId: TARGET_WORKTREE_ID,
runtimePaneTitlesByTabId: { [TARGET_TAB_ID]: { [TARGET_PANE_ID]: 'initial title' } }
} as never)
})
container = document.createElement('div')
document.body.appendChild(container)
@@ -212,10 +216,8 @@ afterEach(() => {
useAppStore.setState(originalState, true)
})
// Why `it.fails` in this commit: it pins the before-state in history. The fix
// commit that follows flips both back to `it`.
describe('one pane title update at live-capture scale', () => {
it.fails('walks no global inventory', () => {
it('walks no global inventory', () => {
mountAtCaptureScale()
reads.sleepingRecords = 0
reads.agentStatusRows = 0
@@ -227,7 +229,7 @@ describe('one pane title update at live-capture scale', () => {
expect(MAIN_SLEEPING_RECORD_READS).toBe(SLEEPING_RECORD_COUNT * 23)
})
it.fails('stays flat as unrelated workspaces accumulate', () => {
it('stays flat as unrelated workspaces accumulate', () => {
mountAtCaptureScale()
reads.sleepingRecords = 0
applyOnePaneTitleUpdate('title a')
@@ -246,7 +248,7 @@ describe('one pane title update at live-capture scale', () => {
reads.sleepingRecords = 0
act(() => {
useAppStore.setState({ sleepingAgentSessionsByPaneKey: buildSleepingRecords() } as never)
useAppStore.setState({ sleepingAgentSessionsByPaneKey: buildSleepingRecords() })
})
// Why: correctness floor — a real inventory change must still be observed.
@@ -19,6 +19,12 @@ function sleepingRecord(
}
}
function stateWith(sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord>): {
sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord>
} {
return { sleepingAgentSessionsByPaneKey }
}
describe('selectSleepingRecordParkExemptTabIds', () => {
it.each([
[`tab-1:${LEAF_ID}`, 'tab-1'],
@@ -26,14 +32,16 @@ describe('selectSleepingRecordParkExemptTabIds', () => {
])('derives the owner from a valid pane key (%s)', (paneKey, tabId) => {
const records = { [paneKey]: sleepingRecord({ paneKey }) }
expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual([tabId])
expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([tabId])
})
it('prefers the persisted tab id over the pane key owner', () => {
const paneKey = `tab-stale:${LEAF_ID}`
const records = { [paneKey]: sleepingRecord({ paneKey, tabId: 'tab-current' }) }
expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual(['tab-current'])
expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([
'tab-current'
])
})
it('does not invent an owner for a delimiter-less pane key', () => {
@@ -41,6 +49,30 @@ describe('selectSleepingRecordParkExemptTabIds', () => {
'orphan-pane-key': sleepingRecord({ paneKey: 'orphan-pane-key' })
}
expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual([])
expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([])
})
it('rebuilds when the record map changes and reuses the result when it does not', () => {
const paneKey = `tab-1:${LEAF_ID}`
const records = { [paneKey]: sleepingRecord({ paneKey }) }
const state = stateWith(records)
const first = selectSleepingRecordParkExemptTabIds(state, 'wt-1')
expect(selectSleepingRecordParkExemptTabIds(state, 'wt-1')).toBe(first)
const nextPaneKey = `tab-2:${LEAF_ID}`
const grown = stateWith({ ...records, [nextPaneKey]: sleepingRecord({ paneKey: nextPaneKey }) })
expect([...selectSleepingRecordParkExemptTabIds(grown, 'wt-1')]).toEqual(['tab-1', 'tab-2'])
})
// Why: a memo that serves a stale generation after the workspace's records are
// dropped would pin a hidden pane mounted for the rest of the session.
it('drops a worktree exemption once its records leave the map', () => {
const paneKey = `tab-1:${LEAF_ID}`
const populated = stateWith({ [paneKey]: sleepingRecord({ paneKey }) })
expect([...selectSleepingRecordParkExemptTabIds(populated, 'wt-1')]).toEqual(['tab-1'])
expect([...selectSleepingRecordParkExemptTabIds(stateWith({}), 'wt-1')]).toEqual([])
})
})
@@ -1,41 +1,58 @@
import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume'
import { parseLegacyNumericPaneKey, parsePaneKey } from '../../../../shared/stable-pane-id'
import { isPassiveCompletedHibernationEvidence } from '../../lib/sleeping-agent-pane-ownership'
import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache'
const EMPTY_TAB_IDS: ReadonlySet<string> = new Set()
export type SleepingRecordParkExemptionState = {
sleepingAgentSessionsByPaneKey?: Record<string, SleepingAgentSessionRecord> | undefined
}
/** Tab ids whose panes own a sleeping record a mount can actually consume.
* Why: a parked pane can never cold-restore, so per-tab parks must exempt
* these — but only these: passive-completed records never resume,
* and exempting them would pin a hidden pane mounted indefinitely.
* Callers subscribe through `useShallow`, which compares the set structurally,
* so a write for another worktree cannot re-render this one. Iterates in place —
* `Object.values` would allocate every record on every store write. */
export function selectSleepingRecordParkExemptTabIds(
sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord> | undefined,
worktreeId: string
): ReadonlySet<string> {
if (!sleepingAgentSessionsByPaneKey) {
return EMPTY_TAB_IDS
*
* Why memoized on the record-map identity (STA-7552): zustand re-runs every
* mounted subscriber's selector on every store write, so an unrelated pane
* title update used to walk the whole sleeping-record inventory once per
* retained worktree. The map only changes when a record is parked or
* consumed, so gating on its identity keeps a keystroke independent of how
* many sleeping agents the profile has accumulated. Callers still compare with
* `useShallow`; the carried generation makes that an identity hit.
* Iterates in place — `Object.values` would allocate every record per rebuild. */
export const selectSleepingRecordParkExemptTabIds = createWorktreeRecordSelector<
SleepingRecordParkExemptionState,
ReadonlySet<string>
>({
readSources: (state) => [state.sleepingAgentSessionsByPaneKey],
empty: EMPTY_TAB_IDS,
isEmpty: (tabIds) => tabIds.size === 0,
build: (state, worktreeId) => {
const sleepingAgentSessionsByPaneKey = state.sleepingAgentSessionsByPaneKey
if (!sleepingAgentSessionsByPaneKey) {
return EMPTY_TAB_IDS
}
let owned: Set<string> | null = null
for (const paneKey in sleepingAgentSessionsByPaneKey) {
const record = sleepingAgentSessionsByPaneKey[paneKey]
if (!record || record.worktreeId !== worktreeId) {
continue
}
if (isPassiveCompletedHibernationEvidence(record)) {
continue
}
// Why: malformed pane keys must yield no owner instead of a truncated tab id.
const tabId =
record.tabId ??
parsePaneKey(record.paneKey)?.tabId ??
parseLegacyNumericPaneKey(record.paneKey)?.tabId
if (tabId) {
owned ??= new Set()
owned.add(tabId)
}
}
return owned ?? EMPTY_TAB_IDS
}
let owned: Set<string> | null = null
for (const paneKey in sleepingAgentSessionsByPaneKey) {
const record = sleepingAgentSessionsByPaneKey[paneKey]
if (!record || record.worktreeId !== worktreeId) {
continue
}
if (isPassiveCompletedHibernationEvidence(record)) {
continue
}
// Why: malformed pane keys must yield no owner instead of a truncated tab id.
const tabId =
record.tabId ??
parsePaneKey(record.paneKey)?.tabId ??
parseLegacyNumericPaneKey(record.paneKey)?.tabId
if (tabId) {
owned ??= new Set()
owned.add(tabId)
}
}
return owned ?? EMPTY_TAB_IDS
}
})
@@ -119,9 +119,7 @@ export function useTerminalTabColdParking(args: {
// Why the worktree-scoped set, not the record map: the map is app-global, so
// subscribing to it re-rendered this worktree on every other worktree's write.
const sleepingRecordOwnedTabIds = useAppStore(
useShallow((state) =>
selectSleepingRecordParkExemptTabIds(state.sleepingAgentSessionsByPaneKey, worktreeId)
)
useShallow((state) => selectSleepingRecordParkExemptTabIds(state, worktreeId))
)
const terminalTabHiddenSinceRef = useRef(new Map<string, number>())
// Why: view switches hide every tab at once, so the park clock cannot rank them.
@@ -34,6 +34,8 @@ export function createWorktreeRecordSelector<TState, TValue extends object>(opti
readSources: (state: TState) => readonly unknown[]
build: (state: TState, worktreeId: string) => TValue
empty: TValue
/** Override for values whose emptiness is not `Object.keys` — a Set, for instance. */
isEmpty?: (value: TValue) => boolean
}): (state: TState, worktreeId: string) => TValue {
let generation: WorktreeRecordGeneration<TValue> | null = null
return (state, worktreeId) => {
@@ -52,7 +54,7 @@ export function createWorktreeRecordSelector<TState, TValue extends object>(opti
const built = options.build(state, worktreeId)
const carried = generation.carried?.get(worktreeId)
let value = built
if (Object.keys(built).length === 0) {
if (options.isEmpty ? options.isEmpty(built) : Object.keys(built).length === 0) {
value = options.empty
} else if (carried && shallow(carried, built)) {
value = carried