Skip to content

[DBA-94] Fix plot propagation to previous messages#68

Open
SimonKaran13 wants to merge 4 commits intomainfrom
simon.karan/DBA-94-fix-plot-propagates-to-previous-message
Open

[DBA-94] Fix plot propagation to previous messages#68
SimonKaran13 wants to merge 4 commits intomainfrom
simon.karan/DBA-94-fix-plot-propagates-to-previous-message

Conversation

@SimonKaran13
Copy link
Member

Problem

When a user sends multiple queries that generate plots, new plots overwrite previously rendered messages. The root cause is that thread._visualization_result is a shared mutable singleton on the Thread object — every thread.plot() call overwrites it, so non-latest messages with has_visualization=True but no per-message visualization_data fall back to this shared state and render the wrong plot.

Solution

Restrict the shared thread fallback to the latest message only. Non-latest messages now render exclusively from their own persisted visualization_data. An allow_thread_fallback guard on render_visualization_section provides defense-in-depth, and a persistence fix ensures missing parquet files don't leave spec_df absent from the dict (which caused silent render failures).

Summary

  • Restrict thread._visualization_result fallback to the latest message only
  • Add allow_thread_fallback keyword-only parameter to render_visualization_section as defense-in-depth guard
  • Fix persistence round-trip in load_chat when _has_spec_df is True but parquet file is missing
  • Add diagnostic warning log when visualization_data is None after a successful thread.plot() call

Test plan

  • make check passes (ruff, mypy, agent guidance)
  • make test passes (pre-existing test_index failure only)
  • 9 new regression tests in tests/test_plot_propagation.py
  • Manual: send two queries that generate plots → first message's plot must not change after second completes
  • Manual: click "Generate Plot" on an older message → only that message gets the new plot
  • Manual: refresh browser → all plots render correctly for their respective messages

🤖 Generated with Claude Code

Restrict the shared thread._visualization_result fallback to the latest
message only, preventing older messages from rendering a newer query's
plot. Add allow_thread_fallback guard to render_visualization_section as
defense-in-depth, fix persistence round-trip when _has_spec_df marker is
present but parquet file is missing, and add diagnostic logging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SimonKaran13 SimonKaran13 force-pushed the simon.karan/DBA-94-fix-plot-propagates-to-previous-message branch from eee1c94 to 429d320 Compare March 20, 2026 13:23
@SimonKaran13 SimonKaran13 force-pushed the simon.karan/DBA-94-fix-plot-propagates-to-previous-message branch from 429d320 to bb68723 Compare March 20, 2026 13:33
@SimonKaran13 SimonKaran13 marked this pull request as ready for review March 20, 2026 13:39
@SimonKaran13 SimonKaran13 requested review from a team and Copilot March 20, 2026 13:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an issue where visualization results from newer plots could incorrectly render in older messages by preventing non-latest messages from falling back to the shared thread._visualization_result, and hardening visualization persistence loading.

Changes:

  • Restricts thread-level visualization fallback rendering to the latest message via an explicit allow_thread_fallback guard.
  • Improves load_chat visualization persistence round-trip when _has_spec_df is set but the parquet file is missing (ensures spec_df is present and None).
  • Adds regression tests covering fallback behavior and persistence edge cases.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/databao_cli/ui/components/results.py Adds guarded thread fallback and adjusts rendering logic to avoid plot “propagation” to older messages.
src/databao_cli/ui/services/chat_persistence.py Ensures spec_df is explicitly set to None (and logs) when _has_spec_df expects parquet but it’s missing or unreadable.
tests/test_plot_propagation.py Adds regression tests for fallback gating and persistence loading behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Only mark has_visualization=True when _extract_visualization_data
actually returns data, preventing collapsed sections with no content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +361 to +364
elif visualization_data is not None:
render_visualization_section(thread, visualization_data)
elif is_latest and (has_visualization or thread._visualization_result is not None):
render_visualization_section(thread, None, allow_thread_fallback=True)
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

In render_visualization_and_actions, the elif visualization_data is not None: branch will run even when the dict can’t actually be rendered (e.g., missing spec/spec_df), which prevents the latest-message thread fallback branch from ever running. This can lead to the latest message showing no visualization despite thread._visualization_result being available. Consider gating this branch on persisted data being renderable (e.g., both spec and spec_df present), or allowing render_visualization_section to fall back to the thread result when allow_thread_fallback=True and persisted data is incomplete.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

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

_extract_visualization_data is all-or-nothing: it either returns a complete dict with both spec and spec_df, or None. There is no code path that produces an incomplete dict, so in practice this branch cannot run with unrenderable data. The early return at L231 in render_visualization_section is already a safety net for that theoretical case. Adding an extra renderability gate here would be defensive code for a scenario that cannot currently occur.

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.

2 participants