Repository navigation
perf(android): long messages without freezing, and crash hardening - #84
Merged
Merged
Conversation
A message of several hundred kilobytes froze the app or brought up an ANR: it was one item of the transcript list, so its whole markdown was parsed synchronously in composition on the main thread, and laid out and measured whole, with every streaming append doing it all again. A long agent or user message is now cut into blocks of about 4,000 characters at blank lines outside code fences (a fence too long for one block is closed and reopened, a long table repeats its header, an over-long line is cut at a space). The cut happens as the entry is decoded, off the main thread, and each block is a list item of its own. Only the blocks on screen are composed, parsed and laid out, and a streaming message re-parses only its last block. A plan's outline and the user's bubble are drawn across the blocks as one frame, bridging the list's spacing. Co-Authored-By: Claude Code <noreply@anthropic.com>
Screens hand intents to the core from fire-and-forget coroutines, and the core's dispatch throws for a value it does not know (and a fault in the core surfaces as an exception too), which crashed the app. Dispatch now logs the intent's kind and the error, never its fields (they may hold secrets), and goes on; cancellation still propagates. Co-Authored-By: Claude Code <noreply@anthropic.com>
No coroutine scope in the app has an exception handler, so a throw in any of these paths took the process down. Each now logs (the exception's class and a short message, never payloads) and carries on: - a transcript row the decoders refuse ends that session's updates instead of crashing; - a failed read of one of the core's views keeps the refresher alive; - the connection service survives a core that fails to open or start (it stops itself, as it does with no login), a refused setOnline, and a failed keep-alive probe; - the Settings hub shows no npub rather than crashing if it cannot be derived; - a malformed upload URL and a signer answer that is not JSON become the failures their callers already handle; - the slash-command highlight no longer reads past a name whose lowercase form is longer than the name. Co-Authored-By: Claude Code <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A huge markdown message froze the app or caused an ANR. The whole message was one item of the transcript list, so its markdown was parsed synchronously in composition on the main thread, and then laid out and measured in one go, again on every streaming append.
markdownBlocks).Crash hardening (from a scan of the app; no coroutine scope had an exception handler):
CoreHost.dispatchlogs a refused intent (kind only, never fields) instead of crashing.setOnline, a failed keep-alive, a non-derivable npub, a malformed upload URL, a non-JSON signer answer, and an out-of-bounds slash-command highlight are all contained.Tool results were already capped at 4,000 characters by the agent host, so tool sheets stay bounded.
Test plan
MarkdownBlocksTest: break points, fences never left open, blank lines inside fences ignored, table header repeated, huge single line, 1 MB in well under a second.testDebugUnitTest verifyPaparazziDebug: no existing golden changed; a newtranscript_longsnapshot was reviewed (the plan frame and bubble are continuous across cuts).🤖 Generated with Claude Code