Skip to content

feat(mobile): show a loading indicator while a cached transcript refreshes - #6223

Merged
iscekic merged 1 commit into
mainfrom
kwf/owner-mobile-stale-transcript-loading-7b2d
Sep 18, 2026
Merged

iscekic merged 1 commit into
mainfrom
kwf/owner-mobile-stale-transcript-loading-7b2d

Conversation

@iscekic

@iscekic iscekic commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • Opening a session whose transcript is cached now shows an inline "Loading…" spinner row above the readable rows until the current transcript arrives.
  • The row keeps a fixed height, so the transcript does not jump when the indicator appears or clears.
  • The indicator disappears as soon as the live transcript lands.
  • If the fetch fails, the row becomes the connection-lost warning with Retry instead of spinning forever.

Changelog for maintainers

  • The session manager exposes a writable isRefreshingCachedTranscript atom, true only while cached transcript rows are visible and the live transcript has not landed.
  • The atom stays false for callers without a cached-page reader, so web and extension behavior is unchanged.
  • It clears on the live page, replay completion, the replayed root session.created on transports that fetch no page, fetch errors, transcript clears, and session resets.
  • On mobile the refresh state outranks the connection state, and the row renders only when at least one message is present.
  • The indicator reuses the existing fixed-height row slot and the generic loading translation.
  • Review hint: walk each clear path in the session manager, especially the replay path without a page fetch that prevents a stuck spinner.

E2E proof

[e3] Warm a session's cache, then reopen it while offline and let the fetch fail: the cached rows stay readable, the row turns into the connection-lost warning with Retry (no endless spinner), and tapping… — android emulator-5554: after fault.sh down nextjs the reopen of the warmed session kept its cached rows ('fake:echo:e2 warm transcript', 'e2, Assistant message') while the row showed 'Connection lost' + 'Retry connection' and no 'Loading' line (e3-service-warning.log; screenshot e3-service-warning.png); the warning persisted with nextjs back up (e3-service-afterup.log) and tapping 'Retry connection' cleared it within 1s with the transcript still readable (e3-service-recovered.log), the online warm open having passed in e3-warm.log ('SCENE e3warm OK'). Harness gap: the parked e3 step list…

[e3] Warm a session's cache, then reopen it while offline and let the fetch fail: the cached rows stay readable, the row turns into the connection-lost warning with Retry (no endless spinner), and tapping… — prior/e3-service-warning.png

[e1] Warm a session's cache, then reopen it offline and let the fetch fail: the cached rows stay readable, the row turns into the connection-lost warning with Retry (no endless spinner), and tapping Retry… — android emulator-5554: after warming 'e2 warm transcript' online (e1-open-online.log) and killing nextjs, reopening the session kept the cached rows and showed the retryable warning — e1-reopen-offline.log: 'android.widget.TextView Connection lost tappable [424,314][621,351]' and 'android.view.ViewGroup fake:echo:e2 warm transcript, User message tappable [0,1913][1080,2051]' (that digest's row line is the warning, not a spinner); the change's refresh row is captured live under a stalled nextjs with the same cached rows — e1-loading.log: 'android.view.ViewGroup Loading… tappable…

[e1] Warm a session's cache, then reopen it offline and let the fetch fail: the cached rows stay readable, the row turns into the connection-lost warning with Retry (no endless spinner), and tapping Retry… — e2e-mobile-app/e1-offline-warning.png

[e1] reopen a warmed session with the live transport stalled — android emulator-5554: digest shows the refresh row 'android.view.ViewGroup Loading… tappable [0,305][1080,360]' in the fixed slot under the header while the cached rows 'fake:echo:e2 warm transcript' (User message) and 'e2' (Assistant message) stay readable, with out/fault-ca2.log 'fault.sh: stalled pids [24331] for 55s' holding the live fetch; the transport-up run (out/e1-refresh.log) shows the rows with no Loading… row; screenshot out/z-e2.png for the visual reviewer.

[e1] reopen a warmed session with the live transport stalled — prior/z-e2.png

[e1] Warm a session's cache, then reopen it offline and let the fetch fail: the cached rows stay readable, the row turns into the connection-lost warning with Retry (no endless spinner), and tapping Retry…

[e1] Warm a session's cache, then reopen it offline and let the fetch fail: the cached rows stay readable, the row turns into the connection-lost warning with Retry (no endless spinner), and tapping Retry… — e2e-mobile-app/e1-loading.png

[e2] Warm a session's cache, then reopen it on a slow/throttled network: the stale transcript shows with the "Loading…" spinner row, and the row disappears (no transcript jump) once the live transcript… — android emulator-5604. Two runs of the rewarded step list. Throttled/blocked backend (fault.sh stall nextjs): e2z-run.log line 8 'android.view.ViewGroup Loading… tappable [0,305][1080,360]' and line 9 'android.widget.TextView Loading… tappable [512,314][633,351]' above the stale rows (line 11 'android.view.ViewGroup fake:echo:refresh warm transcript, User message tappable [0,1913][1080,2051]', line 13 'android.view.ViewGroup refresh, Assistant message tappable [0,2051][1080,2143]'), capture e2z.png. Landing: e2-landing-run.log line 1 'SCENE e2 OK' for the steps saved in e2.replay.json (assert…

[e2] Warm a session's cache, then reopen it on a slow/throttled network: the stale transcript shows with the "Loading…" spinner row, and the row disappears (no transcript jump) once the live transcript… — prior/e2z.png

[e2] Warm a session's cache, then reopen it on a slow/throttled network: the stale transcript shows with the "Loading…" spinner row, and the row disappears (no transcript jump) once the live transcript…

[e2] Warm a session's cache, then reopen it on a slow/throttled network: the stale transcript shows with the "Loading…" spinner row, and the row disappears (no transcript jump) once the live transcript… — scripted-shard1/e2.png

E2E proof — log excerpts

[e1] Warm a session's cache, then reopen it offline and let the fetch fail: the  -> pass :: android emulator-5554: after warming 'e2 warm transcript' online (e1-open-online.log) and killing nextjs, reopening the session kept the cached rows and showed the retryable warning — e1-reopen-offline.log: 'android.widget.TextView Connection lost tappable [424,314][621,351]' and 'android.view.ViewGroup fake:echo:e2 warm transcript, User message tappable [0,1913][1080,2051]' (that digest's row line is the warning, not a spinner); the change's refresh row is captured live under a stalled nextjs with the same cached rows — e1-loading.log: 'android.view.ViewGroup Loading… tappable [0,305][1080,360]'; tapping Retry with nextjs restored cleared the warning and replayed the live transcript — e1-re
/home/igor_kilocode_ai/.local/share/kwf/sections/owner-mobile-stale-transcript-loading-7b2d/e2e-mobile-app/e1-reopen-offline.log
hierarchy: /tmp/kilo-hierarchy.WpBa5W
android.widget.LinearLayout com.kilocode.kiloapp:id/action_bar_root tappable [0,0][1080,2400]
android.widget.FrameLayout android:id/content tappable [0,0][1080,2400]
android.widget.Button Go back tappable [0,163][101,264]
android.view.View e2 warm transcript tappable [111,180][828,245]
android.widget.Button Context 12 of 200,000 tokens, 0% used. Tap to view context details. tappable [854,156][1043,271]
android.widget.TextView 0% tappable [976,195][1013,232]
android.widget.TextView Connection lost tappable [424,314][621,351]
android.widget.Button Retry connection tappable [635,314][701,351]
android.widget.TextView Retry tappable [635,314][701,351]
android.widget.TextView 7:28 PM tappable [478,1855][601,1894]
android.view.ViewGroup fake:echo:e2 warm transcript, User message tappable [0,1913][1080,2051]
android.widget.TextView fake:echo:e2 warm transcript tappable [466,1950][1011,2013]
android.view.ViewGroup e2, Assistant message tappable [0,2051][1080,2143]
android.widget.TextView e2 tappable [37,2065][1045,2128]
android.widget.EditText Message [53,2190][800,2310]
android.widget.Button Start voice input [835,2204][926,2296]
android.widget.Button Send message [926,2187][1052,2313]
/home/igor_kilocode_ai/.local/share/kwf/sections/owner-mobile-stale-transcript-loading-7b2d/e2e-mobile-app/e1-open-online.log
hierarchy: /tmp/kilo-hierarchy.9Qw63i
android.widget.LinearLayout com.kilocode.kiloapp:id/action_bar_root tappable [0,0][1080,2400]
android.widget.FrameLayout android:id/content tappable [0,0][1080,2400]
android.widget.Button Go back tappable [0,163][101,264]
android.widget.Button Rename session: e2 warm transcript tappable [111,149][827,278]
android.view.View e2 warm transcript tappable [111,180][827,245]
android.widget.Button Context 12 of 200,000 tokens, 0% used. Tap to view context details. tappable [854,156][1043,271]
android.widget.TextView 0% tappable [976,195][1013,232]
android.widget.TextView 7:28 PM tappable [478,1712][601,1751]
android.view.ViewGroup fake:echo:e2 warm transcript, User message tappable [0,1770][1080,1908]
android.widget.TextView fake:echo:e2 warm transcript tappable [466,1807][1011,1870]
android.widget.Button Preparation complete tappable [40,1929][1042,2030]
android.widget.TextView Preparation complete tappable [188,1956][506,2002]
android.view.ViewGroup e2, Assistant message tappable [0,2051][1080,2143]
android.widget.TextView e2 tappable [37,2065][1045,2128]
android.widget.Button Add attachment tappable [28,2213][101,2287]
android.widget.EditText Message tappable [126,2190][800,2310]
android.widget.Button Start voice input tappable [835,2204][926,2296]
android.widget.Button Send message [926,2187][1052,2313]
/home/igor_kilocode_ai/.local/share/kwf/sections/owner-mobile-stale-transcript-loading-7b2d/e2e-mobile-app/e1-loading.log
hierarchy: /tmp/kilo-hierarchy.sV8hRO
android.widget.LinearLayout com.kilocode.kiloapp:id/action_bar_root tappable [0,0][1080,2400]
android.widget.FrameLayout android:id/content tappable [0,0][1080,2400]
android.widget.Button Go back tappable [0,163][101,264]
android.view.View e2 warm transcript tappable [111,180][828,245]
android.widget.Button Context 12 of 200,000 tokens, 0% used. Tap to view context details. tappable [854,156][1043,271]
android.widget.TextView 0% tappable [976,195][1013,232]
android.view.ViewGroup Loading… tappable [0,305][1080,360]
android.widget.TextView Loading… tappable [512,314][633,351]
android.widget.TextView 7:28 PM tappable [478,1855][601,1894]
android.view.ViewGroup fake:echo:e2 warm transcript, User message tappable [0,1913][1080,2051]
android.widget.TextView fake:echo:e2 warm transcript tappable [466,1950][1011,2013]
android.view.ViewGroup e2, Assistant message tappable [0,2051][1080,2143]
android.widget.TextView e2 tappable [37,2065][1045,2128]
android.widget.EditText Message [53,2190][800,2310]
android.widget.Button Start voice input [835,2204][926,2296]
android.widget.Button Send message [926,2187][1052,2313]
/home/igor_kilocode_ai/.local/share/kwf/sections/owner-mobile-stale-transcript-loading-7b2d/e2e-mobile-app/e1-retry-recovered.log
hierarchy: /tmp/kilo-hierarchy.NRZJzy
android.widget.LinearLayout com.kilocode.kiloapp:id/action_bar_root tappable [0,0][1080,2400]
android.widget.FrameLayout android:id/content tappable [0,0][1080,2400]
android.widget.Button Go back tappable [0,163][101,264]
android.widget.Button Rename session: e2 warm transcript tappable [111,149][827,278]
android.view.View e2 warm transcript tappable [111,180][827,245]
android.widget.Button Context 12 of 200,000 tokens, 0% used. Tap to view context details. tappable [854,156][1043,271]
android.widget.TextView 0% tappable [976,195][1013,232]
android.widget.TextView 7:28 PM tappable [478,1712][601,1751]
android.view.ViewGroup fake:echo:e2 warm transcript, User message tappable [0,1770][1080,1908]
android.widget.TextView fake:echo:e2 warm transcript tappable [466,1807][1011,1870]
android.widget.Button Preparation complete tappable [40,1929][1042,2030]
android.widget.TextView Preparation complete tappable [188,1956][506,2002]
android.view.ViewGroup e2, Assistant message tappable [0,2051][1080,2143]
android.widget.TextView e2 tappable [37,2065][1045,2128]
android.widget.Button Add attachment tappable [28,2213][101,2287]
android.widget.EditText Message tappable [126,2190][800,2310]
android.widget.Button Start voice input tappable [835,2204][926,2296]
android.widget.Button Send message [926,2187][1052,2313]
Owner request

Surface: the mobile app (apps/mobile).

While a stale session transcript is shown and the new session transcript is being fetched, show a loading indicator. Best in class UX.

Follow-ups (not changed here)

Owner manual verification

Owner verification is pending; these checks did not pass automatically.

  • UX audit: zero UX-DEFECT on the sessions list, the cached-transcript failure screen and the full-screen 'Couldn't load this session' error (each has a next action), no owner-manual check assigned.

e2

Comment thread packages/cloud-agent-sdk/src/session-manager.ts
@kilo-code-bot

kilo-code-bot Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The branch was rebased, so the incremental range was not diffable; the full PR diff (7 files, 271 insertions) was reviewed at HEAD 354fec1f, confirming the previously flagged stuck refresh path is resolved and finding no new issues in the changed code.

Files Reviewed (7 files)
  • apps/mobile/src/components/agents/session-connection-indicator.mounted.test.tsx
  • apps/mobile/src/components/agents/session-connection-indicator.tsx
  • apps/mobile/src/components/agents/session-detail-content.test.ts
  • apps/mobile/src/components/agents/session-detail-content.tsx
  • apps/mobile/src/components/agents/session-detail-queue.test.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts
Previous Review Summaries (2 snapshots, latest commit d0ab8ed)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit d0ab8ed)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The branch was rebased, so the incremental range was not diffable and the full PR diff (7 files, 271 insertions) was reviewed against HEAD: the prior WARNING about isRefreshingCachedTranscript outliving a legacy fetchSnapshot replay is resolved, and no new issues were found in the changed code.

Files Reviewed (7 files)
  • apps/mobile/src/components/agents/session-connection-indicator.mounted.test.tsx
  • apps/mobile/src/components/agents/session-connection-indicator.tsx
  • apps/mobile/src/components/agents/session-detail-content.test.ts
  • apps/mobile/src/components/agents/session-detail-content.tsx
  • apps/mobile/src/components/agents/session-detail-queue.test.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts

Resolved Since Last Review

  • packages/cloud-agent-sdk/src/session-manager.ts — the refresh flag is now cleared on the replayed root session.created when no fetchSnapshotPage is configured (line 1960). replayPage emits session.created on every snapshot path (cloud-agent-transport.ts:114, cli-historical-transport.ts:43, cli-live-transport.ts:761), so the legacy fetchSnapshot/fetchSnapshotPage-less opens can no longer leave a stuck "Loading…" row. A unit test covers the path.

Previous review (commit 77768b4)

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/cloud-agent-sdk/src/session-manager.ts 1809 The new isRefreshingCachedTranscript flag can remain true indefinitely when readCachedSnapshotPage is configured without fetchSnapshotPage: the legacy fetchSnapshot path emits neither onInitialPageLoaded nor onReplayComplete, so nothing clears it. Not reachable by current in-repo callers (mobile supplies both; web/extension supply neither), but the flag is a new public SDK contract.
Files Reviewed (7 files)
  • apps/mobile/src/components/agents/session-connection-indicator.mounted.test.tsx
  • apps/mobile/src/components/agents/session-connection-indicator.tsx
  • apps/mobile/src/components/agents/session-detail-content.test.ts
  • apps/mobile/src/components/agents/session-detail-content.tsx
  • apps/mobile/src/components/agents/session-detail-queue.test.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts - 1 issue

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic marked this pull request as draft September 16, 2026 13:11
@iscekic
iscekic force-pushed the kwf/owner-mobile-stale-transcript-loading-7b2d branch from af00d47 to d0ab8ed Compare September 16, 2026 13:48
@iscekic
iscekic marked this pull request as ready for review September 16, 2026 13:57
@iscekic
iscekic marked this pull request as draft September 16, 2026 18:58
…eshes

Surface: the mobile app (apps/mobile).

While a stale session transcript is shown and the new session transcript is being fetched, show a loading indicator. Best in class UX.
@iscekic
iscekic force-pushed the kwf/owner-mobile-stale-transcript-loading-7b2d branch from d0ab8ed to 354fec1 Compare September 16, 2026 21:11
@iscekic iscekic changed the title feat(mobile): show a loading row while a stale transcript is refreshed feat(mobile): show a loading indicator while a cached transcript refreshes Sep 16, 2026
@iscekic
iscekic marked this pull request as ready for review September 16, 2026 21:24
@iscekic

iscekic commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

This description names a scenario the proof did not capture:

  • not proved live: Open a session in the app to warm its persisted transcript (then go back), enable Airplane Mode, and reopen that session: the cached messages paint immediately with a spinner + "Loading…" row under the header, and the transcript still scrolls/reads normally. (no capture cited it)

A repeated proof run rebuilds the same evidence, so no proof run is dispatched for a named gap. Merging with this gap open is your decision.

@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 17, 2026
@iscekic iscekic self-assigned this Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants