perf(runtime): memoize mobile snapshots for mounted terminal worktrees (#12383)

The per-worktree rebuild memo from #12207 refused to skip any worktree
with a registered TerminalPane, because builders read live PaneManager/DOM
state the memo could not witness. Hidden worktrees stay mounted (8 parked
+ 12 retained by default, unbounded with terminalHiddenViewParking off),
so that set rebuilt on every publication — on the always-on hosts the memo
targeted, ~20 worktrees paid the full content build plus the new inputs
build and deep compare every tick.

Capture the live reads instead: snapshot pane leaf ids, the live active
leaf, the serialized pane tree, and per-pane numeric/pty bindings once per
publication into MountedTerminalSurfaceCapture, compare captures by value
in the memo, and have the builders read only the capture. Live state
outside the capture is now unrepresentable in a builder, so the skip stays
provably complete — and a mounted worktree whose panes did not change
reuses its snapshot like any other.

(cherry picked from commit 64b81cde50)
This commit is contained in:
Brennan Benson
2026-08-04 02:28:06 -04:00
committed by OrcaWin
parent 4987178e7a
commit 0147b33d25
2 changed files with 298 additions and 28 deletions
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { buildMobileSessionTabSnapshots } from './sync-runtime-graph'
import { buildMobileSessionTabSnapshots, registerRuntimeTerminalTab } from './sync-runtime-graph'
import type { AppState } from '../store/types'
// Why: getBrowserTabsByWorktree reads this slice once per worktree inside the build loop,
@@ -207,3 +207,175 @@ describe('mobile session publication cost', () => {
expect(rebuiltWorktrees).toEqual(['repo::/title-wt-7'])
})
})
// Distinct ids per test: the snapshot memo is module state shared in this file.
const MOUNTED_WT = 'repo::/mounted-wt-0'
const CONTROL_WT = 'repo::/mounted-wt-1'
const MOUNTED_LEAF_ID = 'aaaaaaaa-1111-4111-8111-111111111111'
const SPLIT_LEAF_ID = 'bbbbbbbb-1111-4111-8111-111111111111'
function makeMountedState(tabIdPrefix: string): {
state: AppState
mountedTabId: string
titleReads: () => number
resetTitleReads: () => void
} {
let titleReads = 0
const mountedTabId = `${tabIdPrefix}-term-0`
const state = {
tabsByWorktree: {
[MOUNTED_WT]: [
{
id: mountedTabId,
customTitle: null,
ptyId: null,
get title() {
titleReads++
return 'Agent mounted'
}
}
],
[CONTROL_WT]: [
{ id: `${tabIdPrefix}-term-1`, title: 'Agent control', customTitle: null, ptyId: null }
]
},
terminalLayoutsByTabId: {
[mountedTabId]: {
root: { type: 'leaf', leafId: MOUNTED_LEAF_ID },
activeLeafId: MOUNTED_LEAF_ID,
expandedLeafId: null,
ptyIdsByLeafId: { [MOUNTED_LEAF_ID]: 'pty-saved-1' }
}
},
runtimePaneTitlesByTabId: {},
groupsByWorktree: {},
activeGroupIdByWorktree: {},
unifiedTabsByWorktree: {},
tabBarOrderByWorktree: {},
activeFileId: null,
activeFileIdByWorktree: {},
openFiles: [],
editorDrafts: {},
activeTabId: null,
agentStatusByPaneKey: {},
browserTabsByWorktree: {}
} as unknown as AppState
return {
state,
mountedTabId,
titleReads: () => titleReads,
resetTitleReads: () => {
titleReads = 0
}
}
}
function makeLiveSurface(tabId: string): {
registration: Parameters<typeof registerRuntimeTerminalTab>[0]
setPtyId: (paneId: number, ptyId: string) => void
addPane: (paneId: number, leafId: string) => void
} {
const panes = [{ id: 1, leafId: MOUNTED_LEAF_ID }]
const ptyIdByPaneId = new Map<number, string | null>([[1, 'pty-live-1']])
const manager = {
getPanes: () => panes.map((pane) => ({ ...pane })),
getActivePane: () => panes[0] ?? null,
getLeafId: (paneId: number) => panes.find((pane) => pane.id === paneId)?.leafId ?? null,
getNumericIdForLeaf: (leafId: string) =>
panes.find((pane) => pane.leafId === leafId)?.id ?? null
}
const registration = {
tabId,
worktreeId: MOUNTED_WT,
getManager: () => manager,
getContainer: () => null,
getPtyIdForPane: (paneId: number) => ptyIdByPaneId.get(paneId) ?? null
} as unknown as Parameters<typeof registerRuntimeTerminalTab>[0]
return {
registration,
setPtyId: (paneId, ptyId) => {
ptyIdByPaneId.set(paneId, ptyId)
},
addPane: (paneId, leafId) => {
panes.push({ id: paneId, leafId })
ptyIdByPaneId.set(paneId, `pty-live-${paneId}`)
}
}
}
function snapshotsByWorktree(
state: AppState
): Map<string, ReturnType<typeof buildMobileSessionTabSnapshots>[number]> {
return new Map(
buildMobileSessionTabSnapshots(state).map((snapshot) => [snapshot.worktree, snapshot])
)
}
describe('mounted terminal surface memoization', () => {
it('reuses a mounted worktree snapshot when its live pane state is unchanged', () => {
const { state, mountedTabId, titleReads, resetTitleReads } = makeMountedState('mounted-a')
const unregister = registerRuntimeTerminalTab(makeLiveSurface(mountedTabId).registration)
try {
const before = snapshotsByWorktree(state)
resetTitleReads()
const after = snapshotsByWorktree(state)
expect(titleReads()).toBe(0)
expect(after.get(MOUNTED_WT)).toBe(before.get(MOUNTED_WT))
} finally {
unregister()
}
})
it('publishes a live pty rebinding on a mounted worktree', () => {
const { state, mountedTabId } = makeMountedState('mounted-b')
const surface = makeLiveSurface(mountedTabId)
const unregister = registerRuntimeTerminalTab(surface.registration)
try {
const before = snapshotsByWorktree(state)
surface.setPtyId(1, 'pty-live-2')
const after = snapshotsByWorktree(state)
expect(after.get(MOUNTED_WT)).not.toBe(before.get(MOUNTED_WT))
expect(after.get(MOUNTED_WT)?.tabs).toEqual([
expect.objectContaining({ ptyId: 'pty-live-2' })
])
expect(after.get(CONTROL_WT)).toBe(before.get(CONTROL_WT))
} finally {
unregister()
}
})
it('publishes a live pane split on a mounted worktree', () => {
const { state, mountedTabId } = makeMountedState('mounted-c')
const surface = makeLiveSurface(mountedTabId)
const unregister = registerRuntimeTerminalTab(surface.registration)
try {
snapshotsByWorktree(state)
surface.addPane(2, SPLIT_LEAF_ID)
const after = snapshotsByWorktree(state)
expect(
after.get(MOUNTED_WT)?.tabs.map((tab) => (tab.type === 'terminal' ? tab.leafId : null))
).toEqual([MOUNTED_LEAF_ID, SPLIT_LEAF_ID])
} finally {
unregister()
}
})
it('falls back to the persisted pty binding after the surface unmounts', () => {
const { state, mountedTabId, titleReads, resetTitleReads } = makeMountedState('mounted-d')
const unregister = registerRuntimeTerminalTab(makeLiveSurface(mountedTabId).registration)
snapshotsByWorktree(state)
unregister()
resetTitleReads()
const after = snapshotsByWorktree(state)
expect(titleReads()).toBeGreaterThan(0)
expect(after.get(MOUNTED_WT)?.tabs).toEqual([expect.objectContaining({ ptyId: 'pty-saved-1' })])
})
})
+125 -27
View File
@@ -31,6 +31,7 @@ import type {
TabGroup,
TabGroupLayoutNode,
TerminalLayoutSnapshot,
TerminalPaneLayoutNode,
TerminalTab
} from '../../../shared/types'
import { resolveTerminalTabTitle } from '../../../shared/tab-title-resolution'
@@ -92,6 +93,22 @@ type MobileSessionPublicationInputs = {
generatedTitlesEnabled: boolean
terminalTheme: RuntimeMobileTerminalTheme | undefined
}
/**
* Live PaneManager/DOM reads for one mounted terminal tab, captured once per
* publication.
*
* Why: builders read this instead of the registry, so live state the memo
* cannot witness is unrepresentable — mounted worktrees memoize like the rest
* instead of rebuilding on every publication.
*/
type MountedTerminalSurfaceCapture = {
paneLeafIds: readonly string[]
hasLiveActivePane: boolean
liveActiveLeafId: string | null
liveLayoutRoot: TerminalPaneLayoutNode | null
numericPaneIdByLeafId: ReadonlyMap<string, number | null>
ptyIdByNumericPaneId: ReadonlyMap<number, string | null>
}
/**
* One worktree's complete mobile-snapshot input set.
*
@@ -131,8 +148,7 @@ type MobileSessionWorktreeInputs = {
activeBrowserWorkspaceId: string | null
generatedTitlesEnabled: boolean
terminalTheme: RuntimeMobileTerminalTheme | undefined
// Why: a mounted TerminalPane feeds live DOM/PaneManager state that no store reference can witness.
hasMountedTerminalSurface: boolean
mountedSurfaceCaptureByTabId: ReadonlyMap<string, MountedTerminalSurfaceCapture>
}
const registeredTabs = new Map<string, RegisteredTerminalTab>()
@@ -1026,11 +1042,99 @@ function buildMobileSessionWorktreeInputs(
activeBrowserWorkspaceId: state.activeBrowserTabIdByWorktree?.[worktreeId] ?? null,
generatedTitlesEnabled: publication.generatedTitlesEnabled,
terminalTheme: publication.terminalTheme,
hasMountedTerminalSurface: terminalTabs.some((tab) => registeredTabs.has(tab.id))
mountedSurfaceCaptureByTabId: captureMountedTerminalSurfaces(
terminalTabs,
state.terminalLayoutsByTabId
)
}
}
function narrowedEntriesEqual<T>(a: ReadonlyMap<string, T>, b: ReadonlyMap<string, T>): boolean {
function captureMountedTerminalSurfaces(
terminalTabs: AppState['tabsByWorktree'][string],
terminalLayoutsByTabId: AppState['terminalLayoutsByTabId']
): ReadonlyMap<string, MountedTerminalSurfaceCapture> {
let captures: Map<string, MountedTerminalSurfaceCapture> | null = null
for (const tab of terminalTabs) {
const registered = registeredTabs.get(tab.id)
if (!registered) {
continue
}
captures ??= new Map()
captures.set(tab.id, captureMountedTerminalSurface(registered, terminalLayoutsByTabId[tab.id]))
}
return captures ?? EMPTY_NARROWED_BY_KEY
}
function captureMountedTerminalSurface(
registered: RegisteredTerminalTab,
savedLayout: AppState['terminalLayoutsByTabId'][string] | undefined
): MountedTerminalSurfaceCapture {
const manager = registered.getManager()
const paneLeafIds = manager?.getPanes().map((pane) => pane.leafId) ?? []
const activePane = manager?.getActivePane() ?? null
const firstChild = registered.getContainer()?.firstElementChild
// Why: mirrors getRuntimeLeafIdsForTerminal so every leaf the builder resolves has a captured pane id.
const effectiveLeafIds =
paneLeafIds.length > 0
? paneLeafIds
: collectLeafIdsInOrder(savedLayout?.root).filter(isTerminalLeafId)
const numericPaneIdByLeafId = new Map<string, number | null>()
const ptyIdByNumericPaneId = new Map<number, string | null>()
for (const leafId of effectiveLeafIds) {
const numericPaneId = manager?.getNumericIdForLeaf(leafId) ?? null
numericPaneIdByLeafId.set(leafId, numericPaneId)
if (numericPaneId !== null) {
ptyIdByNumericPaneId.set(numericPaneId, registered.getPtyIdForPane(numericPaneId))
}
}
return {
paneLeafIds,
hasLiveActivePane: activePane !== null,
liveActiveLeafId: activePane !== null ? (manager?.getLeafId(activePane.id) ?? null) : null,
liveLayoutRoot: serializePaneTree(
typeof HTMLElement !== 'undefined' && firstChild instanceof HTMLElement ? firstChild : null
),
numericPaneIdByLeafId,
ptyIdByNumericPaneId
}
}
function mountedTerminalSurfaceCaptureEquals(
a: MountedTerminalSurfaceCapture,
b: MountedTerminalSurfaceCapture
): boolean {
return (
a.hasLiveActivePane === b.hasLiveActivePane &&
a.liveActiveLeafId === b.liveActiveLeafId &&
a.paneLeafIds.length === b.paneLeafIds.length &&
a.paneLeafIds.every((leafId, index) => b.paneLeafIds[index] === leafId) &&
narrowedEntriesEqual(a.numericPaneIdByLeafId, b.numericPaneIdByLeafId) &&
narrowedEntriesEqual(a.ptyIdByNumericPaneId, b.ptyIdByNumericPaneId) &&
// Why: serialization allocates a fresh tree per capture, so it compares by content.
jsonContentEquals(a.liveLayoutRoot, b.liveLayoutRoot)
)
}
function mountedSurfaceCapturesEqual(
a: ReadonlyMap<string, MountedTerminalSurfaceCapture>,
b: ReadonlyMap<string, MountedTerminalSurfaceCapture>
): boolean {
if (a === b) {
return true
}
if (a.size !== b.size) {
return false
}
for (const [tabId, capture] of a) {
const other = b.get(tabId)
if (!other || !mountedTerminalSurfaceCaptureEquals(capture, other)) {
return false
}
}
return true
}
function narrowedEntriesEqual<K, T>(a: ReadonlyMap<K, T>, b: ReadonlyMap<K, T>): boolean {
if (a === b) {
return true
}
@@ -1056,9 +1160,6 @@ function canReuseMobileSessionSnapshot(
next: MobileSessionWorktreeInputs
): boolean {
return (
// Why: live DOM/PaneManager state is invisible to store references, so a mounted surface always rebuilds.
!previous.hasMountedTerminalSurface &&
!next.hasMountedTerminalSurface &&
previous.worktreeId === next.worktreeId &&
previous.terminalTabs === next.terminalTabs &&
previous.browserWorkspaces === next.browserWorkspaces &&
@@ -1084,6 +1185,12 @@ function canReuseMobileSessionSnapshot(
narrowedEntriesEqual(
previous.certificateFailureByBrowserPageId,
next.certificateFailureByBrowserPageId
) &&
// Why: live DOM/PaneManager state is invisible to store references; the
// capture makes it comparable instead of forcing mounted worktrees to rebuild.
mountedSurfaceCapturesEqual(
previous.mountedSurfaceCaptureByTabId,
next.mountedSurfaceCaptureByTabId
)
)
}
@@ -1683,12 +1790,10 @@ function resolveMobileTerminalTheme(
}
function getRuntimeLeafIdsForTerminal(
tabId: string,
capture: MountedTerminalSurfaceCapture | undefined,
savedLayout: AppState['terminalLayoutsByTabId'][string] | undefined
): string[] {
const registered = registeredTabs.get(tabId)
const manager = registered?.getManager()
const liveLeafIds = manager?.getPanes().map((pane) => pane.leafId) ?? []
): readonly string[] {
const liveLeafIds = capture?.paneLeafIds ?? []
if (liveLeafIds.length > 0) {
return liveLeafIds
}
@@ -1707,18 +1812,15 @@ function buildMobileTerminalSurfaceTabs(
terminal: NonNullable<AppState['tabsByWorktree'][string]>[number],
unifiedTabId?: string
): RuntimeMobileSessionSnapshotTab[] {
const registered = registeredTabs.get(terminal.id)
const capture = inputs.mountedSurfaceCaptureByTabId.get(terminal.id)
const isDesktopTabActive = unifiedTabId
? isUnifiedTabActiveInActiveGroup(inputs, unifiedTabId)
: inputs.activeTerminalTabId === terminal.id
const manager = registered?.getManager()
const liveActivePaneId = manager?.getActivePane()?.id ?? null
const savedLayout = inputs.terminalLayoutByTabId.get(terminal.id)
const leafIds = getRuntimeLeafIdsForTerminal(terminal.id, savedLayout)
const activeLeafId =
liveActivePaneId !== null
? (manager?.getLeafId(liveActivePaneId) ?? null)
: (savedLayout?.activeLeafId ?? leafIds[0] ?? null)
const leafIds = getRuntimeLeafIdsForTerminal(capture, savedLayout)
const activeLeafId = capture?.hasLiveActivePane
? capture.liveActiveLeafId
: (savedLayout?.activeLeafId ?? leafIds[0] ?? null)
const paneTitles = inputs.paneTitlesByTabId.get(terminal.id) ?? {}
const generatedTitlesEnabled = inputs.generatedTitlesEnabled
const sanitizedSavedLayout = savedLayout
@@ -1736,11 +1838,7 @@ function buildMobileTerminalSurfaceTabs(
? seededLaunchDraft
: null
const publishedLaunchDraft = launchDraftEntry?.text.trim() ? launchDraftEntry : null
const container = registered?.getContainer()
const firstChild = container?.firstElementChild
const liveLayoutRoot = serializePaneTree(
typeof HTMLElement !== 'undefined' && firstChild instanceof HTMLElement ? firstChild : null
)
const liveLayoutRoot = capture?.liveLayoutRoot ?? null
const parentLayout = normalizeTerminalLayoutSnapshot({
// Why: live DOM tree is authoritative when mounted, else the saved tree; synthesize only as a last resort, never re-guess.
root: resolveTerminalLayoutRoot({
@@ -1760,11 +1858,11 @@ function buildMobileTerminalSurfaceTabs(
: {})
} satisfies TerminalLayoutSnapshot).snapshot
return leafIds.map((leafId) => {
const numericPaneId = manager?.getNumericIdForLeaf(leafId) ?? null
const numericPaneId = capture?.numericPaneIdByLeafId.get(leafId) ?? null
const ptyId =
numericPaneId === null
? (savedPtyIdsByLeafId[leafId] ?? (leafIds.length === 1 ? terminal.ptyId : null))
: (registered?.getPtyIdForPane(numericPaneId) ?? savedPtyIdsByLeafId[leafId] ?? null)
: (capture?.ptyIdByNumericPaneId.get(numericPaneId) ?? savedPtyIdsByLeafId[leafId] ?? null)
const legacyPaneId = numericPaneId === null ? /^pane:(\d+)$/.exec(leafId)?.[1] : null
const paneTitle =
numericPaneId !== null