From ba41d8a5545133e92f3e3149bf858ba2ef623d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Sat, 19 Sep 2026 09:30:12 +0000 Subject: [PATCH] fix(mobile): state an unclassified assistant failure once Explorer finding: session-error: The same failure is stated three times in near-identical copy ("Response failed", "The response failed.", "The response failed."), which reads as a glitch rather than a designed error state. The user-agent explorer found this while using the app like a user. One finding per item; the explorer never edits product code. Flow: session-error Found on revision: fd04ca1ef Repro: 1. set this state first: account e2e-mobile-cloud-android@example.com signed in via emailed code (~$258.98 credits), Code Reviewer seeded with 3 pull requests (the `checks` state is blocked by a harness preflight: tracked edits present), a second organization to switch to, one needs-input session; system night mode, display size 480x1040, portrait. 2. open the app on emulator-5554 3. reach the screen the capture names (session-error) 4. the capture shows the defect named below Observed: The same failure is stated three times in near-identical copy ("Response failed", "The response failed.", "The response failed."), which reads as a glitch rather than a designed error state. Expected: the screen renders without this defect Evidence (from the device run): - ~/.local/share/kwf/findings/explorer-session-error-the-same-failure-is-stated-three-t-795818de/session-error.png --- .../components/agents/message-bubble.test.ts | 20 +++ .../src/components/agents/message-bubble.tsx | 4 +- .../agents/message-failure-state.test.ts | 7 +- .../agents/message-failure-state.ts | 18 ++- .../session-detail-content-helpers.test.ts | 129 ++++++++++++++++++ .../agents/session-detail-content-helpers.ts | 68 +++++++++ .../agents/session-detail-content.test.ts | 98 ++++++++++++- .../agents/session-detail-content.tsx | 32 ++++- .../agents/session-detail-queue.test.ts | 1 + .../agents/session-terminal-error.test.ts | 72 ++++++++++ .../agents/session-terminal-error.ts | 34 +++++ .../components/agents/session-transcript.ts | 45 +++--- 12 files changed, 499 insertions(+), 29 deletions(-) diff --git a/apps/mobile/src/components/agents/message-bubble.test.ts b/apps/mobile/src/components/agents/message-bubble.test.ts index ece3b83d92..4cd18985bd 100644 --- a/apps/mobile/src/components/agents/message-bubble.test.ts +++ b/apps/mobile/src/components/agents/message-bubble.test.ts @@ -303,6 +303,26 @@ describe('MessageBubble failure footer', () => { expect(findElementByType(tree, 'Button', p => p.accessibilityLabel === 'Retry')).toBeNull(); }); + it('states a generic assistant failure once, without a detail line repeating the title', async () => { + const tree = await renderBubbleWithHandlers( + assistantMessageWithError('m-asst-laconic', 'APIError'), + { + onRetryMessage: vi.fn<(message: StoredMessage) => void>(), + } + ); + expect(findText(tree, t => t === 'Response failed')).toBe(true); + expect(findText(tree, t => t === 'The response failed.')).toBe(false); + }); + + it('keeps the classified detail line for a known assistant error', async () => { + const tree = await renderBubbleWithHandlers( + assistantMessageWithError('m-asst-known', 'ProviderAuthError'), + { onRetryMessage: vi.fn<(message: StoredMessage) => void>() } + ); + expect(findText(tree, t => t === 'Response failed')).toBe(true); + expect(findText(tree, t => t === 'The provider rejected the request.')).toBe(true); + }); + it('does not render the footer when no handler is supplied', async () => { const tree = await renderBubbleWithHandlers(userMessage('m-nohandler'), { deliveryState: { status: 'failed', error: 'nope', reason: 'exhausted' }, diff --git a/apps/mobile/src/components/agents/message-bubble.tsx b/apps/mobile/src/components/agents/message-bubble.tsx index 081a00c041..b8dcef0b74 100644 --- a/apps/mobile/src/components/agents/message-bubble.tsx +++ b/apps/mobile/src/components/agents/message-bubble.tsx @@ -162,7 +162,9 @@ function MessageBubbleImpl({ > {failure.title} - {failure.detail} + {failure.detail !== null ? ( + {failure.detail} + ) : null} {failure.canRetry && onRetryMessage ? (