perf(sidebar): share one worktree-keyed agent orchestration index (#10678)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil 2026-07-26 00:48:04 -07:00 committed by GitHub
parent a84dd33afb
commit 4ff2f51782
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 741 additions and 41 deletions

View File

@ -403,15 +403,22 @@ describe('selectRuntimeAgentOrchestrationBatch', () => {
)
expect(Object.keys(actual)).toEqual(Object.keys(expected))
const operationBudget = {
// Why the batch stays tighter: it knows which worktrees are on screen. The
// shared index covers all of them, so it saves per *card*, not per worktree.
expect(batched.counts()).toEqual({
runtimeEnumerations: 1,
runtimeValueReads: contextCount,
contextVisits: contextCount,
targetTabIdReads: 1,
unrelatedTabIdReads: 0
}
expect(reference.counts()).toEqual(operationBudget)
expect(batched.counts()).toEqual(operationBudget)
})
expect(reference.counts()).toEqual({
runtimeEnumerations: 1,
runtimeValueReads: contextCount,
contextVisits: contextCount,
targetTabIdReads: 1,
unrelatedTabIdReads: tabCount - 1
})
})
it('collapses multi-worktree runtime scans and caches unchanged publications', () => {
@ -478,10 +485,12 @@ describe('selectRuntimeAgentOrchestrationBatch', () => {
}
selectRuntimeAgentOrchestrationBatch(batched.state, requested)
// One enumeration for worktreeCount calls: the first builds the shared
// index, the rest are Map lookups. This used to scale with mounted cards.
expect(reference.counts()).toEqual({
runtimeEnumerations: worktreeCount,
runtimeValueReads: worktreeCount * contextCount,
contextVisits: worktreeCount * contextCount,
runtimeEnumerations: 1,
runtimeValueReads: contextCount,
contextVisits: contextCount,
tabIdReads: worktreeCount
})
expect(batched.counts()).toEqual({
@ -518,5 +527,42 @@ describe('selectRuntimeAgentOrchestrationBatch', () => {
contextVisits: contextCount * (publicationCount + 1),
tabIdReads: worktreeCount
})
// Publications that change nothing the index reads cost nothing, however
// many cards call in.
const referenceBefore = reference.counts()
for (let publication = 0; publication < publicationCount; publication += 1) {
for (const worktreeId of requested) {
selectRuntimeAgentOrchestrationForWorktree(reference.state, worktreeId)
}
}
expect(reference.counts()).toEqual(referenceBefore)
// Why this is the honest claim: a real live-status ping replaces
// agentStatusByPaneKey, so the index does rebuild once per publication. What
// the shared index removes is the mounted-card multiplier, not the
// per-publication rebuild. Tab reads stay flat because tab membership is
// keyed on the tabs slice, which a live-status ping does not replace.
const churn = makeCountedState()
for (const worktreeId of requested) {
selectRuntimeAgentOrchestrationForWorktree(churn.state, worktreeId)
}
for (let publication = 0; publication < publicationCount; publication += 1) {
const published = {
...churn.state,
agentStatusByPaneKey: {
[`unrelated-${publication}`]: makeEntry(`unrelated-${publication}`, 'elsewhere')
}
}
for (const worktreeId of requested) {
selectRuntimeAgentOrchestrationForWorktree(published, worktreeId)
}
}
expect(churn.counts()).toEqual({
runtimeEnumerations: 1,
runtimeValueReads: contextCount,
contextVisits: contextCount * (publicationCount + 1),
tabIdReads: worktreeCount
})
})
})

View File

@ -40,7 +40,13 @@ const EMPTY_AGENT_STATUS: RuntimeOrchestrationState['agentStatusByPaneKey'] = {}
const EMPTY_RETAINED_AGENTS: RuntimeOrchestrationState['retainedAgentsByPaneKey'] = {}
const EMPTY_BATCH: ReadonlyMap<string, RuntimeOrchestrationRecord> = new Map()
export const EMPTY_WORKTREE_AGENT_ORCHESTRATION: RuntimeOrchestrationRecord = {}
export const EMPTY_WORKTREE_AGENT_ORCHESTRATION: RuntimeOrchestrationRecord = Object.freeze({})
// Why null-prototype: a pane key of `__proto__` is a plain data key here; on a
// normal object the write vanishes into the prototype setter and repoints it.
function createRecord(): RuntimeOrchestrationRecord {
return Object.create(null) as RuntimeOrchestrationRecord
}
let runtimeDomainCache: RuntimeDomainCache | null = null
let requestedTabMembershipCache: RequestedTabMembershipCache | null = null
@ -181,12 +187,12 @@ function buildRuntimeBatch(
}
for (const worktreeId of targets) {
const existing = recordsByWorktree.get(worktreeId)
if (existing) {
existing[paneKey] = orchestration
} else {
recordsByWorktree.set(worktreeId, { [paneKey]: orchestration })
let record = recordsByWorktree.get(worktreeId)
if (!record) {
record = createRecord()
recordsByWorktree.set(worktreeId, record)
}
record[paneKey] = orchestration
}
}

View File

@ -0,0 +1,430 @@
import { beforeEach, describe, expect, it } from 'vitest'
import type {
AgentStatusEntry,
AgentStatusOrchestrationContext
} from '../../../../shared/agent-status-types'
import type { RetainedAgentEntry } from '@/store/slices/agent-status'
import type { TerminalTab } from '../../../../shared/types'
import { makePaneKey, parsePaneKey } from '../../../../shared/stable-pane-id'
import {
EMPTY_WORKTREE_AGENT_ORCHESTRATION,
releaseWorktreeAgentOrchestrationIndexCache,
selectWorktreeAgentOrchestration
} from './worktree-agent-orchestration-index'
type IndexState = Parameters<typeof selectWorktreeAgentOrchestration>[0]
const EMPTY_RECORD = {}
// Pane keys that reach this selector unvalidated. `__proto__` is the one whose
// meaning depends on how the output record is built.
const MALFORMED_RUNTIME_KEYS = ['__proto__', 'constructor', 'toString', 'no-colon', 'a:b:c']
// Why not plain assignment: writing `__proto__` onto an object literal hits the
// prototype setter, so the fixture itself would lose the key under test.
function defineKey<T>(map: Record<string, T>, key: string, value: T): void {
Object.defineProperty(map, key, { value, enumerable: true, writable: true, configurable: true })
}
/**
* The pre-index per-card selector, transcribed from the revision this index
* replaced. Kept as the oracle so equivalence is asserted against real prior
* behavior rather than against a restatement of the new implementation.
*
* One deliberate correction: the original accumulated into `{}`, so a pane key
* of `__proto__` hit the prototype setter and vanished. This builds a
* null-prototype record so the oracle expresses intended attribution.
*/
function legacySelectForWorktree(
state: IndexState,
worktreeId: string
): Record<string, AgentStatusOrchestrationContext> {
const tabs = (state.tabsByWorktree ?? EMPTY_RECORD)[worktreeId] ?? []
const tabIds = new Set(tabs.map((tab) => tab.id))
const out: Record<string, AgentStatusOrchestrationContext> = Object.create(null)
const runtimeAgentOrchestrationByPaneKey =
state.runtimeAgentOrchestrationByPaneKey ?? EMPTY_RECORD
const agentStatusByPaneKey = state.agentStatusByPaneKey ?? EMPTY_RECORD
const retainedAgentsByPaneKey = state.retainedAgentsByPaneKey ?? EMPTY_RECORD
for (const [paneKey, orchestration] of Object.entries(runtimeAgentOrchestrationByPaneKey)) {
const parsed = parsePaneKey(paneKey)
const parsedParent = orchestration.parentPaneKey
? parsePaneKey(orchestration.parentPaneKey)
: null
const liveEntry = agentStatusByPaneKey[paneKey]
const retainedEntry = retainedAgentsByPaneKey[paneKey]
if (
(parsed && tabIds.has(parsed.tabId)) ||
(parsedParent && tabIds.has(parsedParent.tabId)) ||
liveEntry?.worktreeId === worktreeId ||
retainedEntry?.worktreeId === worktreeId
) {
out[paneKey] = orchestration
}
}
return out
}
// Why a seeded PRNG: a randomized differential must reproduce exactly when it
// reports a divergence.
function createRandom(seed: number): () => number {
// Why the multiply: xorshift32 needs a high-entropy state. Seeding it with a
// small integer keeps the first output under 0.019, so `1 + pick(5)` was 1 for
// every seed here and the suite only ever built single-worktree stores.
let state = Math.imul(seed >>> 0 || 1, 0x9e37_79b1) >>> 0 || 1
return () => {
state ^= state << 13
state >>>= 0
state ^= state >> 17
state ^= state << 5
state >>>= 0
return state / 0x1_00_00_00_00
}
}
function makeTab(id: string): TerminalTab {
return {
id,
worktreeId: 'unused',
ptyId: null,
title: 'Claude',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 0
}
}
function paneKeyFor(tabId: string, index: number): string {
return makePaneKey(tabId, `88888888-8888-4888-8888-${index.toString(16).padStart(12, '0')}`)
}
function makeEntry(paneKey: string, worktreeId: string): AgentStatusEntry {
return { paneKey, worktreeId, state: 'busy', startedAt: 0 } as unknown as AgentStatusEntry
}
function makeRetained(paneKey: string, worktreeId: string): RetainedAgentEntry {
return {
worktreeId,
entry: makeEntry(paneKey, worktreeId),
retainedAt: 0
} as unknown as RetainedAgentEntry
}
describe('selectWorktreeAgentOrchestration', () => {
beforeEach(() => {
releaseWorktreeAgentOrchestrationIndexCache()
})
it('matches the pre-index per-card selector across randomized stores', () => {
for (let seed = 1; seed <= 300; seed += 1) {
releaseWorktreeAgentOrchestrationIndexCache()
const random = createRandom(seed)
const pick = (limit: number): number => Math.floor(random() * limit)
const worktreeCount = 1 + pick(5)
const worktreeIds = Array.from({ length: worktreeCount }, (_, index) => `wt-${index}`)
const tabsByWorktree: Record<string, TerminalTab[]> = {}
const tabIds: string[] = []
for (const worktreeId of worktreeIds) {
const tabCount = pick(3)
const tabs: TerminalTab[] = []
for (let tabIndex = 0; tabIndex < tabCount; tabIndex += 1) {
// Why a small shared id space: tab ids collide across worktrees on
// 131 of these 300 seeds, which is the multi-attribution path.
const tabId = `tab-${pick(4)}`
tabs.push(makeTab(tabId))
tabIds.push(tabId)
}
tabsByWorktree[worktreeId] = tabs
}
const runtimeAgentOrchestrationByPaneKey: Record<string, AgentStatusOrchestrationContext> = {}
const agentStatusByPaneKey: Record<string, AgentStatusEntry> = {}
const retainedAgentsByPaneKey: Record<string, RetainedAgentEntry> = {}
const contextCount = pick(8)
for (let index = 0; index < contextCount; index += 1) {
const ownerTabId =
random() < 0.7 && tabIds.length > 0 ? tabIds[pick(tabIds.length)] : 'tab-orphan'
// Why malformed runtime keys: a pane key that is not `tab:uuid` reaches
// this selector unvalidated, and `__proto__` is the one that changes
// meaning depending on how the output record is built.
const keyRoll = random()
const paneKey =
keyRoll < 0.08
? MALFORMED_RUNTIME_KEYS[pick(MALFORMED_RUNTIME_KEYS.length)]
: paneKeyFor(ownerTabId, index)
const parentRoll = random()
const parentPaneKey =
parentRoll < 0.3 && tabIds.length > 0
? paneKeyFor(tabIds[pick(tabIds.length)], 900 + index)
: parentRoll < 0.4
? 'malformed:parent:key'
: undefined
defineKey(runtimeAgentOrchestrationByPaneKey, paneKey, {
taskId: `task-${index}`,
dispatchId: `dispatch-${index}`,
...(parentPaneKey === undefined ? {} : { parentPaneKey })
})
if (random() < 0.35) {
defineKey(
agentStatusByPaneKey,
paneKey,
makeEntry(paneKey, `wt-${pick(worktreeCount + 1)}`)
)
}
if (random() < 0.25) {
defineKey(
retainedAgentsByPaneKey,
paneKey,
makeRetained(paneKey, `wt-${pick(worktreeCount + 1)}`)
)
}
}
const state = {
tabsByWorktree,
runtimeAgentOrchestrationByPaneKey,
agentStatusByPaneKey,
retainedAgentsByPaneKey
} as unknown as IndexState
// Why the extra id: worktrees with no tabs are still reachable through a
// live or retained attribution, and must resolve identically.
for (const worktreeId of [...worktreeIds, `wt-${worktreeCount}`, 'missing']) {
const expected = legacySelectForWorktree(state, worktreeId)
const actual = selectWorktreeAgentOrchestration(state, worktreeId)
expect(Object.keys(actual), `seed ${seed} / ${worktreeId}`).toEqual(Object.keys(expected))
for (const paneKey of Object.keys(expected)) {
expect(actual[paneKey], `seed ${seed} / ${worktreeId} / ${paneKey}`).toBe(
expected[paneKey]
)
}
}
}
})
it('returns one shared empty record for worktrees with no orchestration', () => {
const state = {
tabsByWorktree: { 'wt-1': [makeTab('tab-1')] },
runtimeAgentOrchestrationByPaneKey: {
[paneKeyFor('tab-1', 0)]: { taskId: 't', dispatchId: 'd' }
},
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
} as unknown as IndexState
expect(selectWorktreeAgentOrchestration(state, 'wt-2')).toBe(EMPTY_WORKTREE_AGENT_ORCHESTRATION)
expect(selectWorktreeAgentOrchestration(state, 'wt-2')).toBe(
selectWorktreeAgentOrchestration(state, 'wt-3')
)
})
it('keeps record identity stable across publications that change nothing it reads', () => {
const context = { taskId: 't', dispatchId: 'd' }
const base = {
tabsByWorktree: { 'wt-1': [makeTab('tab-1')] },
runtimeAgentOrchestrationByPaneKey: { [paneKeyFor('tab-1', 0)]: context },
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
} as unknown as IndexState
const first = selectWorktreeAgentOrchestration(base, 'wt-1')
expect(selectWorktreeAgentOrchestration({ ...base } as IndexState, 'wt-1')).toBe(first)
// Why: a live-status ping for an unrelated pane is the highest-frequency
// publication in this store, and must not hand cards a new object.
const liveChurn = {
...base,
agentStatusByPaneKey: { unrelated: makeEntry('unrelated', 'wt-9') }
} as unknown as IndexState
expect(selectWorktreeAgentOrchestration(liveChurn, 'wt-1')).toBe(first)
const retainedChurn = {
...liveChurn,
retainedAgentsByPaneKey: { unrelated: makeRetained('unrelated', 'wt-9') }
} as unknown as IndexState
expect(selectWorktreeAgentOrchestration(retainedChurn, 'wt-1')).toBe(first)
})
it('rebuilds when a source it reads actually changes', () => {
const context = { taskId: 't', dispatchId: 'd' }
const paneKey = paneKeyFor('tab-1', 0)
const base = {
tabsByWorktree: { 'wt-1': [makeTab('tab-1')] },
runtimeAgentOrchestrationByPaneKey: { [paneKey]: context },
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
} as unknown as IndexState
selectWorktreeAgentOrchestration(base, 'wt-1')
const replacement = { taskId: 't2', dispatchId: 'd2' }
const replaced = {
...base,
runtimeAgentOrchestrationByPaneKey: { [paneKey]: replacement }
} as unknown as IndexState
expect(selectWorktreeAgentOrchestration(replaced, 'wt-1')[paneKey]).toBe(replacement)
// Why: moving the tab to another worktree must re-attribute, which only
// happens if tab membership is keyed on the tabs slice identity.
const movedTab = {
...replaced,
tabsByWorktree: { 'wt-2': [makeTab('tab-1')] }
} as unknown as IndexState
expect(selectWorktreeAgentOrchestration(movedTab, 'wt-1')).toBe(
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
expect(selectWorktreeAgentOrchestration(movedTab, 'wt-2')[paneKey]).toBe(replacement)
})
it('treats a missing or emptied orchestration map as empty without reading other slices', () => {
let forbiddenReads = 0
const coldState = {
runtimeAgentOrchestrationByPaneKey: {},
get tabsByWorktree() {
forbiddenReads += 1
return {}
},
get agentStatusByPaneKey() {
forbiddenReads += 1
return {}
},
get retainedAgentsByPaneKey() {
forbiddenReads += 1
return {}
}
} as unknown as IndexState
expect(selectWorktreeAgentOrchestration(coldState, 'wt-1')).toBe(
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
expect(selectWorktreeAgentOrchestration({} as IndexState, 'wt-1')).toBe(
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
expect(forbiddenReads).toBe(0)
})
it('does not attribute unowned panes to a nullish worktree id', () => {
const paneKey = paneKeyFor('tab-orphan', 0)
const state = {
tabsByWorktree: {},
runtimeAgentOrchestrationByPaneKey: { [paneKey]: { taskId: 't', dispatchId: 'd' } },
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
} as unknown as IndexState
// Why asserted despite `worktreeId: string`: the pre-index selector tested
// `entry?.worktreeId === worktreeId`, so a nullish id matched every pane
// that had no live/retained entry. This is the one intentional divergence
// from the oracle — the old result was wrong, not merely different.
expect(selectWorktreeAgentOrchestration(state, undefined as unknown as string)).toBe(
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
expect(legacySelectForWorktree(state, undefined as unknown as string)).toHaveProperty(paneKey)
})
it('stays correct when two stores interleave through the single cache slot', () => {
// Why: the cache is one module-level slot, but the sidebar cards and the
// dashboard snapshot can call in with different state objects. Thrashing may
// cost a rebuild; it must never return another store's answer.
const buildState = (suffix: string): IndexState =>
({
tabsByWorktree: { [`wt-${suffix}`]: [makeTab(`tab-${suffix}`)] },
runtimeAgentOrchestrationByPaneKey: {
[paneKeyFor(`tab-${suffix}`, 0)]: { taskId: `t-${suffix}`, dispatchId: `d-${suffix}` }
},
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
}) as unknown as IndexState
const stateA = buildState('a')
const stateB = buildState('b')
for (let round = 0; round < 4; round += 1) {
for (const [state, suffix] of [
[stateA, 'a'],
[stateB, 'b']
] as const) {
expect(Object.keys(selectWorktreeAgentOrchestration(state, `wt-${suffix}`))).toEqual(
Object.keys(legacySelectForWorktree(state, `wt-${suffix}`))
)
// The other store's worktree must never leak through the shared slot.
const foreign = suffix === 'a' ? 'wt-b' : 'wt-a'
expect(selectWorktreeAgentOrchestration(state, foreign)).toBe(
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
}
}
})
it('treats a __proto__ pane key as data instead of a prototype write', () => {
// Why: writing this key into a normal object silently drops the entry and
// repoints the record's prototype at the orchestration context.
const context = { taskId: 't', dispatchId: 'd' }
const state = {
tabsByWorktree: {},
runtimeAgentOrchestrationByPaneKey: Object.fromEntries([['__proto__', context]]),
agentStatusByPaneKey: Object.fromEntries([['__proto__', makeEntry('__proto__', 'wt-1')]]),
retainedAgentsByPaneKey: {}
} as unknown as IndexState
const record = selectWorktreeAgentOrchestration(state, 'wt-1')
expect(Object.keys(record)).toEqual(['__proto__'])
expect(record['__proto__']).toBe(context)
expect(Object.getPrototypeOf(record)).toBeNull()
})
it('keeps the entries cache warm while the orchestration map is empty', () => {
// Why: the empty map is the common case, so re-enumerating it per card is
// exactly the per-publication cost this index exists to remove.
let enumerations = 0
const runtimeAgentOrchestrationByPaneKey = new Proxy(
{},
{
ownKeys(target) {
enumerations += 1
return Reflect.ownKeys(target)
}
}
)
const state = {
tabsByWorktree: {},
runtimeAgentOrchestrationByPaneKey,
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
} as unknown as IndexState
for (const worktreeId of ['wt-a', 'wt-b', 'wt-c']) {
expect(selectWorktreeAgentOrchestration(state, worktreeId)).toBe(
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
}
expect(enumerations).toBe(1)
})
it('never mutates a record already handed to a subscriber', () => {
// Why: buildIndex fills record objects in place and can hand back a previous
// build's record. A mounted card holds that object across renders, so a later
// build writing into it would break React's snapshot contract silently.
const paneKey = paneKeyFor('tab-1', 0)
const base = {
tabsByWorktree: { 'wt-1': [makeTab('tab-1')] },
runtimeAgentOrchestrationByPaneKey: { [paneKey]: { taskId: 't', dispatchId: 'd' } },
agentStatusByPaneKey: {},
retainedAgentsByPaneKey: {}
} as unknown as IndexState
const held = selectWorktreeAgentOrchestration(base, 'wt-1')
const heldKeys = Object.keys(held)
const secondPaneKey = paneKeyFor('tab-1', 1)
const grown = {
...base,
runtimeAgentOrchestrationByPaneKey: {
...base.runtimeAgentOrchestrationByPaneKey,
[secondPaneKey]: { taskId: 't2', dispatchId: 'd2' }
}
} as unknown as IndexState
expect(Object.keys(selectWorktreeAgentOrchestration(grown, 'wt-1'))).toHaveLength(2)
expect(Object.keys(held)).toEqual(heldKeys)
})
})

View File

@ -0,0 +1,240 @@
import type { AppState } from '@/store/types'
import type { AgentStatusOrchestrationContext } from '../../../../shared/agent-status-types'
import { parsePaneKey } from '../../../../shared/stable-pane-id'
type OrchestrationIndexState = Pick<
AppState,
| 'agentStatusByPaneKey'
| 'retainedAgentsByPaneKey'
| 'runtimeAgentOrchestrationByPaneKey'
| 'tabsByWorktree'
>
type RuntimeOrchestrationRecord = Record<string, AgentStatusOrchestrationContext>
type RuntimeEntriesCache = {
source: OrchestrationIndexState['runtimeAgentOrchestrationByPaneKey']
entries: [string, AgentStatusOrchestrationContext][]
}
type TabMembershipCache = {
tabsSource: OrchestrationIndexState['tabsByWorktree']
worktreeIdsByTabId: Map<string, Set<string>>
}
type OrchestrationIndexCache = {
runtimeSource: OrchestrationIndexState['runtimeAgentOrchestrationByPaneKey']
tabsSource: OrchestrationIndexState['tabsByWorktree']
liveSource: OrchestrationIndexState['agentStatusByPaneKey']
retainedSource: OrchestrationIndexState['retainedAgentsByPaneKey']
recordsByWorktree: ReadonlyMap<string, RuntimeOrchestrationRecord>
}
// Why: selector unit tests pass partial store mocks; a missing map must behave
// like an empty slice while keeping a stable identity for the source cache.
const EMPTY_SOURCE = {}
// Why frozen: these are shared by every card, so an accidental write would
// corrupt unrelated worktrees rather than fail locally.
export const EMPTY_WORKTREE_AGENT_ORCHESTRATION: RuntimeOrchestrationRecord = Object.freeze({})
export const EMPTY_WORKTREE_AGENT_ORCHESTRATION_INDEX: ReadonlyMap<
string,
RuntimeOrchestrationRecord
> = new Map()
// Why null-prototype: a pane key of `__proto__` is a plain data key here. On a
// normal object the first write silently vanishes into the prototype setter,
// which both drops the entry and repoints the record's prototype.
function createRecord(): RuntimeOrchestrationRecord {
return Object.create(null) as RuntimeOrchestrationRecord
}
let runtimeEntriesCache: RuntimeEntriesCache | null = null
let tabMembershipCache: TabMembershipCache | null = null
let orchestrationIndexCache: OrchestrationIndexCache | null = null
export function releaseWorktreeAgentOrchestrationIndexCache(): void {
runtimeEntriesCache = null
tabMembershipCache = null
orchestrationIndexCache = null
}
function reuseRecordIfOrderedEqual(
previous: RuntimeOrchestrationRecord | undefined,
next: RuntimeOrchestrationRecord
): RuntimeOrchestrationRecord {
if (!previous) {
return next
}
const previousEntries = Object.entries(previous)
const nextEntries = Object.entries(next)
if (previousEntries.length !== nextEntries.length) {
return next
}
for (let index = 0; index < nextEntries.length; index += 1) {
if (
previousEntries[index]?.[0] !== nextEntries[index]?.[0] ||
previousEntries[index]?.[1] !== nextEntries[index]?.[1]
) {
return next
}
}
return previous
}
function getWorktreeIdsByTabId(
tabsByWorktree: OrchestrationIndexState['tabsByWorktree']
): Map<string, Set<string>> {
if (tabMembershipCache?.tabsSource === tabsByWorktree) {
return tabMembershipCache.worktreeIdsByTabId
}
// Why a Set per tab: the same tab id can appear under more than one worktree,
// and each of those worktrees must still see the pane's orchestration.
const worktreeIdsByTabId = new Map<string, Set<string>>()
for (const [worktreeId, tabs] of Object.entries(tabsByWorktree)) {
for (const tab of tabs ?? []) {
const tabId = tab.id
const existing = worktreeIdsByTabId.get(tabId)
if (existing) {
existing.add(worktreeId)
} else {
worktreeIdsByTabId.set(tabId, new Set([worktreeId]))
}
}
}
tabMembershipCache = { tabsSource: tabsByWorktree, worktreeIdsByTabId }
return worktreeIdsByTabId
}
function buildIndex(
runtimeEntries: [string, AgentStatusOrchestrationContext][],
tabsByWorktree: OrchestrationIndexState['tabsByWorktree'],
agentStatusByPaneKey: OrchestrationIndexState['agentStatusByPaneKey'],
retainedAgentsByPaneKey: OrchestrationIndexState['retainedAgentsByPaneKey']
): ReadonlyMap<string, RuntimeOrchestrationRecord> {
const worktreeIdsByTabId = getWorktreeIdsByTabId(tabsByWorktree)
const recordsByWorktree = new Map<string, RuntimeOrchestrationRecord>()
for (const [paneKey, orchestration] of runtimeEntries) {
const parsed = parsePaneKey(paneKey)
const parsedParent = orchestration.parentPaneKey
? parsePaneKey(orchestration.parentPaneKey)
: null
const targets = new Set<string>()
if (parsed) {
for (const worktreeId of worktreeIdsByTabId.get(parsed.tabId) ?? []) {
targets.add(worktreeId)
}
}
// Why: child agent terminals can be attributed to a worktree before their
// tab reaches this renderer, or after the row has been retained as done.
// The parent link must still reach that worktree card.
if (parsedParent) {
for (const worktreeId of worktreeIdsByTabId.get(parsedParent.tabId) ?? []) {
targets.add(worktreeId)
}
}
// Why exact runtime keys: this preserves early SSH attribution and ignores
// stale entry.paneKey fields carried by a live or retained row.
const liveWorktreeId = agentStatusByPaneKey[paneKey]?.worktreeId
if (typeof liveWorktreeId === 'string') {
targets.add(liveWorktreeId)
}
const retainedWorktreeId = retainedAgentsByPaneKey[paneKey]?.worktreeId
if (typeof retainedWorktreeId === 'string') {
targets.add(retainedWorktreeId)
}
for (const worktreeId of targets) {
let record = recordsByWorktree.get(worktreeId)
if (!record) {
record = createRecord()
recordsByWorktree.set(worktreeId, record)
}
record[paneKey] = orchestration
}
}
const previousRecords = orchestrationIndexCache?.recordsByWorktree
for (const [worktreeId, record] of recordsByWorktree) {
recordsByWorktree.set(
worktreeId,
reuseRecordIfOrderedEqual(previousRecords?.get(worktreeId), record)
)
}
return recordsByWorktree
}
/**
* Worktree-keyed index of runtime agent orchestration contexts, rebuilt only
* when one of its four source maps changes identity.
*
* Why: every mounted worktree card subscribes to its own orchestration slice,
* and Zustand re-runs every subscriber's selector on every store publication.
* Scanning the whole context map per card made that O(cards x contexts). What
* this removes is the per-card multiplier, not the rebuild itself: an agent
* ping replaces the live map, so the index still rebuilds once per publication.
* The first caller through a given store version pays O(tabs + contexts); the
* rest are a Map lookup.
*/
export function selectWorktreeAgentOrchestrationIndex(
state: OrchestrationIndexState
): ReadonlyMap<string, RuntimeOrchestrationRecord> {
const runtimeAgentOrchestrationByPaneKey =
state.runtimeAgentOrchestrationByPaneKey ?? EMPTY_SOURCE
// Why cached separately from the index: enumerating the context map is the
// per-publication cost this index exists to remove, and the entry list stays
// valid even when a churning live/retained slice forces an index rebuild.
if (runtimeEntriesCache?.source !== runtimeAgentOrchestrationByPaneKey) {
runtimeEntriesCache = {
source: runtimeAgentOrchestrationByPaneKey,
entries: Object.entries(runtimeAgentOrchestrationByPaneKey)
}
}
const runtimeEntries = runtimeEntriesCache.entries
// Why here rather than before the enumeration: with no contexts the index is
// empty whatever the other slices hold, and callers rely on them staying unread.
if (runtimeEntries.length === 0) {
// Why the entries cache survives: dropping it would re-enumerate the empty
// map once per card, which is the per-publication cost this index removes.
tabMembershipCache = null
orchestrationIndexCache = null
return EMPTY_WORKTREE_AGENT_ORCHESTRATION_INDEX
}
const tabsByWorktree = state.tabsByWorktree ?? EMPTY_SOURCE
const agentStatusByPaneKey = state.agentStatusByPaneKey ?? EMPTY_SOURCE
const retainedAgentsByPaneKey = state.retainedAgentsByPaneKey ?? EMPTY_SOURCE
if (
orchestrationIndexCache?.runtimeSource === runtimeAgentOrchestrationByPaneKey &&
orchestrationIndexCache.tabsSource === tabsByWorktree &&
orchestrationIndexCache.liveSource === agentStatusByPaneKey &&
orchestrationIndexCache.retainedSource === retainedAgentsByPaneKey
) {
return orchestrationIndexCache.recordsByWorktree
}
orchestrationIndexCache = {
runtimeSource: runtimeAgentOrchestrationByPaneKey,
tabsSource: tabsByWorktree,
liveSource: agentStatusByPaneKey,
retainedSource: retainedAgentsByPaneKey,
recordsByWorktree: buildIndex(
runtimeEntries,
tabsByWorktree,
agentStatusByPaneKey,
retainedAgentsByPaneKey
)
}
return orchestrationIndexCache.recordsByWorktree
}
export function selectWorktreeAgentOrchestration(
state: OrchestrationIndexState,
worktreeId: string
): RuntimeOrchestrationRecord {
return (
selectWorktreeAgentOrchestrationIndex(state).get(worktreeId) ??
EMPTY_WORKTREE_AGENT_ORCHESTRATION
)
}

View File

@ -12,12 +12,12 @@ import {
patchLiveEntriesByWorktree,
recordLiveEntriesFullRebuild
} from './worktree-agent-live-index-patch'
import { selectWorktreeAgentOrchestration } from './worktree-agent-orchestration-index'
import type { TerminalLayoutSnapshot } from '../../../../shared/types'
const EMPTY_LIVE_ENTRIES: AgentStatusEntry[] = []
const EMPTY_MIGRATION_UNSUPPORTED_ENTRIES: MigrationUnsupportedPtyEntry[] = []
const EMPTY_RETAINED: RetainedAgentEntry[] = []
const EMPTY_RUNTIME_AGENT_ORCHESTRATION: Record<string, AgentStatusOrchestrationContext> = {}
// Why: selector unit tests often pass partial store mocks; production state
// owns these maps, but missing mock maps should behave like empty slices.
const EMPTY_RECORD = {}
@ -227,6 +227,10 @@ export function selectRetainedAgentEntriesForWorktree(
return getRetainedEntriesByWorktree(state).get(worktreeId) ?? EMPTY_RETAINED
}
// Why: reads a shared worktree-keyed index instead of rescanning every
// orchestration context. Zustand re-runs each mounted card's selector on every
// publication, so the old per-card scan was O(cards x contexts) on unrelated
// traffic; only the first card through a given store version now pays a build.
export function selectRuntimeAgentOrchestrationForWorktree(
state: Pick<
AppState,
@ -237,33 +241,7 @@ export function selectRuntimeAgentOrchestrationForWorktree(
>,
worktreeId: string
): Record<string, AgentStatusOrchestrationContext> {
const tabs = (state.tabsByWorktree ?? EMPTY_RECORD)[worktreeId] ?? []
const tabIds = new Set(tabs.map((tab) => tab.id))
const out: Record<string, AgentStatusOrchestrationContext> = {}
const runtimeAgentOrchestrationByPaneKey =
state.runtimeAgentOrchestrationByPaneKey ?? EMPTY_RECORD
const agentStatusByPaneKey = state.agentStatusByPaneKey ?? EMPTY_RECORD
const retainedAgentsByPaneKey = state.retainedAgentsByPaneKey ?? EMPTY_RECORD
for (const [paneKey, orchestration] of Object.entries(runtimeAgentOrchestrationByPaneKey)) {
const parsed = parsePaneKey(paneKey)
const parsedParent = orchestration.parentPaneKey
? parsePaneKey(orchestration.parentPaneKey)
: null
const liveEntry = agentStatusByPaneKey[paneKey]
const retainedEntry = retainedAgentsByPaneKey[paneKey]
// Why: child agent terminals can be attributed to a worktree before their
// tab reaches this renderer, or after the row has been retained as done.
// The parent link must still reach that worktree card.
if (
(parsed && tabIds.has(parsed.tabId)) ||
(parsedParent && tabIds.has(parsedParent.tabId)) ||
liveEntry?.worktreeId === worktreeId ||
retainedEntry?.worktreeId === worktreeId
) {
out[paneKey] = orchestration
}
}
return Object.keys(out).length > 0 ? out : EMPTY_RUNTIME_AGENT_ORCHESTRATION
return selectWorktreeAgentOrchestration(state, worktreeId)
}
export function selectTerminalLayoutsForWorktree(