From e04db518b2feaee53963af16c82a2b6da703b2df Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:16:45 -0700 Subject: [PATCH] fix(mobile): preserve terminal creation through reconnect --- .../mobile-session-startup-source.test.ts | 3 +- ...nitial-session-terminal-autocreate.test.ts | 43 ++++++++++++++++++- ...use-initial-session-terminal-autocreate.ts | 8 +++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/mobile/src/session/mobile-session-startup-source.test.ts b/mobile/src/session/mobile-session-startup-source.test.ts index 0fc3ab0a25e..80484431c62 100644 --- a/mobile/src/session/mobile-session-startup-source.test.ts +++ b/mobile/src/session/mobile-session-startup-source.test.ts @@ -44,7 +44,8 @@ describe('mobile session startup', () => { expect(autoCreateHookSource).toContain('shouldAutoCreateInitialSessionTerminal({') expect(autoCreateHookSource).toContain('stateRef.current.autoCreatedForWorktree === worktreeId') expect(autoCreateHookSource).toContain('stateRef.current.autoCreatedForWorktree = worktreeId') - expect(autoCreateHookSource).toContain('newlyCreatedWorkspace && terminalsLoaded') + expect(autoCreateHookSource).toContain("connState === 'connected'") + expect(autoCreateHookSource).toContain('(visibleTabCount > 0 || activeHandle !== null)') // Why: both callbacks are re-created every render, so the effect must reach them // through useEffectEvent rather than deps or a render-time ref write. expect(autoCreateHookSource).toContain('useEffectEvent(args.consumeCreationRoute)') diff --git a/mobile/src/session/use-initial-session-terminal-autocreate.test.ts b/mobile/src/session/use-initial-session-terminal-autocreate.test.ts index 794fb712aad..46a7bb859fa 100644 --- a/mobile/src/session/use-initial-session-terminal-autocreate.test.ts +++ b/mobile/src/session/use-initial-session-terminal-autocreate.test.ts @@ -11,6 +11,8 @@ type HarnessProps = { terminalsLoaded: boolean visibleTabCount: number worktreeId: string + connected?: boolean + hasClient?: boolean } describe('useInitialSessionTerminalAutoCreate', () => { @@ -24,9 +26,9 @@ describe('useInitialSessionTerminalAutoCreate', () => { function Harness(props: HarnessProps): null { useInitialSessionTerminalAutoCreate({ - client: {}, + client: props.hasClient === false ? null : {}, newlyCreatedWorkspace: props.newlyCreatedWorkspace, - connState: 'connected', + connState: props.connected === false ? 'disconnected' : 'connected', terminalsLoaded: props.terminalsLoaded, visibleTabCount: props.visibleTabCount, activeHandle: null, @@ -91,6 +93,15 @@ describe('useInitialSessionTerminalAutoCreate', () => { visibleTabCount: 0, worktreeId: 'new' }) + expect(consumeCreationRoute).not.toHaveBeenCalled() + expect(createTerminal).toHaveBeenCalledOnce() + + await render({ + newlyCreatedWorkspace: true, + terminalsLoaded: true, + visibleTabCount: 1, + worktreeId: 'new' + }) expect(consumeCreationRoute).toHaveBeenCalledOnce() expect(createTerminal).toHaveBeenCalledOnce() }) @@ -118,6 +129,34 @@ describe('useInitialSessionTerminalAutoCreate', () => { }) expect(createTerminal).not.toHaveBeenCalled() }) + + it.each([ + { connected: false, hasClient: true }, + { connected: true, hasClient: false } + ])( + 'keeps the creation route armed until a client reconnects (connected=$connected, hasClient=$hasClient)', + async ({ connected, hasClient }) => { + await render({ + newlyCreatedWorkspace: true, + terminalsLoaded: true, + visibleTabCount: 0, + worktreeId: 'new', + connected, + hasClient + }) + expect(consumeCreationRoute).not.toHaveBeenCalled() + expect(createTerminal).not.toHaveBeenCalled() + + await render({ + newlyCreatedWorkspace: true, + terminalsLoaded: true, + visibleTabCount: 0, + worktreeId: 'new' + }) + expect(consumeCreationRoute).not.toHaveBeenCalled() + expect(createTerminal).toHaveBeenCalledOnce() + } + ) }) describe('useWorktreeSessionTabsLoaded', () => { diff --git a/mobile/src/session/use-initial-session-terminal-autocreate.ts b/mobile/src/session/use-initial-session-terminal-autocreate.ts index e71aa8a91aa..a5b3fef0550 100644 --- a/mobile/src/session/use-initial-session-terminal-autocreate.ts +++ b/mobile/src/session/use-initial-session-terminal-autocreate.ts @@ -52,7 +52,13 @@ export function useInitialSessionTerminalAutoCreate( const createTerminal = useEffectEvent(args.createTerminal) useEffect(() => { - if (newlyCreatedWorkspace && terminalsLoaded) { + if ( + newlyCreatedWorkspace && + client && + connState === 'connected' && + terminalsLoaded && + (visibleTabCount > 0 || activeHandle !== null) + ) { consumeCreationRoute() } if (