Fix content flash when reopening a chat with new messages#53
Fix content flash when reopening a chat with new messages#53mcintyre94 merged 2 commits intomainfrom
Conversation
When reopening a chat where Claude had been running, a fresh ChatViewModel had empty processedEventUUIDs, causing reconnectToServiceLogs to clear the last assistant message's content before replaying logs. This produced a jarring flash where only the user message was visible before the assistant response slowly refilled. Fix: persist processedEventUUIDs alongside messages in SpriteChat. On loadSession, restore the UUID set so reconnect correctly identifies all already-seen events, skips them, and only appends genuinely new content. Reconnect also completes much faster since the full log replay is skipped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code reviewTwo issues found. 1. Bug: Persisted UUID set can be overwritten with an empty set (ChatViewModel.swift line 412)processedEventUUIDs is reset to [] at the start of each executeClaudeCommand call, before any events are processed. persistMessages is then called unconditionally with that empty set, overwriting whatever valid UUIDs were saved from the prior turn. Concrete failure scenario:
The same race exists via the periodic persist inside processServiceStream (every ~1s) if the first tick fires before any events arrive. Suggested fix - guard the save in persistMessages so a reset-empty set never overwrites valid prior UUIDs: wisp/Wisp/ViewModels/ChatViewModel.swift Lines 409 to 413 in e8e6985 2. CLAUDE.md: New model/view model logic requires unit testsThe PR adds loadStreamEventUUIDs() and saveStreamEventUUIDs() (new model methods) and modifies loadSession() and persistMessages() in ChatViewModel (view model logic), but no tests are added. CLAUDE.md rule: "Add new unit tests when adding or modifying logic (models, parsers, utilities, view models)" The existing test suite already covers SpriteChat serialization round-trips (SpriteChatMigrationTests.swift) and ChatViewModel session-restore behaviour - the new UUID persistence path fits naturally into those suites. wisp/Wisp/Models/Local/SpriteChat.swift Lines 50 to 58 in e8e6985 |
Guards saveStreamEventUUIDs in persistMessages so an empty processedEventUUIDs (reset at the start of a new turn) never overwrites a valid set saved from a prior turn. Adds unit tests for SpriteChat UUID round-trip and ChatViewModel persist/restore behaviour, including the empty-set guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
streamEventUUIDsData: Data?toSpriteChat(SwiftData field + helpers)processedEventUUIDsalongside messages on everypersistMessages()callprocessedEventUUIDsinloadSession()so reconnect deduplication works on a fresh VMProblem
When reopening a chat where Claude had been running,
ChatViewModelwas freshly created with emptyprocessedEventUUIDs.reconnectToServiceLogscheckshasPriorEvents = !processedEventUUIDs.isEmpty— since it was alwaysfalse, it cleared the last assistant message's content before replaying the full log. This caused:Fix
By persisting the UUID set alongside messages, the fresh VM loads the correct
processedEventUUIDson reopen. Reconnect then seeshasPriorEvents = true, skips clearing, and skips all already-seen events during replay — completing nearly instantly. Only genuinely new events (if Claude was still running) get appended.SwiftData migration is automatic —
streamEventUUIDsDatadefaults tonilfor existing chats, which gracefully falls back to the previous behaviour on first reopen.Test plan
nil) still work correctly on first reopen🤖 Generated with Claude Code