Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
256f83b
Unify terminal starts under one host operation
brennanb2025 Oct 22, 2026
d1156f5
Pin winning geometry across a late terminal join
brennanb2025 Oct 22, 2026
04b4271
Use terminal creation options type in launch race test
brennanb2025 Oct 22, 2026
a311773
Check closed panes after acquiring the terminal spawn lease
brennanb2025 Oct 22, 2026
62c15b5
Preserve terminal start resource and join boundaries
brennanb2025 Oct 22, 2026
0f9abfc
Keep terminal attach regression within project boundaries
brennanb2025 Oct 22, 2026
2f7ccfe
Capture terminal spawn authority and preserve image coverage
brennanb2025 Oct 22, 2026
da3062f
Use checked fixtures for terminal image replay regression
brennanb2025 Oct 22, 2026
8af7dc2
test: expose persisted pane to main layout in attach race
brennanb2025 Oct 22, 2026
7273c51
Merge origin/main into brennanb2025/conv-c6-build
brennanb2025 Oct 23, 2026
1711d5c
test: contract terminal spawn scaling and retention
brennanb2025 Oct 23, 2026
eeb8681
test: correct spawn scaling fixture types and CI selection
brennanb2025 Oct 23, 2026
9393ee0
test(terminal): cover exited prompt claims and adopted team cleanup
brennanb2025 Oct 23, 2026
7294039
Merge main and preserve unified terminal spawn ownership
brennanb2025 Oct 25, 2026
f7f4fea
Merge main and retain terminal pane withdrawal ownership
brennanb2025 Oct 25, 2026
8a71108
Merge main and preserve terminal lifecycle tests across PTY moves
brennanb2025 Oct 25, 2026
be1251a
Merge main and preserve saved-resume ownership in shared spawn
brennanb2025 Oct 25, 2026
bbea78e
Merge main to include CI line-limit repair
brennanb2025 Oct 26, 2026
7eec8f7
test(pty): consolidate mock registry imports
brennanb2025 Oct 26, 2026
fd0fb1a
fix(pty): retain untyped IPC resume inputs in common contract
brennanb2025 Oct 26, 2026
fa3a98d
Merge origin/main into brennanb2025/conv-c6-build
brennanb2025 Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/performance-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ on:
- '!config/scripts/renderer-scrollbar-style-plugin.test.mjs'
# Keep in sync with the contract list in config/vitest.performance.config.ts;
# without these a rename lands green and only breaks the next nightly.
- 'src/main/ipc/pty-spawn-scaling.performance.test.ts'
- 'src/main/ipc/pty/runtime/spawn-preflight-saved-resume.test.ts'
- 'src/main/native-chat/agent-session-wire/structured-agent-session-terminal-writer-acquire.test.ts'
- 'src/main/runtime/hook-status-resume-sleep-ordering.test.ts'
Expand Down
1 change: 1 addition & 0 deletions config/vitest.performance.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import baseConfig from './vitest.config'
import { nodeRuntimePool } from './scripts/vitest-node-runtime-pool'

const contracts = [
'src/main/ipc/pty-spawn-scaling.performance.test.ts',
'src/renderer/src/components/terminal-pane/pty-connection/saved-resume-cancellation.test.ts',
'src/main/ipc/pty/runtime/spawn-preflight-saved-resume.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-terminal-writer-acquire.test.ts',
Expand Down
282 changes: 254 additions & 28 deletions src/main/ipc/pty-attach-view-joins-main-start.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@ import { describe, expect, it, vi } from 'vitest'
import { setupPtyIpcSuite } from './pty-ipc-test-harness'
import { registerPtyHandlers } from './pty/register-handlers'
import { setLocalPtyProvider } from './pty/provider/registry'
import { OrcaRuntimeService } from '../runtime/orca-runtime'
import type { RuntimeStore } from '../runtime/runtime-store-contract'
import { TerminalSurfaceIndex } from '../runtime/terminal/surface/terminal-surface-index'
import { createPaneBindingStore } from './pty-launch-race-test-fixture'
import {
pendingRuntimePaneCreatesByOwnerKey,
makePaneSpawnReservationKey
} from './pty/pane/spawn-reservation'
import { makePaneKey } from '../../shared/stable-pane-id'

import { AgentSessionRecordStore } from '../runtime/agent-session-record-store'
import {
agentSessionRecordFixture,
agentSessionLeaseFixture
} from '../../shared/agent-session-record.test-fixture'
import type { SleepingAgentSessionRecord } from '../../shared/agent-session-resume'
import { forgetRefusedPaneResumeMock } from './pty-ipc-mock-registry'
import { agentSessionOwners } from './pty/pane/agent-session-owners'
import type { TerminalCreateOptions } from '../runtime/runtime-terminal-contracts'

vi.mock('../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
vi.mock('electron', () => import('./pty-ipc-mock-registry').then((m) => m.electronModuleMock()))
Expand Down Expand Up @@ -49,11 +68,6 @@ vi.mock('../codex/codex-state-db-backfill-recovery', () =>
)

type PaneArgs = { worktreeId: string; connectionId: string | null; tabId: string; leafId: string }
type RuntimeSpawnController = {
claimStablePaneCreate(args: PaneArgs): () => void
spawn(args: Record<string, unknown>): Promise<{ id: string }>
}

// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: test doubles implement only the members these spawn paths read.
const testDouble = <T>(value: unknown): T => value as T

Expand All @@ -67,11 +81,38 @@ const PANE: PaneArgs = {
describe('a window view of a pane main is starting', () => {
const { handlers, mainWindow } = setupPtyIpcSuite()

async function viewDuringMainStart(startResult: Record<string, unknown>) {
const providerSpawn = vi.fn(async (opts: { sessionId?: string }) => ({
id: opts.sessionId ?? 'pty-main',
...startResult
}))
async function viewDuringMainStart(
startResult: Record<string, unknown>,
resume?: 'fresh' | 'cold'
) {
let release!: () => void
const gate = new Promise<void>((done) => {
release = done
})
const providerSpawn = vi.fn(async (opts: { sessionId?: string; attachOnly?: boolean }) => {
if (resume) {
expect(forgetRefusedPaneResumeMock).not.toHaveBeenCalled()
expect(cleanup).not.toHaveBeenCalled()
expect(agentSessionOwners.find(claim)).toBeNull()
expect(opts).not.toHaveProperty('command')
expect(opts).not.toHaveProperty('agentSessionEnsure')
expect(opts).not.toHaveProperty('startupCommandDelivery')
expect(opts).not.toHaveProperty('startupIngress')
expect(opts).not.toHaveProperty('launchAgent')
expect(opts).not.toHaveProperty('launchToken')
const env = testDouble<{ env?: Record<string, string> }>(opts).env
expect(env?.AGENT_ONLY).toBeUndefined()
expect(env?.ORCA_AGENT_LAUNCH_TOKEN).toBeUndefined()
expect(env?.ORCA_SEQUENCED_STARTUP_COMMAND).toBeUndefined()
}
await gate
return {
id: opts.sessionId ?? 'pty-main',
incarnationId: 'main-inc',
...(opts.attachOnly ? { isReattach: true } : {}),
...startResult
}
})
setLocalPtyProvider(
testDouble({
spawn: providerSpawn,
Expand All @@ -97,35 +138,220 @@ describe('a window view of a pane main is starting', () => {
getProfiles: vi.fn()
})
)
let controller: RuntimeSpawnController | null = null
const runtime = {
setPtyController: vi.fn((value: RuntimeSpawnController) => {
controller = value
const bindingStore = createPaneBindingStore()
type WriteListener = Parameters<NonNullable<RuntimeStore['onWorkspaceSessionWritten']>>[0]
const written = new Set<WriteListener>()
const notifyWritten = (): void => {
for (const listener of written) {
listener('local')
}
}
const store = {
...bindingStore,
getSettings: () => ({}),
getWorkspaceSessionHostIds: (): ReturnType<
NonNullable<RuntimeStore['getWorkspaceSessionHostIds']>
> => ['local'],
setWorkspaceSession: (...args: Parameters<typeof bindingStore.setWorkspaceSession>) => {
bindingStore.setWorkspaceSession(...args)
notifyWritten()
},
persistPtyBinding: async (...args: Parameters<typeof bindingStore.persistPtyBinding>) => {
const persisted = await bindingStore.persistPtyBinding(...args)
if (persisted) {
notifyWritten()
}
return persisted
},
onWorkspaceSessionWritten: (listener: WriteListener) => {
written.add(listener)
return () => written.delete(listener)
}
}
const runtime = new OrcaRuntimeService()
Object.assign(runtime, {
store,
resolveTerminalWorkspaceLaunchScope: async () => ({
id: PANE.worktreeId,
path: '/tmp/view-join',
connectionId: null
}),
createPreAllocatedTerminalHandle: vi.fn(() => 'term_main'),
getAgentProviderSessionRows: vi.fn(() => []),
registerPreAllocatedHandleForPty: vi.fn(),
registerPty: vi.fn(),
onPtySpawned: vi.fn(),
onPtyExit: vi.fn(),
onPtyData: vi.fn(),
seedHeadlessTerminal: vi.fn()
resolveAgentTerminalCreateOptions: async (_workspace: unknown, opts: unknown) => opts,
resolveWorkspaceTerminalStartupCwd: () => '/tmp/view-join'
})
registerPtyHandlers(
testDouble(mainWindow),
runtime,
undefined,
undefined,
undefined,
testDouble(store)
)
const mainSurfaces = new TerminalSurfaceIndex({
reads: () => runtime.workspaceLayoutReads,
hostIds: () => store.getWorkspaceSessionHostIds(),
homeHostId: (key) => runtime['tryGetWorkspaceSessionHostIdForWorktree'](key),
generatedTitlesEnabled: () => false,
onChanged: () => {}
})
const registerPty = runtime.registerPty.bind(runtime)
const registrations = vi.spyOn(runtime, 'registerPty').mockImplementation((...args) => {
expect(mainSurfaces.surfaces().leaves.find((leaf) => leaf.ptyId === args[0])).toMatchObject({
worktreeId: PANE.worktreeId,
tabId: PANE.tabId,
leafId: PANE.leafId
})
registerPty(...args)
})
const paneKey = makePaneKey(PANE.tabId, PANE.leafId)
const providerSession = { key: 'session_id' as const, id: 'provider-session-alpha-1' }
const saved: SleepingAgentSessionRecord = {
paneKey,
worktreeId: PANE.worktreeId,
agent: 'claude',
providerSession,
prompt: '',
state: 'working',
capturedAt: 20,
updatedAt: 21,
connectionId: null
}
registerPtyHandlers(testDouble(mainWindow), testDouble(runtime))
const main = testDouble<RuntimeSpawnController>(controller)
const releaseClaim = main.claimStablePaneCreate(PANE)
const chat = agentSessionRecordFixture(
agentSessionLeaseFixture({
claimStatus: 'released',
ownerProcess: null,
reservedSpawnToken: null,
deathEvidence: { kind: 'exit-observed', detail: 'closed', observedAt: 1 }
})
)
const records: AgentSessionRecordStore = Object.create(AgentSessionRecordStore.prototype)
const list = vi.fn(() => [chat])
records.listRecords = list
records.getVisibleSessionTabIndex = () => ({ present: true, sessionIds: [chat.sessionId] })
const capture = vi.spyOn(runtime, 'readSavedResumeSource').mockResolvedValue(saved)
const cleanup = vi.spyOn(runtime, 'forgetRefusedSavedResume').mockResolvedValue()
if (resume) {
vi.spyOn(runtime, 'openAgentSessionRecordStore').mockResolvedValue(records)
vi.spyOn(runtime, 'getAgentProviderSessionRows').mockReturnValue([
{
paneKey,
connectionId: null,
agentType: 'claude',
state: 'working',
prompt: '',
receivedAt: 30,
stateStartedAt: 31,
providerSession
}
])
}
const claim = {
agent: 'claude' as const,
digestVersion: 1 as const,
keyId: 'composition-key',
identityDigest: 'composition-C',
worktreeScopeDigest: 'composition-wt'
}
const launch: TerminalCreateOptions =
resume === 'fresh'
? {
command: 'claude --resume provider-session-alpha-1',
launchAgent: 'claude',
launchConfig: {
agentCommand: 'claude',
agentArgs: '',
agentEnv: { AGENT_ONLY: 'yes' }
},
launchToken: 'composition-token',
resumeProviderSession: providerSession,
resumeOrigin: 'saved',
env: {
AGENT_ONLY: 'yes',
ORCA_AGENT_LAUNCH_TOKEN: 'composition-token',
ORCA_SEQUENCED_STARTUP_COMMAND: 'claude --resume provider-session-alpha-1'
},
agentSessionClaim: claim
}
: {}
const started = runtime.createTerminal(`id:${PANE.worktreeId}`, {
tabId: PANE.tabId,
leafId: PANE.leafId,
sessionId: 'pty-main',
presentation: 'background',
...launch
})
await vi.waitFor(() => expect(providerSpawn).toHaveBeenCalledOnce())
const key = makePaneSpawnReservationKey(
PANE.worktreeId,
null,
makePaneKey(PANE.tabId, PANE.leafId)
)!
const pending = pendingRuntimePaneCreatesByOwnerKey.get(key)!
const barrier = pending.promise
// Deliver the captured result after the producer's second registration settles.
const settled = Promise.all([barrier, started]).then(([result]) => result)
const waiting = vi.fn(() => settled)
Object.defineProperty(pending, 'promise', { get: waiting })
const view = handlers.get('pty:spawn')!(null, {
cols: 80,
rows: 24,
...PANE,
sessionId: 'pty-main',
attachOnlyView: true
})
await main.spawn({ cols: 80, rows: 24, cwd: '/tmp/view-join', ...PANE, sessionId: 'pty-main' })
releaseClaim()
return { view: await view, providerSpawn }
const completed = Promise.all([started, view])
await vi.waitFor(() => expect(waiting).toHaveBeenCalled())
release()
const [created, attached] = await completed
expect(registrations.mock.calls.length).toBeGreaterThanOrEqual(2)
expect(pending.operation?.acquisition?.reservation.outcome?.kind).toBe('committed')
expect(pendingRuntimePaneCreatesByOwnerKey.has(key)).toBe(false)
if (resume) {
const decision = { agent: 'claude', providerSession, verdict: 'chat-home' }
expect(created).toMatchObject({
ptyId: 'pty-main',
agentResumeRefused: 'chat-home',
savedResumeDecision: decision
})
expect(attached).toMatchObject({
id: 'pty-main',
isReattach: true,
agentResumeRefused: 'chat-home',
savedResumeDecision: decision
})
expect(list).toHaveBeenCalledOnce()
expect(capture).toHaveBeenCalledOnce()
expect(forgetRefusedPaneResumeMock).toHaveBeenCalledOnce()
expect(cleanup).toHaveBeenCalledExactlyOnceWith(
PANE.worktreeId,
paneKey,
expect.objectContaining({
agent: 'claude',
providerSession,
expectedStatus: { paneKey, receivedAt: 30, stateStartedAt: 31 },
expectedSavedRecord: { paneKey, capturedAt: 20, updatedAt: 21 }
}),
saved
)
expect(runtime.getTerminalLaunchResult('pty-main')).toMatchObject({
savedResumeDecision: decision
})
expect(agentSessionOwners.find(claim)).toBeNull()
}
return { view: attached, providerSpawn }
}

it.each(['fresh', 'cold'] as const)(
'inherits the %s saved refusal through the actual producer and pane join exactly once',
async (mode) => {
const { providerSpawn } = await viewDuringMainStart(
{ coldRestore: { scrollback: 'saved output', cwd: '/tmp/view-join' } },
mode
)
expect(providerSpawn).toHaveBeenCalledOnce()
}
)

it('attaches to the terminal main starts, with one provider request', async () => {
const coldRestore = { scrollback: 'restored history', cwd: '/tmp/view-join' }
const { view, providerSpawn } = await viewDuringMainStart({ coldRestore })
Expand Down
Loading
Loading