Skip to content

fix(mobile): correct four visual defects in session flows - #6701

Merged
iscekic merged 9 commits into
mainfrom
kwf/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a
Sep 26, 2026
Merged

iscekic merged 9 commits into
mainfrom
kwf/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a

Conversation

@iscekic

@iscekic iscekic commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Changelog for users

  • The queued-message "Cancel message" action now draws with the destructive red border and label, so it reads differently from "Copy message" and "Select text".
  • Preparation phase steps are title-cased, so "Workspace setup" matches "Preparing environment" and "Preparation complete".
  • Retrying a failed message no longer draws the prompt twice: the failed row is hidden in the same tap as the retry.
  • When the retry is rejected again, the failed row and its Retry control come back and the failure is stated once.
  • The primary Start session button keeps its label while starting and shows "Starting…" beside the spinner instead of an unlabelled bar.

Changelog for maintainers

  • session-message-details (message-details-sheet.tsx): accepted; the queued-cancel action reused the neutral button tokens, so it now uses the destructive tokens.
  • session-retry (preparation-group.tsx): accepted; the server emits raw phase keys, so a humanizer title-cases each phase row to match its group title.
  • session-retry-working (session-detail-content.tsx, session-manager.ts): accepted; a retry left the original failed row rendering beside its own optimistic row.
  • session-starting (new-session-start-button.tsx): accepted; the ordinary branch rendered no child while starting, so the brand bar read as empty.
  • Only client-materialised user rows are deleted on acceptance; a server-confirmed failed row stays as history.
  • Hiding a superseded row also filters its preparation attempts, so the attempt is not re-emitted at the transcript tail.
  • A rejected re-send reverts the superseded set, restoring the failed row and its Retry control.
  • Tests cover the destructive sheet action, the start label, the phase casing, and both accepted and rejected retries.

E2E proof

[e3] ux-check: Android light mode: open the details sheet for a queued user message; the 'Cancel message' button renders with a red destructive border and red label, visibly distinct from the neutral… — android: state.sh session-open STATE HIT (state-session-open.txt); e3-sheet.txt digest shows the Message details sheet for a queued user message with content-desc="Copy message" resource-id="message-details-copy", content-desc="Select text" resource-id="message-details-select-text" and content-desc="Cancel queued message" resource-id="message-details-cancel-queued" text="Cancel message", and e3-queued-messages.txt shows the same session's messages as content-desc="Message queued"/text="Queued" while content-desc="Stop generating" (turn running), which is what makes the sheet the…

[e3] ux-check: Android light mode: open the details sheet for a queued user message; the 'Cancel message' button renders with a red destructive border and red label, visibly distinct from the neutral… — e2e-mobile-app/e3-sheet.png

[e3] ux-check: Android light mode: open the details sheet for a queued user message; the 'Cancel message' button renders with a red destructive border and red label, visibly distinct from the neutral…

[e3] ux-check: Android light mode: open the details sheet for a queued user message; the 'Cancel message' button renders with a red destructive border and red label, visibly distinct from the neutral… — e2e-mobile-app/e3-queued-messages.png

[e1] session-message-details (android emulator-5554) — Live digest in e1-sheet.log shows the transcript with the second prompt queued (lines 'queued turn, User message', 'Message queued', 'Queued' beside 'Retrying…') and the message-details sheet drawing 'Copy message', 'Select text' and 'Cancel queued message'/'Cancel message'; queued state created with the fake-LLM stub (session.sh cloud-enter --model kilo/fake-deterministic --prompt 'fake:error'), queued run rows in e1-queued-run.log; the red-vs-neutral treatment is judged by the visual reviewer from e1-sheet.png.

[e1] session-message-details (android emulator-5554) — prior/e1-sheet.png

[e2] session-retry: an expanded preparation group shows its phase step title-cased as "Workspace setup", matching the group title's convention ("Preparing environment" / "Preparation complete") in the… — e2.png

[e9] ux-check: On the session-retry screen, the preparation step row shows 'Workspace setup' (capital W) and reads consistently with 'Preparation complete' and 'Preparing environment'. — e9.png

[e2] session-retry-rejected: when the re-sent message is rejected again, the original failed row and its Retry control come back, so the failure is still stated exactly once (the optimistic hide is… — On android emulator-5554, Retry while cloudflare-session-ingest and nextjs were down restored one android.view.ViewGroup "fault2-reject-once, User message" with Failed to deliver. Retry available. and Button "Retry" plus Connection trouble. Please retry in a moment. (e2-retry-rejected.log).

[e2] session-retry-rejected: when the re-sent message is rejected again, the original failed row and its Retry control come back, so the failure is still stated exactly once (the optimistic hide is… — e2e-mobile-app/e2-retry-rejected.png

[e1] session-retry-working: after a failed message is retried, the transcript shows the prompt ONCE (one user bubble), never two identical copies in one frame; the verifier counts occurrences of the… — android emulator-5554; stub fault.sh down cloudflare-session-ingest then Retry; after retry e1-after-retry.log shows one android.view.ViewGroup "duponce-alpha-24, User message" beside TextView "Considering next steps · 1 sec" (same one ViewGroup at 17 sec), after TextView "Failed to deliver. Retry available." and Button "Retry" before the tap.

[e1] session-retry-working: after a failed message is retried, the transcript shows the prompt ONCE (one user bubble), never two identical copies in one frame; the verifier counts occurrences of the… — e2e-mobile-app/e1-after-retry.png

[e5] session-retry-working: with a user message whose delivery failed, tapping Retry removes the failed row in the same tap (its Retry control is gone) and the prompt renders once for the whole re-send… — android emulator-5554: from state session-open with a delivery-failed user row (state-session-open.txt shows 'Response failed. Retry available.' beside a 'Retry' control), one Retry tap produced e5-inflight.txt with content-desc="retry in-flight probe, User message" once and no 'Retry' control (their Retry control is gone in the same tap), and after the re-send landed e5-after-final.txt shows exactly one content-desc="retry in-flight probe, User message" and zero 'Response failed. Retry available.' — the prompt renders once for the whole re-send; the same-tap removal was observed after…

[e5] session-retry-working: with a user message whose delivery failed, tapping Retry removes the failed row in the same tap (its Retry control is gone) and the prompt renders once for the whole re-send… — prior/e5-inflight.png

[e4] session-starting: pressing the primary "Start session" button shows the busy label "Starting…" beside the spinner, so the yellow bar is never an unlabelled empty control; the visual reviewer judges… — e4.png

E2E proof — log excerpts

[e3] ux-check: Android light mode: open the details sheet for a queued user mess -> pass :: android: state.sh session-open STATE HIT (state-session-open.txt); e3-sheet.txt digest shows the Message details sheet for a queued user message with content-desc="Copy message" resource-id="message-details-copy", content-desc="Select text" resource-id="message-details-select-text" and content-desc="Cancel queued message" resource-id="message-details-cancel-queued" text="Cancel message", and e3-queued-messages.txt shows the same session's messages as content-desc="Message queued"/text="Queued" while content-desc="Stop generating" (turn running), which is what makes the sheet the queued-message sheet; the red border and red label are a visual judgement owned by the visual reviewer from the sa
[e4] ux-check: Retry a failed submission whose re-send is rejected: the original -> pass :: jev read the digest: pass (confidence 0.97)
[e5] ux-check: new-session Start button shows a spinner with the 'Starting…' lab -> pass :: Android emulator-5606: 'SCENE e5 OK' in e5-start.log shows the primary CTA 'android.widget.Button busy [37,2117][1043,2232]' carrying the child 'android.widget.TextView Starting… tappable [504,2151][647,2197]', and e5-dark.log shows the same lines in dark mode (captures e5.png/e5-light.png light, e5-dark.png dark for the visual reviewer); busy state held with a nextjs stall, recovered; no UX-DEFECT observed (the fault-induced failure state shows 'Failed to create session' with a Retry action).
[e1] session-retry-working -> pass :: android emulator-5554; stub <internal>; after retry e1-after-retry.log shows one android.view.ViewGroup "duponce-alpha-24, User message" beside TextView "Considering next steps · 1 sec" (same one ViewGroup at 17 sec), after TextView "Failed to deliver. Retry available." and Button "Retry" before the tap.
[e2] session-retry-rejected -> pass :: On android emulator-5554, Retry while cloudflare-session-ingest and nextjs were down restored one `android.view.ViewGroup "fault2-reject-once, User message"` with `Failed to deliver. Retry available.` and `Button "Retry"` plus `Connection trouble. Please retry in a moment.` (e2-retry-rejected.log).
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/e3-sheet.txt
<redacted>
<redacted>
                    </android.widget.Button>
<redacted>
<redacted>
                    </android.widget.Button>
<redacted>
<redacted>
                    </android.widget.Button>
<redacted>
<redacted>
<redacted>
<redacted>
                  </android.view.ViewGroup>
                </android.widget.ScrollView>
<redacted>
              </android.view.ViewGroup>
            </android.view.ViewGroup>
          </android.view.ViewGroup>
        </android.widget.FrameLayout>
      </android.widget.FrameLayout>
    </android.widget.LinearLayout>
  </android.widget.FrameLayout>
</hierarchy>
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/state-session-open.txt
android.widget.TextView 3:18 PM tappable [476,829][604,868]
android.view.ViewGroup say hello, User message tappable [0,887][1080,1025]
android.widget.TextView say hello tappable [847,924][1011,987]
android.widget.Button Preparation complete tappable [40,1046][1042,1147]
android.widget.TextView Preparation complete tappable [188,1073][506,1119]
android.view.ViewGroup Hello, Assistant message tappable [0,1168][1080,1260]
android.widget.TextView Hello tappable [37,1182][1045,1245]
android.view.ViewGroup retry duplicate probe, User message tappable [0,1260][1080,1398]
android.widget.TextView retry duplicate probe tappable [627,1297][1011,1360]
android.view.ViewGroup No prior task exists to retry — this session only contains the greeting so far. Let me know what you'd like me to do., Assistant message tappable [0,1398][1080,1713]
android.widget.Button Thought tappable [40,1410][1042,1484]
android.widget.TextView THOUGHT tappable [68,1428][206,1465]
android.widget.TextView No prior task exists to retry — this session only contains the greeting so far. Let me know what you'd like me to do. tappable [37,1509][1045,1698]
android.view.ViewGroup duponce-alpha-24, User message tappable [0,1713][1080,1851]
android.widget.TextView duponce-alpha-24 tappable [674,1750][1011,1813]
android.view.ViewGroup retry rejected probe, User message tappable [0,1851][1080,2035]
android.widget.TextView retry rejected probe tappable [649,1888][1011,1951]
android.widget.TextView Message queued tappable [887,1989][1034,2026]
android.widget.TextView Queued tappable [927,1989][1024,2026]
android.widget.TextView Considering next steps · 35 sec tappable [107,2086][569,2132]
android.widget.Button Add attachment tappable [28,2214][101,2288]
android.widget.EditText Message tappable [126,2193][800,2309]
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/e3-queued-messages.txt
                                          </com.horcrux.svg.SvgView>
                                        </android.widget.Button>
                                      </android.view.ViewGroup>
                                    </android.view.ViewGroup>
                                  </android.view.ViewGroup>
<redacted>
                                </android.view.ViewGroup>
                              </android.view.ViewGroup>
                            </android.widget.ScrollView>
                          </android.view.ViewGroup>
                        </android.view.ViewGroup>
                      </android.view.ViewGroup>
<redacted>
                    </android.view.ViewGroup>
                  </android.view.ViewGroup>
                </android.view.ViewGroup>
              </android.widget.FrameLayout>
            </android.widget.FrameLayout>
          </android.widget.FrameLayout>
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.widget.LinearLayout>
  </android.widget.FrameLayout>
</hierarchy>
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/scripted-e4.log
SCENE e4 OK
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,195][101,296]
android.widget.Button Rename session: Session tappable [111,149][883,342]
android.view.View Session tappable [111,212][883,277]
android.widget.Button Tap to view context details. tappable [910,188][1044,303]
android.widget.TextView 2:41 PM tappable [476,1315][604,1354]
android.view.ViewGroup starting label probe, User message tappable [0,1373][1080,1557]
android.widget.TextView starting label probe tappable [649,1410][1011,1473]
android.widget.TextView Message queued tappable [887,1511][1034,1548]
android.widget.TextView Queued tappable [927,1511][1024,1548]
android.widget.Button Preparing environment tappable [40,1578][1042,1686]
android.widget.TextView Preparing environment tappable [198,1608][535,1654]
android.widget.Button Sandbox boot tappable [71,1709][1011,1807]
android.widget.TextView Sandbox boot tappable [215,1735][995,1781]
android.widget.TextView Starting environment… tappable [98,1828][984,1874]
android.widget.Button Workspace setup tappable [71,1916][1011,2014]
android.widget.TextView Workspace setup tappable [215,1942][995,1988]
android.widget.TextView Setting up workspace… tappable [98,2035][984,2081]
android.widget.Button Add attachment [28,2214][101,2288]
android.widget.EditText Setting up environment... tappable [126,2191][800,2311]
android.widget.Button Start voice input [835,2205][926,2297]
android.widget.Button Send message [926,2188][1052,2314]
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/e5-start.log
android.widget.Button Run on: Cloud Agent [37,921][909,1037]
android.widget.TextView Cloud Agent tappable [67,951][844,1007]
android.widget.Button Refresh [928,921][1043,1037]
android.widget.TextView To run on your computer, start Kilo there and leave it running. tappable [36,1056][1044,1093]
android.widget.TextView Repository tappable [37,1139][1045,1185]
android.widget.Button Repository: GitHub · iscekic/panon-deking [37,1203][1043,1319]
android.widget.TextView GitHub · iscekic/panon-deking tappable [67,1233][978,1289]
android.widget.TextView Branch tappable [37,1347][1045,1393]
android.widget.Button Branch: main [37,1411][1043,1522]
android.widget.TextView main tappable [67,1438][850,1494]
android.widget.TextView Default tappable [867,1447][958,1484]
android.widget.Button Connect GitLab tappable [37,1550][909,1666]
android.widget.TextView Connect GitLab tappable [136,1584][807,1630]
android.widget.Button Refresh repositories tappable [928,1550][1043,1666]
android.widget.TextView Changes tappable [37,1711][1045,1757]
android.view.View Changes tappable [37,1775][1043,1894]
android.widget.RadioButton Leave changes tappable [46,1784][540,1885]
android.widget.TextView Leave changes tappable [192,1811][393,1857]
android.widget.RadioButton Commit and push tappable [540,1784][1034,1885]
android.widget.TextView Commit and push tappable [668,1811][905,1857]
android.widget.TextView Environment tappable [37,1941][1045,1987]
android.widget.TextView busy tappable [37,2005][1045,2034]
android.widget.Button busy [37,2117][1043,2232]
android.widget.TextView Starting… tappable [504,2151][647,2197]
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/e5-dark.log
android.widget.Button Run on: Cloud Agent [37,921][909,1037]
android.widget.TextView Cloud Agent tappable [67,951][844,1007]
android.widget.Button Refresh [928,921][1043,1037]
android.widget.TextView To run on your computer, start Kilo there and leave it running. tappable [36,1056][1044,1093]
android.widget.TextView Repository tappable [37,1139][1045,1185]
android.widget.Button Repository: GitHub · iscekic/panon-deking [37,1203][1043,1319]
android.widget.TextView GitHub · iscekic/panon-deking tappable [67,1233][978,1289]
android.widget.TextView Branch tappable [37,1347][1045,1393]
android.widget.Button Branch: main [37,1411][1043,1522]
android.widget.TextView main tappable [67,1438][850,1494]
android.widget.TextView Default tappable [867,1447][958,1484]
android.widget.Button Connect GitLab tappable [37,1550][909,1666]
android.widget.TextView Connect GitLab tappable [136,1584][807,1630]
android.widget.Button Refresh repositories tappable [928,1550][1043,1666]
android.widget.TextView Changes tappable [37,1711][1045,1757]
android.view.View Changes tappable [37,1775][1043,1894]
android.widget.RadioButton Leave changes tappable [46,1784][540,1885]
android.widget.TextView Leave changes tappable [192,1811][393,1857]
android.widget.RadioButton Commit and push tappable [540,1784][1034,1885]
android.widget.TextView Commit and push tappable [668,1811][905,1857]
android.widget.TextView Environment tappable [37,1941][1045,1987]
android.widget.TextView Default environment tappable [37,2005][1045,2034]
android.widget.Button busy [37,2117][1043,2232]
android.widget.TextView Starting… tappable [504,2151][647,2197]
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/e1-after-retry.log
android.view.ViewGroup "Hello, Assistant message"
TextView "Hello"
android.view.ViewGroup "retry duplicate probe, User message"
TextView "retry duplicate probe"
android.view.ViewGroup "No prior task exists to retry — this session only contains the greeting so far. Let me know what you'd like me to do., Assistant message"
Button "Thought"
TextView "THOUGHT"
TextView "No prior task exists to retry — this session only contains the greeting so far. Let me know what you'd like me to do."
android.view.ViewGroup "duponce-alpha-24, User message"
TextView "duponce-alpha-24"
TextView "Considering next steps · 1 sec"
Button "Add attachment"
EditText "Message"
Button "Start voice input"
Button "Stop generating"
## AFTER retry still in-flight (Considering next steps · 17 sec)
android.view.ViewGroup "duponce-alpha-24, User message"
TextView "duponce-alpha-24"
TextView "Considering next steps · 17 sec"
Button "Stop generating"
## counts of prompt text "duponce-alpha-24" on User message ViewGroup
before retry: 1
after retry 1s: 1
after retry 17s: 1
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-4-visual-defects-on-android-the-destructive-canc-55bd5-0f8a/e2e-mobile-app/e2-retry-rejected.log
- TextView "Failed to deliver. Retry available." [ref=e28]
- TextView "You stopped this message." [ref=e29]
- Button "Retry" [ref=e30]
- TextView "Retry" [ref=e31]
- Button "Copy to composer" [ref=e32]
- TextView "Copy to composer" [ref=e33]
- android.view.ViewGroup "fault2-reject-once, User message" [ref=e34]
- TextView "fault2-reject-once" [ref=e35]
- TextView "Failed to deliver. Retry available." [ref=e36]
- TextView "You stopped this message." [ref=e37]
- Button "Retry" [ref=e38]
- TextView "Retry" [ref=e39]
- Button "Copy to composer" [ref=e40]
- TextView "Copy to composer" [ref=e41]
- TextView "Connection trouble. Please retry in a moment." [ref=e42]
- Button "Add attachment" [ref=e43]
- EditText "Message" [ref=e44]
- Button "Start voice input" [ref=e45]
- Button "Send message" [disabled] [ref=e46]
COUNT fault2-reject-once user-message groups: 1
COUNT TextView fault2-reject-once: 1
original failed row restored: Failed to deliver. Retry available.
Retry control restored: Button "Retry" [ref=e38] after fault2-reject-once
rejection toast: Connection trouble. Please retry in a moment.
Owner request

Surface: mobile-app

Explorer finding: 4 visual defects on android: The destructive "Cancel message" action ; The step row 'workspace setup' is lowerc; one copy clipped at the top of the viewp (+1 more)

The user-agent explorer found these 4 while using the app like a user. They are one kind of defect on one platform, so they are ONE item: the pull request must fix and prove every one of them.
One must-run scenario per finding: the plan needs 4 musts, one for each finding below.
The explorer never edits product code.

Evidence (from the device runs):

--- Finding 1 of 4: session-message-details: The destructive "Cancel message" action is drawn as an identical full-width outlined button to the non-destructive "Copy message" and "Select text" actions, so it carries no destructive affordance.
Flow: session-message-details
Found on revision: c09a170
Repro:

  1. set this state first: seed app:kilo-pass-orgs; relaunch; account dev-seed-kilo-pass-orgs-owner@example.com; uimode day; battery 33 discharging; credits 20; the device in light mode
  2. open the app on emulator-5602
  3. reach session-message-details
  4. the capture shows the defect named below
    Observed: The destructive "Cancel message" action is drawn as an identical full-width outlined button to the non-destructive "Copy message" and "Select text" actions, so it carries no destructive affordance.
    Expected: the screen renders without this defect

--- Finding 2 of 4: session-retry: The step row 'workspace setup' is lowercase while its siblings 'Preparation complete' and 'Preparing environment' are title-cased, breaking one labelling convention in the same stack.
Flow: session-retry
Found on revision: c09a170
Repro:

  1. set this state first: reviews 6; credits 12; uimode day; battery 88 discharging; relaunch; deeplink kiloapp://expo-development-client/?url=http%3A%2F%2Flocalhost%3A20081; the device in light mode
  2. open the app on emulator-5602
  3. reach session-retry
  4. the capture shows the defect named below
    Observed: The step row 'workspace setup' is lowercase while its siblings 'Preparation complete' and 'Preparing environment' are title-cased, breaking one labelling convention in the same stack.
    Expected: the screen renders without this defect

--- Finding 3 of 4: session-retry-working: The transcript shows the identical user message twice in one frame: one copy clipped at the top of the viewport under the Session header, and a second full copy below the failure/preparation block.
Flow: session-retry-working
Found on revision: c09a170
Repro:

  1. set this state first: seed app:kilo-pass-orgs; relaunch; account dev-seed-kilo-pass-orgs-owner@example.com; uimode day; battery 33 discharging; credits 20; the device in light mode
  2. open the app on emulator-5602
  3. reach session-retry-working
  4. the capture shows the defect named below
    Observed: The transcript shows the identical user message twice in one frame: one copy clipped at the top of the viewport under the Session header, and a second full copy below the failure/preparation block.
    Expected: the screen renders without this defect

--- Finding 4 of 4: session-starting: The primary yellow call-to-action renders as an empty bar with only a tiny centered dot, so no label or spinner text identifies what the action does or what it is waiting for.
Flow: session-starting
Found on revision: c09a170
Repro:

  1. set this state first: reviews 6; credits 12; uimode day; battery 88 discharging; relaunch; deeplink kiloapp://expo-development-client/?url=http%3A%2F%2Flocalhost%3A20081; the device in light mode
  2. open the app on emulator-5602
  3. reach session-starting
  4. the capture shows the defect named below
    Observed: The primary yellow call-to-action renders as an empty bar with only a tiny centered dot, so no label or spinner text identifies what the action does or what it is waiting for.
    Expected: the screen renders without this defect

[e10] ux-check retry duplicate — One retry of a failed submission (fake-deterministic, fake:error-terminal) leaves exactly one 'fake:error-terminal' user row in flight (e10-fresh-inflight.txt line 9), after landing (e10-fresh-landed.txt line 9) and after reopening (e10-fresh-reloaded.txt line 9); screenshot e10-session.png.

[e10] ux-check retry duplicate — e10-session.png

[e9] ux-check: On the session-retry screen, the preparation step row shows 'Workspace setup' (capital W) and reads consistently with 'Preparation complete' and 'Preparing environment'. — e9-scene.log line 1 'SCENE e9 OK' with digest lines 13-14 'Preparation complete' and 17-18 'Workspace setup' (complete state); e9-prep.xml running state carries text="Preparing environment", text="Workspace setup" and text="Setting up workspace…"; screenshots e9.png / e9-prep.png. The phase row is title-cased in both states and matches the group title convention; no UX defect observed.

[e9] ux-check: On the session-retry screen, the preparation step row shows 'Workspace setup' (capital W) and reads consistently with 'Preparation complete' and 'Preparing environment'. — e9-prep.png

[e8] ux-check Android dark mode (android emulator-5554) — App appearance set to Dark (Preferences > Appearance > Dark [checked]); e8-sheet-dark.log shows the queued-message transcript ('queued turn, User message', 'Message queued', 'Queued') and the same message-details sheet drawing 'Copy message', 'Select text' and 'Cancel message' together; no missing feedback, lost work or broken recovery, and legibility/distinctness of the destructive affordance is judged by the visual reviewer from e8-sheet-dark.png (state created with the fake-LLM gate stub, prompt 'fake:gate:q1').

[e8] ux-check Android dark mode (android emulator-5554) — e8-sheet-dark.png

Follow-ups (not changed here)

  • not proved live: session-message-details: with a queued user message, the message-details sheet draws "Cancel message" with a red border and red label (a destructive affordance) while "Copy message" and "Select text" stay neutral outlined buttons; the visual reviewer judges the red treatment in the screenshot. needs:seed: a cloud session whose second prompt is queued behind a running first turn (send two prompts without waiting). (no capture cited it)
  • not proved live: ux-check: Android dark mode: the same destructive cancel affordance stays legible and distinct from the neutral actions. (no capture cited it)
  • not proved live: ux-check: On a session with a failed submission, tap Retry: while the re-send is in flight and after it lands, the prompt appears exactly once in the transcript, with no clipped duplicate under the Session header. (no capture cited it)
  • not proved live: ux-check: On the new-session form, tap Start session: the primary yellow button shows a spinner together with the 'Starting…' label (never an empty bar), and the label stays readable in light and dark mode. (no capture cited it)

Open findings (not fixed here)

  • not proved live: session-message-details.png is no longer on the host that took it, so no publish can carry it
  • not proved live: session-retry-working.png is no longer on the host that took it, so no publish can carry it
  • not proved live: session-retry.png is no longer on the host that took it, so no publish can carry it
  • not proved live: session-starting.png is no longer on the host that took it, so no publish can carry it

e5-dark

e5-light

Surface: mobile-app

Explorer finding: 4 visual defects on android: The destructive "Cancel message" action ; The step row 'workspace setup' is lowerc; one copy clipped at the top of the viewp (+1 more)

The user-agent explorer found these 4 while using the app like a user. They are one kind of defect on one platform, so they are ONE item: the pull request must fix and prove every one of them.
One must-run scenario per finding: the plan needs 4 musts, one for each finding below.
The explorer never edits product code.

Evidence (from the device runs):

--- Finding 1 of 4: session-message-details: The destructive "Cancel message" action is drawn as an identical full-width outlined button to the non-destructive "Copy message" and "Select text" actions, so it carries no destructive affordance.
Flow: session-message-details
Found on revision: c09a170
Repro:
1. set this state first: seed app:kilo-pass-orgs; relaunch; account dev-seed-kilo-pass-orgs-owner@example.com; uimode day; battery 33 discharging; credits 20; the device in light mode
2. open the app on emulator-5602
3. reach session-message-details
4. the capture shows the defect named below
Observed: The destructive "Cancel message" action is drawn as an identical full-width outlined button to the non-destructive "Copy message" and "Select text" actions, so it carries no destructive affordance.
Expected: the screen renders without this defect
- ~/.local/share/kwf/findings/explorer-4-visual-defects-on-android-the-destructive
Comment thread apps/mobile/src/components/agents/session-detail-content.tsx Outdated
Comment thread apps/mobile/src/components/agents/session-detail-content.tsx Outdated
@kilo-code-bot

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

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the single commit added since the previous review (ac8627308) found no new issues: the in-flight superseded-row record now lives in the manager keyed by the owning session, so switching away and back before a retry settles keeps the row hidden, and the mark is dropped on rejection or replaced by the durable resolved record on acceptance. The screen-local set and its owner ref were removed cleanly, and the new atom mirrors the existing resolved-delivery projection pattern.

Files Reviewed (5 files)
  • 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 (3 snapshots, latest commit 6a786df)

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

Previous review (commit 6a786df)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the two commits added since the previous review (1d57abd7, 6a786dfc) found no issues: the resolved-delivery projection now re-derives on a session switch, a recorded retry, and the durable seed, and the revision-keyed memo gives the value a new identity exactly when its content changes, so the transcript filter is notified without unbounded allocation. The earlier fix on session-detail-content.tsx (unchanged in this range) still holds.

Files Reviewed (2 files)
  • packages/cloud-agent-sdk/src/session-manager.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts

Previous review (commit 0188bcf)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the only commit added since the previous review (0188bcf1) found no issues: it renames shadowed test helpers in the merged preparation-group suite without changing behavior, and the earlier resolved-delivery-failure fix (0f451c7) remains correct at current HEAD (0188bcf1). Both prior findings stay resolved and their inline threads are outdated (line: null), so no new findings are raised.

Files Reviewed (5 files)
  • apps/mobile/src/components/agents/preparation-group.mounted.test.tsx
  • 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.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
Resolved since the previous review
  • WARNING session-detail-content.tsx: superseded-row suppression now survives a session switch-back and a relaunch via the manager's durable resolvedDeliveryFailures projection consumed by the transcript filter. Fixed in 0f451c7.
  • SUGGESTION session-detail-content.tsx: in-flight locallySupersededMessageIds writes are gated on supersededOwnerSessionIdRef.current === ownerSessionId. Fixed in 0f451c7.

Previous review (commit 46d6b2c)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Follow-up commit 0f451c7 resolves both prior findings: the manager now projects its durable resolved-delivery record on the read-only atoms.resolvedDeliveryFailures (seeded on session open, bumped on each resolution), and the in-flight local writes are gated to the owning session, so a superseded row stays hidden after a switch-back and a relaunch. No new issues were found in the changed code.

Files Reviewed (4 files changed since the previous review)
  • apps/mobile/src/components/agents/session-detail-content.tsx - 0 issues
  • apps/mobile/src/components/agents/session-detail-queue.test.ts - 0 issues
  • packages/cloud-agent-sdk/src/session-manager.ts - 0 issues
  • packages/cloud-agent-sdk/src/session-manager.test.ts - 0 issues
Resolved since the previous review
  • WARNING session-detail-content.tsx: superseded-row suppression now survives a session switch and a relaunch via the manager's durable resolvedDeliveryFailures projection consumed by the transcript filter. Fixed in 0f451c7.
  • SUGGESTION session-detail-content.tsx: the locallySupersededMessageIds writes are now gated on supersededOwnerSessionIdRef.current === ownerSessionId, matching clearFailedMessage's owning-session contract. Fixed in 0f451c7.

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

Review guidance: REVIEW.md from base branch main

…-defects-on-android-the-destructive-canc-55bd5-0f8a

# Conflicts:
#	apps/mobile/src/components/agents/preparation-group.mounted.test.tsx
A superseded failed row was hidden by screen state alone, so switching away
and back — or relaunching — restored a server-confirmed duplicate next to
the retry's own row. Project the manager's resolved-delivery record (seeded
from the durable reader on open) on a new `resolvedDeliveryFailures` atom and
union it with the in-flight local set, so the hide survives both.

Scope the in-flight local set to the session that owns the retried row: the
re-send is awaited, so a mid-flight switch must not write the previous
session's id into the switched-to session's state.
… switch

The read-only projection keyed off the storage atom, which a cached re-open
preserves and `destroy` nulls before the active session id clears, so a
switch could keep the previous session's superseded ids. Bump an explicit
revision on the switch, on destroy, on a recorded retry, and on the durable
seed instead, and add an SDK test for the per-session projection.
A derived value that keeps its reference is invisible to jotai subscribers
(`Object.is` on the read result), and the resolved-failure record is mutated
in place, so an accepted retry left readers on the pre-retry set. Derive a
fresh Set per revision behind a revision-keyed memo, and bump only when the
active session's record changed. Added identity tests.
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 25, 2026
Comment thread apps/mobile/src/components/agents/session-detail-content.tsx Outdated
…-defects-on-android-the-destructive-canc-55bd5-0f8a
The in-flight supersede record lived in the session screen, so a session
change dropped it: the row a retry superseded rendered again beside the
retry's own row when the user switched away and back before the re-send
settled.

Move the record into the manager, keyed by the session that owns the row,
as the resolved-delivery record already is. Add `markMessageSuperseded`
and `unmarkMessageSuperseded`, project the active session's marks on
`supersededInFlightMessageIds`, and drop a mark when the resolution is
accepted or the re-send is rejected.
@iscekic
iscekic marked this pull request as draft September 25, 2026 19:09
@iscekic
iscekic marked this pull request as ready for review September 25, 2026 19:09
…-defects-on-android-the-destructive-canc-55bd5-0f8a
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