Skip to content

fix(mobile): keep the agent transcript from crashing on a files-less patch part - #6237

Merged
iscekic merged 1 commit into
mainfrom
kwf/report-mobile-feedback-screenshot-28186-1fb8
Sep 18, 2026
Merged

iscekic merged 1 commit into
mainfrom
kwf/report-mobile-feedback-screenshot-28186-1fb8

Conversation

@iscekic

@iscekic iscekic commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • The agent chat screen no longer falls back to the "Something went wrong" error screen when a patch part arrives without file paths.
  • A patch part with no file paths now draws nothing instead of taking down the conversation.
  • The rest of the conversation, including later answers, still renders after such a part.
  • Patch rows that do carry paths keep showing "Updated N files" with the changed paths listed.

Changelog for maintainers

  • normalizeMissingPatchFiles fills files: [] for patch parts on message.part.updated in the chat processor, the single funnel every part write goes through.
  • The normalizer is identity-preserving when files is already an array, so memoized stored messages keep their object reference.
  • The mobile transcript reader now reads files through a declared optional and treats a missing array as empty, covering parts stored by older builds.
  • Root cause: the per-event schemas are .passthrough(), so the wire can omit files on a patch part even though the generated Part type declares the array; the stored part then threw on part.files.length (Sentry KILO-APP-BZ).
  • Review first the normalization order in the chat processor (text, then files) and the renderer's partRendersContent guard.
  • No type, storage, or config contract changed; the generated Part type still declares files unconditionally.

E2E proof

[e1] Force-stop the app and reopen that same session (warm start from the cached first transcript page): the transcript paints without the error boundary. — Android emulator-5554, packed tree: e1-force-stop.log shows the process died and a new one started ("pidof after force-stop: []", "pidof after relaunch: 14331"), e1-enter.log shows the same session reopened after that restart ("session.sh: link delivered on emulator-5554"), and the edited parked step list reported "SCENE e1 OK" with the transcript painted in the digest ("android.widget.Button Rename session: probe.ts console.log(1) tappable [111,149][883,278]", "android.widget.Button Preparation complete tappable [40,1913][1042,2014]", "android.widget.Button Add attachment tappable…

[e1] Force-stop the app and reopen that same session (warm start from the cached first transcript page): the transcript paints without the error boundary. — e2e-mobile-app/e1.png

[e2] Open or run a session where the agent edits files: the patch row still reads 'Updated N files' with the changed paths under it. — android emulator-5604: after repairing the device (corrupt app data dir, adb pm clear + login.sh) the scene ran SCENE e2 OK (e2-scene.log) and the live digest e2-open.log shows the patch row 'android.widget.TextView Updated 1 file' with the changed path underneath, 'android.widget.TextView probe.ts', both inside the group 'Updated 1 file, probe.ts, LIVE STREAM MARKER A, Answer visible after broken patch, Assistant message'; capture e2-patch-row.png is the same screen for the visual reviewer; no functional defect observed (visual audit belongs to the visual reviewer).

[e2] Open or run a session where the agent edits files: the patch row still reads 'Updated N files' with the changed paths under it. — prior/e2-patch-row.png

E2E proof — log excerpts

[e1] Force-stop the app and reopen that same session (warm start from the cached -> pass :: Android emulator-5554, packed tree: e1-force-stop.log shows the process died and a new one started ("pidof after force-stop: []", "pidof after relaunch: 14331"), e1-enter.log shows the same session reopened after that restart ("session.sh: link delivered on emulator-5554"), and the edited parked step list reported "SCENE e1 OK" with the transcript painted in the digest ("android.widget.Button Rename session: probe.ts console.log(1) tappable [111,149][883,278]", "android.widget.Button Preparation complete tappable [40,1913][1042,2014]", "android.widget.Button Add attachment tappable [28,2213][101,2287]") and no error-boundary line anywhere in the digest (grep for wrong/try again/could not be
/home/igor_kilocode_ai/.local/share/kwf/sections/report-mobile-feedback-screenshot-28186-1fb8/e2e-mobile-app/e1-scene.log
android.widget.Button Preparation complete tappable [40,1913][1042,2014]
android.widget.TextView Preparation complete tappable [188,1940][506,1986]
android.widget.TextView Considering next steps · 48 sec tappable [107,2086][569,2132]
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 Stop generating tappable [926,2187][1052,2313]
SCENE e1 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,163][101,264]
android.widget.Button Rename session: probe.ts console.log(1) tappable [111,149][883,278]
android.view.View probe.ts console.log(1) tappable [111,180][883,245]
android.widget.Button Tap to view context details. tappable [910,156][1044,271]
android.widget.TextView 7:44 PM tappable [476,1633][604,1672]
android.view.ViewGroup fake:read-then-write:kilowarm:probe.ts:console.log(1), User message tappable [0,1691][1080,1892]
android.widget.TextView fake:read-then-write:kilowarm:probe.ts:console.log(1) tappable [250,1728][1011,1854]
android.widget.Button Preparation complete tappable [40,1913][1042,2014]
android.widget.TextView Preparation complete tappable [188,1940][506,1986]
android.widget.TextView Considering next steps · 48 sec tappable [107,2086][569,2132]
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 Stop generating tappable [926,2187][1052,2313]
/home/igor_kilocode_ai/.local/share/kwf/sections/report-mobile-feedback-screenshot-28186-1fb8/e2e-mobile-app/e1-force-stop.log
scenario e1 warm start, android emulator-5554, packed tree
run start (UTC): 19:46:18Z
pidof before force-stop: 13354
pidof after force-stop: []
## Network stats: elapsed time=63ms (0ms mobile, 0ms wifi, 63ms not connected)
pidof after relaunch: 14331
/home/igor_kilocode_ai/.local/share/kwf/sections/report-mobile-feedback-screenshot-28186-1fb8/e2e-mobile-app/e1-enter.log
session.sh: link delivered on emulator-5554
ses_f5440ba08ffctulpLET2tqi8sF
Owner request

Surface: the mobile app (apps/mobile).

(user report, Pylon issue 28186, 2026-09-14)

[no prose: the report is one screenshot]
ise kese fix karu        (Hindi: "how do I fix this")
---
User ID: 01519d37-e306-40f0-b2b8-7e53dc9720ea
App version: 1.0.10 (214)
OS: android 36

The whole report is a screenshot the user attached, and the words ask how to fix what it shows. Read the screenshot first - it is on the Pylon issue, and python3 v2/bin/pylon.py get 28186 prints the issue with the attachment url in its body.

Then find what production recorded for that user id at that time, name the defect the screenshot shows, and fix it. If the screenshot shows no product defect, say so with the evidence and change nothing.

Follow-ups (not changed here)

@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

Incremental re-review at a5c2b9470: the seven changed files are byte-identical to the previously reviewed commit d46cc5882 (the branch was rebased onto a newer main, so only the base moved), so the earlier no-issue conclusion stands. Writer-side normalizeMissingPatchFiles fills files: [] at the single chat-processor seam, and the reader-side optional read in partRendersContent drops an unfixed files-less patch part instead of throwing, while PartRenderer gates on that same predicate before its part.files dereference.

Files Reviewed (7 files)
  • packages/cloud-agent-sdk/src/part-utils.ts
  • packages/cloud-agent-sdk/src/part-utils.test.ts
  • packages/cloud-agent-sdk/src/chat-processor.ts
  • packages/cloud-agent-sdk/src/chat-processor.test.ts
  • apps/mobile/src/components/agents/message-visibility.ts
  • apps/mobile/src/components/agents/message-visibility.test.ts
  • apps/mobile/src/app/(app)/agent-chat/[session-id].mounted.test.tsx
Previous Review Summary (commit d46cc58)

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

Previous review (commit d46cc58)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The patch-part fix is sound: writer-side normalization fills files: [] at the single chat-processor seam (covers live and replayed/history pages), and the reader-side optional read makes partRendersContent treat absent files as empty, so the transcript drops the part instead of throwing; the unguarded part.files dereferences in part-renderer.tsx remain unreachable because PartRenderer gates on the same predicate.

Files Reviewed (7 files)
  • packages/cloud-agent-sdk/src/part-utils.ts
  • packages/cloud-agent-sdk/src/part-utils.test.ts
  • packages/cloud-agent-sdk/src/chat-processor.ts
  • packages/cloud-agent-sdk/src/chat-processor.test.ts
  • apps/mobile/src/components/agents/message-visibility.ts
  • apps/mobile/src/components/agents/message-visibility.test.ts
  • apps/mobile/src/app/(app)/agent-chat/[session-id].mounted.test.tsx

Verification notes

  • normalizeMissingPatchFiles is identity-preserving for non-patch parts and for patch parts that already carry an array, and uses Array.isArray so null/non-array values are also repaired. Ordering before the empty-text guard does not change behaviour: the guard keys on hasTextField, which is false for patch parts (pre-existing overwrite semantics on upsertPart are unchanged).
  • The snapshot/history path (session-manager.ts applyPage/replay) runs parts through createChatProcessor, so normalization applies to replayed transcripts as well; the reader-side read covers stored parts from older builds that bypass it.
  • No markdown/image or kilocode-landing rules apply to this PR, and the change adds no subscriptions, timers, or retained references, so no memory-leak surface is introduced.

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 18:31
…patch part

Surface: the mobile app (apps/mobile).

(user report, Pylon issue 28186, 2026-09-14)
```
[no prose: the report is one screenshot]
ise kese fix karu        (Hindi: "how do I fix this")
---
User ID: 01519d37-e306-40f0-b2b8-7e53dc9720ea
App version: 1.0.10 (214)
OS: android 36
```

The whole report is a screenshot the user attached, and the words ask how to fix what it shows. Read the screenshot first - it is on the Pylon issue, and `python3 v2/bin/pylon.py get 28186` prints the issue with the attachment url in its body.

Then find what production recorded for that user id at that time, name the defect the screenshot shows, and fix it. If the screenshot shows no product defect, say so with the evidence and change nothing.
@iscekic
iscekic force-pushed the kwf/report-mobile-feedback-screenshot-28186-1fb8 branch from d46cc58 to a5c2b94 Compare September 16, 2026 19:53
@iscekic iscekic changed the title fix(mobile): handle a patch part without a file list in chat transcripts fix(mobile): keep the agent transcript from crashing on a files-less patch part Sep 16, 2026
@iscekic
iscekic marked this pull request as ready for review September 16, 2026 20:08
@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 the Kilo Android app and enter a cloud-agent session whose transcript holds a patch part with no file list (the state the reporter screenshotted): the session screen paints its header, transcript and tab bar, and the full-screen 'Something went wrong / Please try again.' surface never appears, including after pressing Retry. (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