Skip to content

feat: Add chart-owned runtime data limits and issue reporting - #656

Merged
dmitriy-kostianetskiy merged 2 commits into
mainfrom
delegate-chart-data-limits
May 21, 2026
Merged

dmitriy-kostianetskiy merged 2 commits into
mainfrom
delegate-chart-data-limits

Conversation

@ilyabo

@ilyabo ilyabo commented May 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Charts now enforce configurable data limits, surface per-panel runtime issues, and show a focused issue panel when a chart is broken.
    • AI assistant tooling can surface panel runtime issues to suggest targeted repairs.
  • Improvements

    • Improved error messages for data-limit and query errors; renderers clear or report runtime issues appropriately.
  • Tests

    • Added suites covering chart runtime policies, issue reporting, and dashboard issue visibility.

Review Change Stack

@ilyabo
ilyabo marked this pull request as draft May 21, 2026 13:10
@coderabbitai

coderabbitai Bot commented May 21, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds chart-runtime types/utilities, per-panel runtime issue state and APIs, chart-type data-policy hooks, renderer/client enforcement/reporting of policies and issues, and AI/tooling exposure of panel issues used by repair workflows.

Changes

Chart Runtime Issue Tracking

Layer / File(s) Summary
Chart runtime types and tests
packages/mosaic/src/chart-runtime.ts, packages/mosaic/__tests__/chart-runtime.test.ts
Adds ChartDataPolicy/context and ChartRuntimeIssue types; implements getQueryResultRowCount, assertChartDataPolicy, and createChartRuntimeIssueFromError. Tests exercise counting, policy enforcement, disabled/getResultSize behavior, maxRows:0, and issue creation from DataPointLimitError.
Dashboard panel runtime state & APIs
packages/mosaic/src/dashboard/MosaicDashboardSlice.ts, packages/mosaic/__tests__/MosaicDashboardRuntimeIssue.test.ts
Introduces runtime-only panelIssuesByPanelId and store methods getPanelIssue, reportPanelIssue, clearPanelIssue; eviction and clear-all paths remove issue entries. Test verifies report → retrieval → non-persistence in config → eviction clearing.
Chart type data-policy hooks
packages/mosaic/src/chart-types/base-types.ts, packages/mosaic/src/chart-types/*/definition.ts
Extends BaseChartTypeDefinition with optional getDataPolicy(context); box-plot, bubble, custom-spec, and line-chart provide getDataPolicy mapping maxDataPoints→maxRows and reason strings (line-chart returns null when xInterval set).
Dashboard rendering & issue display
packages/mosaic/src/chart/MosaicDashboardChartContent.tsx, packages/mosaic/src/chart/ChartRuntimeIssuePanel.tsx, packages/mosaic/src/VgPlotChart.tsx, packages/mosaic/src/VgPlotChartError.tsx, packages/mosaic/src/wrapCoordinatorWithValidation.ts, packages/mosaic/src/index.ts
MosaicDashboardChartContent computes chart dataPolicy, builds runtimeIssueContext/reporter, renders ChartRuntimeIssuePanel early for panel issues, and forwards runtime plumbing into component and vgplot render paths. Adds ChartRuntimeIssuePanel component, updates VgPlotChart props/equality checks, changes DataPointLimitError title to "Too much data", deprecates coordinator wrapper in favor of renderer policies, and re-exports chart-runtime helpers/types.
Renderer and client enforcement/reporting
packages/mosaic/src/useVgPlotChartRender.ts, packages/mosaic/src/useMosaicClient.ts, packages/mosaic/src/boxplot/BoxPlotClient.ts, packages/mosaic/src/chart-types/box-plot/renderer/useBoxPlotClient.ts, packages/mosaic/src/chart-types/box-plot/renderer/BoxPlotPanelRenderer.tsx
Adds optional dataPolicy, runtimeIssueContext, and runtimeIssueReporter parameters; wraps mark/query handlers to call assertChartDataPolicy, normalize errors, call onError, set retained chart error, and create/report ChartRuntimeIssue as appropriate. BoxPlotClient validates queryResult, reports on queryError, and client/hooks/renderer thread the new params.
AI adapter and tool integration
packages/mosaic/src/ai.ts, packages/mosaic/src/chart-types/list-panels-tool.ts, packages/mosaic/__tests__/dashboard-ai.test.ts
Extends DashboardAiAdapter with optional getPanelIssue and conditionally wires it into tool deps; list_dashboard_panels tool augments panel entries with transient issue when available. AI instructions updated to use list_dashboard_panels runtime issues for repair guidance; tests update harness and verify returned panels include seeded runtime issues.
Coordinator validation migration & exports
packages/mosaic/src/MosaicSlice.ts, packages/mosaic/src/wrapCoordinatorWithValidation.ts, packages/mosaic/src/index.ts
Removes coordinator-level wrapCoordinatorWithValidation usage from MosaicSlice (validation now renderer-owned), updates wrapCoordinatorWithValidation to use imported row-count helper and deprecation notice, and re-exports chart-runtime helpers/types from package index.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#638: Related work on coordinator vs renderer data-point limiting and DataPointLimitError plumbing.
  • sqlrooms/sqlrooms#650: Related AI tooling refactors that interact with dashboard AI utilities updated here.
  • sqlrooms/sqlrooms#588: Overlaps in box-plot client/renderer changes that this PR extends with runtime-issue reporting.

Suggested reviewers

  • dmitriy-kostianetskiy

Poem

🐰 I hopped through chart and policy land,
Counted rows with careful hand.
When data floods, I sound the bell,
An issue shows where charts won't dwell.
AI can mend, the dashboard sings—
Happy hops and safer things!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: adding chart-owned runtime data limits and issue reporting mechanisms throughout the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch delegate-chart-data-limits

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/mosaic/src/VgPlotChart.tsx (1)

194-221: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix cached-chart lifecycle so dataPolicy / runtime issue props can’t go stale.

  • MosaicDashboardChartContent already memoizes dataPolicy, runtimeIssueContext, and runtimeIssueReporter with useMemo, so inline-object remount concerns at those call sites are already handled.
  • Still, useVgPlotChartRender returns early when cachedChart is present and never re-wraps plot.marks[*].queryResult/queryError; since useVgPlotChartRetention keys only by specKey + params, changing dataPolicy/runtimeIssue* can leave the cached chart using stale closure values (wrong policy assertion / wrong runtime issue reporting).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mosaic/src/VgPlotChart.tsx` around lines 194 - 221, The cached chart
can hold stale closures for dataPolicy/runtimeIssue reporting because
useVgPlotChartRender returns early when cachedChart exists; update the hook so
when a cachedChart is found it either (A) invalidates the cache based on
dataPolicy/runtimeIssueContext/runtimeIssueReporter changes (add those to the
cache key used by useVgPlotChartRetention along with specKey+params) or (B)
after retrieving cachedChart, re-wrap each mark’s runtime handlers (iterate
cachedChart.plot.marks and replace mark.queryResult and mark.queryError with
wrappers that call the current
dataPolicy/runtimeIssueContext/runtimeIssueReporter) so the cached chart uses
fresh closures; reference useVgPlotChartRender, useVgPlotChartRetention,
cachedChart, plot.marks[*].queryResult/queryError, specKey, params, dataPolicy,
runtimeIssueContext, runtimeIssueReporter when making the change.
🧹 Nitpick comments (3)
packages/mosaic/__tests__/chart-runtime.test.ts (1)

21-35: ⚡ Quick win

Add a regression test for maxRows: 0.

Given policy semantics, zero should be a valid hard limit. Adding this test will lock in boundary behavior.

Suggested test case
   it('skips disabled policies and supports custom size estimators', () => {
@@
   });
+
+  it('enforces zero-row limits when maxRows is 0', () => {
+    expect(() => assertChartDataPolicy({maxRows: 0}, [{x: 1}])).toThrow(
+      DataPointLimitError,
+    );
+    expect(() => assertChartDataPolicy({maxRows: 0}, [])).not.toThrow();
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mosaic/__tests__/chart-runtime.test.ts` around lines 21 - 35, Add a
regression test that verifies maxRows: 0 is treated as a valid hard limit: using
assertChartDataPolicy with {maxRows: 0} and an empty data array should not
throw, while using {maxRows: 0} with any non-empty array (e.g., [{x:1}]) should
throw DataPointLimitError; place this assertion alongside the existing tests
that call assertChartDataPolicy and reference the same DataPointLimitError to
lock in the boundary behavior.
packages/mosaic/src/useVgPlotChartRender.ts (1)

154-173: ⚡ Quick win

Subtle: catch-path delegates to mark.queryError, which by then is the wrapped version.

In the queryResult catch branch you call mark.queryError(normalizedError) (line 160). Because the same forEach iteration installs the queryError wrapper a few lines later (175–197), the runtime call dispatches to the wrapper — so the issue is still reported via that path. This is correct but fragile: if the two if blocks are ever reordered or the wrapping is split, the runtimeIssueContext branch at lines 162–170 will be silently skipped whenever mark.queryError exists. A short comment explaining the dependency, or moving the explicit reportIssue call out of the else so it always fires on failure, would harden this.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mosaic/src/useVgPlotChartRender.ts` around lines 154 - 173, In the
queryResult catch branch, ensure the runtime issue is always reported regardless
of whether mark.queryError is present: move or duplicate the
runtimeIssueReporter?.reportIssue(createChartRuntimeIssueFromError(normalizedError,
runtimeIssueContext, dataPolicy)) call so it executes before delegating to
mark.queryError(normalizedError) (or call it unconditionally then call
mark.queryError if present), and/or add a short comment clarifying the ordering
dependency; update the catch block around mark.queryError, runtimeIssueContext,
runtimeIssueReporter, and createChartRuntimeIssueFromError to guarantee
reporting always happens prior to or independent of the wrapped mark.queryError
invocation.
packages/mosaic/src/useMosaicClient.ts (1)

110-121: ⚡ Quick win

onQueryError forwards raw unknown; consider normalizing for consistency.

useVgPlotChartRender normalizes thrown values to Error before reporting/forwarding, but here the raw error: unknown is passed both to createChartRuntimeIssueFromError and to queryErrorRef.current. If a non-Error value (string, DOMException, etc.) bubbles up from Mosaic, downstream consumers expecting Error (e.g. .message) may break. Normalize once and reuse:

♻️ Suggested change
       onQueryError: (error) => {
+        const normalizedError =
+          error instanceof Error ? error : new Error(String(error));
         if (runtimeIssueContextRef.current) {
           runtimeIssueReporterRef.current?.reportIssue(
             createChartRuntimeIssueFromError(
-              error,
+              normalizedError,
               runtimeIssueContextRef.current,
               dataPolicyRef.current,
             ),
           );
         }
-        queryErrorRef.current?.(error);
+        queryErrorRef.current?.(normalizedError);
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mosaic/src/useMosaicClient.ts` around lines 110 - 121, Normalize the
incoming unknown `error` in `onQueryError` to an actual `Error` instance before
using it: create a normalized Error (e.g., via a small helper or `error
instanceof Error ? error : new Error(String(error))`) and pass that same
normalized Error into `createChartRuntimeIssueFromError(...)` and to
`queryErrorRef.current(...)` so downstream consumers always receive an Error
object; update the `onQueryError` handler in `useMosaicClient` to perform this
single normalization and reuse the normalized value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mosaic/src/chart-runtime.ts`:
- Around line 69-72: The warning in getQueryResultRowCount currently prints the
full result payload (result) which can leak sensitive data; change the
console.warn in that function to avoid including result directly—either remove
the result argument entirely or replace it with a safe summary (e.g., typeof
result, Array.isArray(result), a boolean for presence of toArray, or a bounded
length/row count) so logs show only non-sensitive shape/metadata and not the raw
data.
- Around line 80-82: The early-return treats policy.maxRows falsy (so 0) as "no
limit"; change the check in the validation guard so that it only returns when
policy is missing or disabled or when maxRows is null/undefined (not when it
equals 0). Locate the guard that reads "if (!policy || policy.disabled ||
!policy.maxRows) return;" and replace the maxRows falsy check with an explicit
undefined/null check (e.g., policy.maxRows == null) so policies with maxRows: 0
are validated.

---

Outside diff comments:
In `@packages/mosaic/src/VgPlotChart.tsx`:
- Around line 194-221: The cached chart can hold stale closures for
dataPolicy/runtimeIssue reporting because useVgPlotChartRender returns early
when cachedChart exists; update the hook so when a cachedChart is found it
either (A) invalidates the cache based on
dataPolicy/runtimeIssueContext/runtimeIssueReporter changes (add those to the
cache key used by useVgPlotChartRetention along with specKey+params) or (B)
after retrieving cachedChart, re-wrap each mark’s runtime handlers (iterate
cachedChart.plot.marks and replace mark.queryResult and mark.queryError with
wrappers that call the current
dataPolicy/runtimeIssueContext/runtimeIssueReporter) so the cached chart uses
fresh closures; reference useVgPlotChartRender, useVgPlotChartRetention,
cachedChart, plot.marks[*].queryResult/queryError, specKey, params, dataPolicy,
runtimeIssueContext, runtimeIssueReporter when making the change.

---

Nitpick comments:
In `@packages/mosaic/__tests__/chart-runtime.test.ts`:
- Around line 21-35: Add a regression test that verifies maxRows: 0 is treated
as a valid hard limit: using assertChartDataPolicy with {maxRows: 0} and an
empty data array should not throw, while using {maxRows: 0} with any non-empty
array (e.g., [{x:1}]) should throw DataPointLimitError; place this assertion
alongside the existing tests that call assertChartDataPolicy and reference the
same DataPointLimitError to lock in the boundary behavior.

In `@packages/mosaic/src/useMosaicClient.ts`:
- Around line 110-121: Normalize the incoming unknown `error` in `onQueryError`
to an actual `Error` instance before using it: create a normalized Error (e.g.,
via a small helper or `error instanceof Error ? error : new
Error(String(error))`) and pass that same normalized Error into
`createChartRuntimeIssueFromError(...)` and to `queryErrorRef.current(...)` so
downstream consumers always receive an Error object; update the `onQueryError`
handler in `useMosaicClient` to perform this single normalization and reuse the
normalized value.

In `@packages/mosaic/src/useVgPlotChartRender.ts`:
- Around line 154-173: In the queryResult catch branch, ensure the runtime issue
is always reported regardless of whether mark.queryError is present: move or
duplicate the
runtimeIssueReporter?.reportIssue(createChartRuntimeIssueFromError(normalizedError,
runtimeIssueContext, dataPolicy)) call so it executes before delegating to
mark.queryError(normalizedError) (or call it unconditionally then call
mark.queryError if present), and/or add a short comment clarifying the ordering
dependency; update the catch block around mark.queryError, runtimeIssueContext,
runtimeIssueReporter, and createChartRuntimeIssueFromError to guarantee
reporting always happens prior to or independent of the wrapped mark.queryError
invocation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 90bb4f67-d625-4c86-8293-8d6e97b49162

📥 Commits

Reviewing files that changed from the base of the PR and between f3f0b9e and 0ff4328.

📒 Files selected for processing (26)
  • packages/mosaic/__tests__/MosaicDashboardRuntimeIssue.test.ts
  • packages/mosaic/__tests__/chart-runtime.test.ts
  • packages/mosaic/__tests__/dashboard-ai.test.ts
  • packages/mosaic/src/MosaicSlice.ts
  • packages/mosaic/src/VgPlotChart.tsx
  • packages/mosaic/src/VgPlotChartError.tsx
  • packages/mosaic/src/ai.ts
  • packages/mosaic/src/boxplot/BoxPlotClient.ts
  • packages/mosaic/src/chart-runtime.ts
  • packages/mosaic/src/chart-types/base-types.ts
  • packages/mosaic/src/chart-types/box-plot/definition.ts
  • packages/mosaic/src/chart-types/box-plot/renderer/BoxPlotPanelRenderer.tsx
  • packages/mosaic/src/chart-types/box-plot/renderer/useBoxPlotClient.ts
  • packages/mosaic/src/chart-types/bubble-chart/definition.ts
  • packages/mosaic/src/chart-types/custom-spec/definition.ts
  • packages/mosaic/src/chart-types/line-chart/definition.ts
  • packages/mosaic/src/chart-types/list-panels-tool.ts
  • packages/mosaic/src/chart/ChartRuntimeIssuePanel.tsx
  • packages/mosaic/src/chart/MosaicDashboardChartContent.tsx
  • packages/mosaic/src/chart/MosaicDashboardComponentChart.tsx
  • packages/mosaic/src/chart/MosaicDashboardVgPlotChart.tsx
  • packages/mosaic/src/dashboard/MosaicDashboardSlice.ts
  • packages/mosaic/src/index.ts
  • packages/mosaic/src/useMosaicClient.ts
  • packages/mosaic/src/useVgPlotChartRender.ts
  • packages/mosaic/src/wrapCoordinatorWithValidation.ts
💤 Files with no reviewable changes (1)
  • packages/mosaic/src/MosaicSlice.ts

Comment thread packages/mosaic/src/chart-runtime.ts
Comment thread packages/mosaic/src/chart-runtime.ts Outdated
@dmitriy-kostianetskiy
dmitriy-kostianetskiy marked this pull request as ready for review May 21, 2026 13:22
@dmitriy-kostianetskiy
dmitriy-kostianetskiy merged commit 6c65b20 into main May 21, 2026
3 of 4 checks passed
@dmitriy-kostianetskiy
dmitriy-kostianetskiy deleted the delegate-chart-data-limits branch May 21, 2026 13:33
@coderabbitai coderabbitai Bot mentioned this pull request May 22, 2026
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