perf(automations): narrow status observer updates (#17249)

This commit is contained in:
Neil
2026-08-29 16:02:29 -07:00
committed by GitHub
parent e258785884
commit 1fe558dd28
5 changed files with 164 additions and 40 deletions
@@ -0,0 +1,66 @@
import { describe, expect, it } from 'vitest'
import type { AgentStatusEntry } from '../../../shared/agent-status-types'
import {
selectAutomationAgentStatusEntryChange,
UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY
} from './automation-agent-status-entry-change'
function makeEntry(paneKey: string): AgentStatusEntry {
return {
paneKey,
state: 'working',
prompt: 'turn',
updatedAt: 1,
stateStartedAt: 1,
stateHistory: []
}
}
describe('selectAutomationAgentStatusEntryChange', () => {
it('reads only the target and skips an unchanged entry', () => {
const targetPaneKey = 'target-tab:leaf'
const targetEntry = makeEntry(targetPaneKey)
const entries = Object.fromEntries(
Array.from({ length: 499 }, (_, index) => {
const paneKey = `other-tab:${index}`
return [paneKey, makeEntry(paneKey)]
})
)
entries[targetPaneKey] = targetEntry
let enumerations = 0
const measuredEntries = new Proxy(entries, {
ownKeys: (target) => {
enumerations += 1
return Reflect.ownKeys(target)
}
})
expect(selectAutomationAgentStatusEntryChange(measuredEntries, targetPaneKey, undefined)).toBe(
targetEntry
)
expect(
selectAutomationAgentStatusEntryChange(measuredEntries, targetPaneKey, targetEntry)
).toBe(UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY)
expect(enumerations).toBe(0)
})
it('reports removal and ignores inherited or non-enumerable entries', () => {
const targetPaneKey = 'target-tab:leaf'
const previousEntry = makeEntry(targetPaneKey)
const inheritedEntries = Object.create({ [targetPaneKey]: previousEntry }) as Record<
string,
AgentStatusEntry
>
const nonEnumerableEntries = Object.defineProperty({}, targetPaneKey, {
value: previousEntry
}) as Record<string, AgentStatusEntry>
expect(selectAutomationAgentStatusEntryChange({}, targetPaneKey, previousEntry)).toBeUndefined()
expect(selectAutomationAgentStatusEntryChange(inheritedEntries, targetPaneKey, undefined)).toBe(
UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY
)
expect(
selectAutomationAgentStatusEntryChange(nonEnumerableEntries, targetPaneKey, undefined)
).toBe(UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY)
})
})
@@ -0,0 +1,15 @@
import type { AgentStatusEntry } from '../../../shared/agent-status-types'
export const UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY = Symbol('unchanged-agent-status-entry')
export function selectAutomationAgentStatusEntryChange(
entries: Readonly<Record<string, AgentStatusEntry>>,
targetPaneKey: string,
previousEntry: AgentStatusEntry | undefined
): AgentStatusEntry | undefined | typeof UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY {
const entry = Object.prototype.propertyIsEnumerable.call(entries, targetPaneKey)
? entries[targetPaneKey]
: undefined
// Why: status writes replace entries; unchanged identity proves an unrelated publication.
return entry === previousEntry ? UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY : entry
}
@@ -0,0 +1,30 @@
import type { AgentStatusEntry } from '../../../shared/agent-status-types'
const TARGET_PANE_KEY = 'agent-tab:7c6fb4e5-3bf1-4ff4-8259-03f7ae81c40d'
export function countUnchangedObserverHistoryReads(
state: { agentStatusByPaneKey: Record<string, AgentStatusEntry> },
subscriber: (() => void) | null
): number {
if (!subscriber) {
throw new Error('agent status observer was not registered')
}
let historyReads = 0
state.agentStatusByPaneKey = {
[TARGET_PANE_KEY]: {
paneKey: TARGET_PANE_KEY,
state: 'working',
prompt: 'turn',
updatedAt: Date.now() + 1,
stateStartedAt: Date.now() + 1,
get stateHistory() {
historyReads += 1
return []
}
}
}
subscriber()
historyReads = 0
subscriber()
return historyReads
}
@@ -1,6 +1,7 @@
import type * as ReactModule from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AUTOMATIONS_CHANGED_EVENT } from '@/lib/automations-changed-window-event'
import { countUnchangedObserverHistoryReads } from './automation-dispatch-observer-test-probe'
const mockDispatchEvent = vi.fn()
@@ -620,10 +621,11 @@ describe('useAutomationDispatchEvents setup launch', () => {
await vi.waitFor(() => expect(mockFinalizeTerminalOwnership).toHaveBeenCalledOnce())
})
it('persists assistant output from a batched working→done→working transition', async () => {
it('skips unchanged status and persists batched working→done→working output', async () => {
const paneKey = 'agent-tab:7c6fb4e5-3bf1-4ff4-8259-03f7ae81c40d'
await registerAndDispatch()
expect(countUnchangedObserverHistoryReads(state, latestStoreSubscriber)).toBe(0)
const transitionStartedAt = Date.now() + 1
state.agentStatusByPaneKey = {
[paneKey]: {
@@ -32,8 +32,12 @@ import {
toSshExecutionHostId
} from '../../../shared/execution-host'
import { parseWorkspaceKey } from '../../../shared/workspace-scope'
import type { AgentStateHistoryEntry } from '../../../shared/agent-status-types'
import type { AgentStateHistoryEntry, AgentStatusEntry } from '../../../shared/agent-status-types'
import { resolveFolderWorkspaceHost } from '../../../shared/folder-workspace-execution-host'
import {
selectAutomationAgentStatusEntryChange,
UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY
} from './automation-agent-status-entry-change'
const activeReuseDispatchTabIds = new Set<string>()
@@ -450,57 +454,64 @@ export function useAutomationDispatchEvents(): void {
): void => {
let sawWorkingAfterStart = false
let observedStateHistory: AgentStateHistoryEntry[] = []
let observedEntry: AgentStatusEntry | undefined
const checkCurrentStatus = (): void => {
const { agentStatusByPaneKey } = useAppStore.getState()
for (const [paneKey, entry] of Object.entries(agentStatusByPaneKey)) {
if (paneKey !== targetPaneKey || entry.updatedAt < startedAfter) {
const entryChange = selectAutomationAgentStatusEntryChange(
useAppStore.getState().agentStatusByPaneKey,
targetPaneKey,
observedEntry
)
if (entryChange === UNCHANGED_AUTOMATION_AGENT_STATUS_ENTRY) {
return
}
const entry = entryChange
observedEntry = entry
if (!entry || entry.updatedAt < startedAfter) {
return
}
const historyOverlap = getAgentStateHistoryOverlap(
observedStateHistory,
entry.stateHistory
)
// Why: sawWorkingAfterStart stays monotonic — a recreated entry
// (transport loss, PTY exit, cap eviction) arrives with an empty
// stateHistory, so clearing it here would strand reuseSession runs
// with nothing left to re-derive the working edge from.
for (const historicalState of entry.stateHistory.slice(historyOverlap)) {
if (historicalState.startedAt < startedAfter) {
continue
}
const historyOverlap = getAgentStateHistoryOverlap(
observedStateHistory,
entry.stateHistory
)
// Why: sawWorkingAfterStart stays monotonic — a recreated entry
// (transport loss, PTY exit, cap eviction) arrives with an empty
// stateHistory, so clearing it here would strand reuseSession runs
// with nothing left to re-derive the working edge from.
for (const historicalState of entry.stateHistory.slice(historyOverlap)) {
if (historicalState.startedAt < startedAfter) {
continue
}
if (historicalState.state === 'working') {
sawWorkingAfterStart = true
}
if (
historicalState.state === 'done' &&
(!options?.requireWorkingAfterStart || sawWorkingAfterStart)
) {
// Why: this `done` already rolled out of the live entry, so its output
// survives only in the entry-level completed slot.
latestAssistantMessage =
entry.lastCompletedAssistantMessage?.trim() || latestAssistantMessage
handleAgentDone()
return
}
}
observedStateHistory = [...entry.stateHistory]
if (entry.state === 'working') {
if (historicalState.state === 'working') {
sawWorkingAfterStart = true
}
if (
entry.state === 'done' &&
// Why: a session-boundary done is the agent CONNECTING (Claude SessionStart
// fires at launch, before the argv prompt submits) — completing here would
// close the tab and record an empty run result.
entry.sessionBoundary !== true &&
historicalState.state === 'done' &&
(!options?.requireWorkingAfterStart || sawWorkingAfterStart)
) {
// Why: this `done` already rolled out of the live entry, so its output
// survives only in the entry-level completed slot.
latestAssistantMessage =
entry.lastAssistantMessage?.trim() || latestAssistantMessage
entry.lastCompletedAssistantMessage?.trim() || latestAssistantMessage
handleAgentDone()
return
}
}
observedStateHistory = [...entry.stateHistory]
if (entry.state === 'working') {
sawWorkingAfterStart = true
}
if (
entry.state === 'done' &&
// Why: a session-boundary done is the agent CONNECTING (Claude SessionStart
// fires at launch, before the argv prompt submits) — completing here would
// close the tab and record an empty run result.
entry.sessionBoundary !== true &&
(!options?.requireWorkingAfterStart || sawWorkingAfterStart)
) {
latestAssistantMessage =
entry.lastAssistantMessage?.trim() || latestAssistantMessage
handleAgentDone()
}
}
// Why: Codex/Claude completion normally arrives through the global
// hook IPC listener, not the hidden PTY OSC fallback.