From c308df1addcff55e75ef23cafd2a4d4aca16cc66 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:45:36 -0700 Subject: [PATCH] fix(mobile): skip floating workspace repo lookup --- ...use-mobile-native-chat-readability.test.ts | 25 ++++++++++++++----- .../use-mobile-native-chat-readability.ts | 11 +++++++- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/mobile/src/session/use-mobile-native-chat-readability.test.ts b/mobile/src/session/use-mobile-native-chat-readability.test.ts index 46feaf79217..41d08c3c32c 100644 --- a/mobile/src/session/use-mobile-native-chat-readability.test.ts +++ b/mobile/src/session/use-mobile-native-chat-readability.test.ts @@ -2,6 +2,7 @@ import { createElement } from 'react' import { act, create, type ReactTestRenderer } from 'react-test-renderer' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { RpcClient } from '../transport/rpc-client' +import { FLOATING_WORKSPACE_WORKTREE_ID } from './floating-workspace' import { useMobileNativeChatReadability } from './use-mobile-native-chat-readability' describe('useMobileNativeChatReadability', () => { @@ -18,15 +19,19 @@ describe('useMobileNativeChatReadability', () => { renderer = null }) - async function mount(connectionId: string | null): Promise { + async function mount( + connectionId: string | null, + worktreeId = 'repo::/worktree' + ): Promise> { + const sendRequest = vi.fn().mockResolvedValue({ + ok: true, + result: { repos: [{ id: 'repo', connectionId }] } + }) const client = { - sendRequest: vi.fn().mockResolvedValue({ - ok: true, - result: { repos: [{ id: 'repo', connectionId }] } - }) + sendRequest } as unknown as RpcClient function Harness(): null { - readable = useMobileNativeChatReadability(client, 'repo::/worktree') + readable = useMobileNativeChatReadability(client, worktreeId) return null } const original = console.error @@ -44,6 +49,7 @@ describe('useMobileNativeChatReadability', () => { } finally { consoleSpy.mockRestore() } + return sendRequest } it('admits local and runtime-owned transcript hosts', async () => { @@ -61,6 +67,13 @@ describe('useMobileNativeChatReadability', () => { expect(readable).toBe(false) }) + it('treats the host-local floating workspace as readable without listing repos', async () => { + const sendRequest = await mount(null, FLOATING_WORKSPACE_WORKTREE_ID) + + expect(readable).toBe(true) + expect(sendRequest).not.toHaveBeenCalled() + }) + it('fails closed immediately while a reused route resolves its new worktree', async () => { let resolveNext: (response: unknown) => void = () => {} const client = { diff --git a/mobile/src/session/use-mobile-native-chat-readability.ts b/mobile/src/session/use-mobile-native-chat-readability.ts index e952335fe23..e3729022d38 100644 --- a/mobile/src/session/use-mobile-native-chat-readability.ts +++ b/mobile/src/session/use-mobile-native-chat-readability.ts @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react' import type { RpcClient } from '../transport/rpc-client' +import { isFloatingWorkspaceWorktreeId } from './floating-workspace' import { isMobileNativeChatTranscriptReadable } from './mobile-native-chat-eligibility' import { getRepoIdFromMobileWorktreeId } from './mobile-session-route-helpers' @@ -10,12 +11,17 @@ export function useMobileNativeChatReadability( client: RpcClient | null, worktreeId: string ): boolean { + const isFloatingWorkspace = isFloatingWorkspaceWorktreeId(worktreeId) const [state, setState] = useState({ client: null, worktreeId: '', readable: false }) useEffect(() => { + // Why: the floating workspace always runs on the paired host and has no repo connection to resolve. + if (isFloatingWorkspace) { + return + } let active = true if (!client) { setState({ client, worktreeId, readable: false }) @@ -46,7 +52,10 @@ export function useMobileNativeChatReadability( return () => { active = false } - }, [client, worktreeId]) + }, [client, isFloatingWorkspace, worktreeId]) + if (isFloatingWorkspace) { + return true + } // Why: route reuse renders before its new effect resolves; never expose the // previous repo's readability under a different client/worktree key. return state.client === client && state.worktreeId === worktreeId ? state.readable : false