Skip to content

Fix main-thread freeze on large conversations#10

Merged
nickdirienzo merged 1 commit intomainfrom
nvd/crash-fix
Apr 26, 2026
Merged

Fix main-thread freeze on large conversations#10
nickdirienzo merged 1 commit intomainfrom
nvd/crash-fix

Conversation

@nickdirienzo
Copy link
Copy Markdown
Collaborator

@nickdirienzo nickdirienzo commented Apr 26, 2026

Summary

  • Replace ForEach(Array(sections.enumerated()), id: \.element.id) with ForEach(visibleSections) — the tuple key path \.element.id was copying the entire ChatSection enum (including [AgentMessage] arrays) on every layout pass to evaluate identities. Visible in the cpu_resource.diag crash trace as ForEach.IDGenerator.makeIDinitializeWithCopy for ChatSection. Using native Identifiable evaluates \.id directly — for tool/thinking/provision groups this reads the stored UUID without touching the message arrays at all.

  • Throttle scrollToBottom with a scrollQueued flaglastMessageLength fires per streaming token, previously queuing hundreds of proxy.scrollTo calls per burst. Each call triggers a layout pass; the 89% CPU for 100s in the second crash report was this hot loop.

  • Add FlightBench targets + wire bench into CI — two new targets guard against regressions in visibleSections slice cost and section ID enumeration. swift run -c release FlightBench now runs on every PR and blocks on failure.

Test plan

  • swift run -c release FlightBench — all 10 targets green
  • Switch to a conversation with many messages; UI stays responsive
  • Streaming still auto-scrolls to bottom
  • Search navigates to matches

🤖 Generated with Claude Code

@nickdirienzo nickdirienzo changed the title Fix main-thread freeze when switching to large conversations Fix main-thread freeze on large conversations Apr 26, 2026
Three changes working together:

1. Paginate ChatMessageListView to max 150 visible sections — prevents the
   AttributeGraph from growing unbounded during long remote sessions and bounds
   the work done by scrollToBottom.

2. Replace ForEach(Array(sections.enumerated()), id: \.element.id) with
   ForEach(visibleSections) using native Identifiable — eliminates the
   per-layout-pass initializeWithCopy for ChatSection that was copying entire
   [AgentMessage] arrays to evaluate tuple key paths, visible in the
   cpu_resource.diag crash trace.

3. Throttle scrollToBottom with a scrollQueued flag — lastMessageLength fires
   per streaming token, previously queuing hundreds of proxy.scrollTo calls
   per second and keeping the main thread in a continuous layout loop.

Adds FlightBench targets for visibleSections slice cost and section ID
enumeration, and wires FlightBench into the PR workflow so regressions
in these paths block CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nickdirienzo nickdirienzo merged commit 9264d96 into main Apr 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant