Skip to content

fix(platform): prevent chat loading state flicker during tool call transitions#492

Merged
larryro merged 2 commits into
mainfrom
fix/chat-loading-state-flicker
Feb 18, 2026
Merged

fix(platform): prevent chat loading state flicker during tool call transitions#492
larryro merged 2 commits into
mainfrom
fix/chat-loading-state-flicker

Conversation

@larryro

@larryro larryro commented Feb 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add hasIncompleteAssistantMessage check to useMessageProcessing that detects when the last assistant message is still in a non-terminal state (streaming or pending)
  • Include this flag in the isLoading computation in ChatInterface to keep the loading indicator active during status transitions between streaming and pending (e.g., during tool calls)
  • Add comprehensive tests for useMessageProcessing and useChatPendingState hooks covering the new behavior and existing functionality

Test plan

  • Unit tests added for hasIncompleteAssistantMessage covering: no messages, no assistant messages, success/failed/streaming/pending statuses, and multi-message scenarios
  • Unit tests added for useChatPendingState covering pending state clearing on streaming, fallback, and human input responses
  • Manual verification: trigger a tool call in chat and confirm the loading indicator stays active throughout the streaming → pending → streaming cycle

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved chat interface loading state to reduce UI flicker during message transitions, providing a smoother user experience when the chat transitions between different message states.
  • Tests

    • Added comprehensive test coverage for chat pending state management and message processing workflows.

…ansitions

Add hasIncompleteAssistantMessage check to keep loading indicator active
when status transitions between streaming and pending during tool calls.
@greptile-apps

greptile-apps Bot commented Feb 18, 2026

Copy link
Copy Markdown

Greptile Summary

Fixes a UI flicker where the chat loading indicator would briefly disappear during tool call transitions. When an assistant message transitions from streamingpending (while a tool executes) → streaming (when the agent resumes), the prior isLoading logic (isPending || !!streamingMessage) would evaluate to false during the pending gap.

  • Adds hasIncompleteAssistantMessage to useMessageProcessing — a memoized check that returns true when the last assistant message has a non-terminal status (streaming or pending)
  • Includes hasIncompleteAssistantMessage in the isLoading computation in ChatInterface, bridging the gap during tool call transitions
  • Adds comprehensive unit tests for both useMessageProcessing and useChatPendingState hooks

Confidence Score: 5/5

  • This PR is safe to merge — it's a focused UI fix with a small, well-understood surface area and comprehensive test coverage.
  • The change is minimal (one new memoized boolean + one OR condition), logically correct for all four known status values, and well-tested. The hasIncompleteAssistantMessage flag is partially redundant with !!streamingMessage during the streaming phase, but this is harmless in a boolean OR. No risk of stuck states beyond what already exists with the streamingMessage check, since the Convex SDK guarantees transition to terminal states.
  • No files require special attention

Important Files Changed

Filename Overview
services/platform/app/features/chat/hooks/use-message-processing.ts Adds hasIncompleteAssistantMessage computed via useMemo — checks if last assistant message status is non-terminal (streaming/pending). Clean logic, correct use of findLast, proper memoization dependency.
services/platform/app/features/chat/components/chat-interface.tsx Integrates hasIncompleteAssistantMessage into isLoading boolean. This keeps loading indicator active during streaming→pending→streaming transitions. The flag is partially redundant with !!streamingMessage during the streaming phase, but the redundancy is harmless and the pending coverage is the key improvement.
services/platform/app/features/chat/hooks/tests/use-message-processing.test.ts Comprehensive test coverage for hasIncompleteAssistantMessage, streamingMessage, and pendingToolResponse. Covers edge cases: no messages, user-only messages, all four status values, multi-message ordering.
services/platform/app/features/chat/hooks/tests/use-chat-pending-state.test.ts New tests for useChatPendingState covering streaming clearing, fallback clearing, human input response behavior, and combined scenarios. Well-structured with proper use of renderHook, rerender, and act.

Sequence Diagram

sequenceDiagram
    participant User
    participant ChatInterface
    participant useMessageProcessing
    participant UIMessages

    User->>ChatInterface: Send message
    ChatInterface->>ChatInterface: isPending = true

    UIMessages-->>useMessageProcessing: assistant status = "streaming"
    useMessageProcessing-->>ChatInterface: streamingMessage ✓, hasIncomplete ✓
    ChatInterface->>ChatInterface: isLoading = true (streaming)

    Note over UIMessages: Tool call begins
    UIMessages-->>useMessageProcessing: assistant status = "pending"
    useMessageProcessing-->>ChatInterface: streamingMessage ✗, hasIncomplete ✓
    ChatInterface->>ChatInterface: isLoading = true (hasIncomplete prevents flicker)

    Note over UIMessages: Tool call completes
    UIMessages-->>useMessageProcessing: assistant status = "streaming"
    useMessageProcessing-->>ChatInterface: streamingMessage ✓, hasIncomplete ✓
    ChatInterface->>ChatInterface: isLoading = true (streaming resumes)

    UIMessages-->>useMessageProcessing: assistant status = "success"
    useMessageProcessing-->>ChatInterface: streamingMessage ✗, hasIncomplete ✗
    ChatInterface->>ChatInterface: isLoading = false
Loading

Last reviewed commit: ea94fad

@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces a new hasIncompleteAssistantMessage computed field to the useMessageProcessing hook that identifies when the last assistant message in a thread is in a non-terminal state (status not 'success' or 'failed'). The chat interface component then incorporates this flag into its loading state calculation to prevent UI flicker during state transitions, such as when a message status changes from 'streaming' to 'pending' during tool calls. Supporting tests are added for both the hook and related pending state logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preventing chat loading state flicker during tool call transitions, which directly reflects the core fix in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/chat-loading-state-flicker

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@services/platform/app/features/chat/hooks/__tests__/use-message-processing.test.ts`:
- Around line 22-33: The test fixture createUIMessage currently force-casts the
returned object with "as UIMessage"; replace that cast by using TypeScript's
"satisfies UIMessage" on the object literal so the compiler verifies
compatibility without widening types — keep the function signature the same,
merge overrides as before, and remove the trailing "as UIMessage" so the
returned object satisfies UIMessage while preserving precise literal types.

@larryro larryro merged commit d72b98d into main Feb 18, 2026
16 checks passed
@larryro larryro deleted the fix/chat-loading-state-flicker branch February 18, 2026 14:09
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