Skip to content

feat(schema): bubble-up paired histograms + inline profile in schema view#1380

Closed
danyelf wants to merge 36 commits into
mainfrom
prototype/bubbleup-histograms
Closed

feat(schema): bubble-up paired histograms + inline profile in schema view#1380
danyelf wants to merge 36 commits into
mainfrom
prototype/bubbleup-histograms

Conversation

@danyelf
Copy link
Copy Markdown
Contributor

@danyelf danyelf commented May 18, 2026

Draft — adding screenshots inline.

Note that this is a prorotype that is used to show how this concept is used. I expect it to be replaced by later PRs that actually build and wire performant machinery.

PR checklist

  • Ensure you have added or ran the appropriate tests for your PR.
  • DCO signed

What type of PR is this?

feat — adds an inline profile / paired-histogram experience to the schema view, gated behind a new --inline-profile server flag (requires --new-cll-experience).

What this PR does / why we need it

Prototype branch for the DRC-3390 paired-histogram / bubble-up profile experience. End-to-end:

  • Backend — new ProfileDistributionTask (recce/tasks/profile_distribution.py) computes paired distributions per column. Probes COUNT(DISTINCT) and dispatches to top-K (categorical) or uniform-bin histogram (continuous). Handles added/removed columns. Registered as RunType.PROFILE_DISTRIBUTION.
  • CLI--inline-profile server flag exposes the experience (recce/cli.py).
  • Frontend primitivesPairedHistogramDiscreteCell and PairedHistogramContinuousCell promoted from Storybook to @datarecce/ui/primitives.
    • Discrete: paired side-by-side bars with gap-on-absent semantics.
    • Continuous: stacked-overlap with a 2×2 orange/blue checker pattern in the agreement zone (replaces the earlier shmellow taupe).
    • Native SVG <title> tooltips per slot; per-instance pattern IDs via useId().
  • Schema view — inline distribution column on Compact mode and gallery cards; replaces the unique quadrant on cards. Compact / Grid toggle (Wide mode removed — falls back silently for users with "wide" in localStorage).
  • HooksuseInlineProfile fires profile_diff and profile_distribution in parallel with independent error paths; useProfileMode persists Compact/Grid choice.
  • Loading / error — per-cell pending spinner (small pulsing dot). Full empty-state pass still TODO.
  • DiffEditor fix — pinned @codemirror/view to 6.41.0 in pnpm.overrides so unifiedMergeView decorations actually render (two copies were silently no-op'ing the merge plugin). Unrelated to the histogram work but discovered during prototype review.

Exploration / design notes live under docs/feature-exploration/profile-render-modes-paired-histograms/.

Which issue(s) this PR fixes

  • DRC-3390 (primary)
  • Related: DRC-3389 (perf — multiple sequential full-table scans per column), DRC-3392 (misleading Snowflake "model is empty" rewrite)

Special notes for your reviewer

  • Branch is still labelled prototype/… — landing as a single PR for review feedback rather than splitting per subsystem.
  • Demo project: jaffle_shop_duckdb against --target snowflake_dev. Note that stg_orders.sql literally rewrites every status value to 'ORDER-' || status — this is real, not a chart bug.
  • Two distinct visual languages for discrete vs continuous are intentional (different data shapes); don't unify.
  • Don't reintroduce synthetic / fallback distributions — ProfileDistributionTask returns empty slots if data isn't available.

Does this PR introduce a user-facing change?

Add `--inline-profile` server flag that surfaces paired column distributions (discrete + continuous) directly on the schema view. 

NOTE: this is a protoiype PR. We do not expect to ship both'Compact' and 'Grid.' This will be reimplemented and requires additional research before release. In particular, there are a few Linear issues on query perf -- based on samples of data or other efficiencies.

image image image image

Test plan

  • recce server --new-cll-experience --inline-profile -t snowflake_dev against jaffle_shop_duckdb
  • Schema view → Compact mode shows distribution sparklines per column
  • Schema view → Grid mode shows 2×2 cards with the distribution quadrant
  • Hover a discrete cell → per-slot tooltip value [base: P%, current: P%]
  • Hover a continuous cell → per-bin tooltip lo–hi [base: P%, current: P%]
  • Code pane on stg_orders shows red/green SQL diff for the 'ORDER-' || rewrite (regression check for the @codemirror/view dedup)
  • Storybook stories under "Paired Histograms" still render

🤖 Generated with Claude Code

danyelf and others added 30 commits April 20, 2026 14:54
Adds an `inline_profile` server flag, plumbed from the CLI through
the /api/flag endpoint to the frontend RecceServerFlags type.
Feature gate for the forthcoming inline-profile schema-view feature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Submits profile_diff for a given model + columns and transforms the
resulting DataFrames into a per-column stats Map. React Query caches
by (modelName, sorted-columns) so reopening the same schema view
renders synchronously.

Handles uppercase DataFrame keys (Snowflake) by normalising to
lower-cased column-name keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…s provided

Extends toSchemaDataGrid with a new optional `profileByColumn` option.
When provided, the generator appends five inline-diff columns
(% non-null, min, max, avg, unique) after the existing index/name
columns and populates base__* / current__* row fields from the map,
matching columns case-insensitively.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
PrivateSchemaView reads the new inline_profile flag alongside
new_cll_experience, scopes the profile run to impacted columns
present in both environments, and passes the resulting
profileByColumn map through to toSchemaDataGrid which appends
% non-null / min / max / avg / unique columns.

An error banner surfaces profile-load failures above the grid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Adds a button next to SchemaLegend that switches useInlineProfile's
column list from the impacted-columns subset to every column present
in both environments. Label adapts:

- "Profile all columns" when no stats have been loaded yet
  (empty-impacted-set case)
- "Profile remaining columns" when impacted-column stats are already
  present

The button hides when every profilable column is already covered,
and shows "Profiling…" while a run is in flight.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Without this reset, clicking "Profile remaining columns" on one model
left the schema view stuck in expanded mode when the user navigated to
another model. The next model would immediately profile every column
in both envs, bypassing the impacted-only default.

A useEffect keyed on the node id clears expanded back to false on
navigation. Adds a regression test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Keep STORAGE_KEY exported from the useProfileMode module so the colocated
test can import it, but remove it from the @datarecce/ui hooks barrel
export so the storage key string does not become part of the package's
public API.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Why: statState only guarded against undefined. Two null values (a plausible
serialization of "no data") fell through to the equality check and rendered
as 'same' (grey) instead of 'empty' (outlined).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Why: row-hydration and wide-mode column defs were both enumerating the
same five stat field names in two separate lists. Hoist a single source
of truth so adding or renaming a stat requires one edit, not two.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Why: the gallery view uses --schema-color-impacted-accent and
--schema-badge-impacted-{bg,fg} but they were only declared inside
.cll-experience. When the gallery mounts without that ancestor class,
the impacted card border and badge would lose their colors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…emaView

Plumbs useProfileMode into PrivateSchemaView, places the toggle in the
inline-profile header next to the expand button, and routes rendering
between ScreenshotDataGrid (wide/strip) and SchemaGalleryView (grid).

Also widens SchemaDataGridOptions.profileMode to accept "grid" so the
caller can pass the unified mode through — toSchemaDataGrid treats "grid"
like "wide" for column layout; the gallery renders from rows directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…ack test

Why: the two-condition form made the mutual exclusion between
SchemaGalleryView and ScreenshotDataGrid hard to verify at a glance.
Collapsing to a ternary makes the invariant self-evident and removes
a reconciler node. Also adds an explicit test for the non-obvious
case where profileMode is "grid" but inlineProfileActive is false —
expected behavior is to fall back to ag-grid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Biome reflowed a few multi-line statements, sorted barrel exports, and
flagged two empty onChange={() => {}} blocks in ProfileModeToggle tests
(replaced with vi.fn()).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
- Gallery now requires isProfilable in addition to inlineProfileActive,
  matching the toggle's guard. Prevents an empty all-dashes gallery
  when a stored mode="grid" collides with a non-profilable node.
- is_unique=false now renders as "✗" instead of "—", distinguishing
  "definitively not unique" from "no profile data available".
- toSchemaDataGrid short-circuits grid mode instead of generating 5
  wide-mode column defs that the caller immediately discards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…er open

- Gallery quadrants now render "base → current" inline whenever a stat
  differs between envs. Unchanged stats still show a single value.
- Strip-cell button now stops click propagation so the popover survives
  ag-grid's cell-click handler (which was navigating to CLL and
  re-rendering the cell, dismissing the popover within a frame).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…+ hover

- formatQuadValue: percentages now show 2 decimals (96.00%), floats are
  trimmed to at most 2 decimals (69.370000 → 69.37). Also parses numeric
  strings so the backend's "69.370000" strings render cleanly. Numeric
  comparison is used for change detection so string-level drift like
  "69.370000" vs "69.37" doesn't show a spurious diff.
- Strip: per-square tooltips now render via MUI Tooltip (hover works
  reliably; native title attribute was unreliable inside the popover).
- Grid cards and chips now fire onColumnClick when clicked. SchemaView
  wires this to the same CLL navigation handler ag-grid uses, so grid
  mode retains the click-to-view-column-lineage behaviour.
- Gallery header shows "total rows" label with base → current when the
  row count changed. Sourced from row_count added to useInlineProfile's
  STAT_FIELDS and hydrated onto rows via PROFILE_STAT_SPECS. row_count
  is intentionally excluded from wide-mode columns (it duplicates per
  row).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…popover)

- Replaces the click-triggered Popover (table of label/base/→/current)
  with a hover-triggered MUI Tooltip whose content is a mini card
  matching the grid-card visual: name header + stacked stat rows with
  base → current and amber highlight on changed rows.
- Strip is no longer a click target of its own — clicks on the cell
  bubble to ag-grid's cell handler, so CLL navigation works consistently
  with the name column.
- Extracts toNumeric/formatProfileValue into profileFormat.ts for reuse
  between the gallery and the strip hover card.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
- Adds a small CircularProgress + "Profiling…" indicator next to the
  ProfileModeToggle while useInlineProfile is in flight. Previously the
  only loading hint was on the expand button, which isn't visible for
  the default impacted-only scope.
- The "Couldn't load column profile" banner now appends the actual
  backend error (e.g. "the model is empty") so the user can see why
  the run failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…er walkthrough)

Pre-flight built the worktree frontend successfully (sandbox disabled, next
build invoked directly to bypass pnpm spawn EPERM). Recording itself is
blocked: agent-browser is not installed; no Playwright/Puppeteer reachable;
no browser binary visible from sandbox; ffmpeg missing; ~/code/Recce/jaffle_shop_duckdb
is sandbox-blocked even with dangerouslyDisableSandbox. Per dispatch hard
rule, ad-hoc Playwright fallback is not allowed — surfacing instead.

Captures dir reserved at docs/feature-exploration/profile-render-modes-captures/
for the captain to populate from their own terminal where browser + screen
recorder are reachable.

Signed-off-by: Danyel Fisher <danyel@gmail.com>
Replace silent except: pass with logger.warning so per-node change-analysis failures are visible in recce server logs during capture/iterate work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…report

Walked through Profile Render Modes (Wide/Strip/Grid) live against jaffle_shop_duckdb (recce server with --new-cll-experience --inline-profile, model stg_orders, 999 rows) using e2e-pipeline:e2e-walkthrough in auto mode. Captured AC-1 (three render modes via toggle), AC-2 (strip hover mini-card with NULL%/MIN/MAX/AVG/UNIQUE), and AC-3 (click profile cell -> Column Lineage view focused on stg_orders.ORDER_ID).

AC-4 (DCO): all 27 commits on this branch already have Signed-off-by trailers. None are GPG-signed but AC-4 only requires the DCO trailer.

Captures saved to docs/feature-exploration/profile-render-modes-captures/. Stage report appended to entity body. Feedback ask is BLOCKED on captain-side Slack delivery (sandbox has no Slack channel); top-2 candidate reviewers proposed in stage report. Recommend: PASSED contingent on directional feedback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Storybook-only exploration of cell-sized BubbleUp histograms paired
on baseline + current data, covering three cardinality buckets:

- low-card string (auto X-axis at <=40, auto-trim at >75 + outliers)
- low-card numeric (equal-width buckets, NOT proportional spacing)
- high-card quantitative (overlapping bars, NOT side-by-side)

Custom inline SVG component (~250 LOC) lives under
js/packages/storybook/stories/bubbleup/ -- NOT shipped via @datarecce/ui.
Synthetic fixtures only; no backend wiring.

Captures + design notes (chart library choice, data shapes, threshold
rationale, backend deltas needed in ColumnProfileStats, open questions)
under docs/feature-exploration/profile-render-modes-bubbleup-exploration/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
danyelf and others added 6 commits May 8, 2026 14:04
…components

Replaces the earlier custom-SVG single-component prototype with a paired
design exploration covering the two render targets that matter for the
SchemaView surface:

- PairedHistogramDiscreteCell — categorical paired-bar chart for
  low-cardinality columns (string + numeric labels). Slot padding for
  category separation, optional per-slot labels at card density.
- PairedHistogramContinuousCell — continuous paired-bar chart for
  high-cardinality numerics. Bars touch (no slot padding) for visual
  continuity, endpoint min/max labels at card density.

Both share overlap-with-alpha visual + color tokens from HistogramChart.
HistogramChart itself isn't reused at cell density because its title +
legend eat ~50 px of overhead, leaving zero chart area at h=36; it
remains the right tool for full-size detail/popover surfaces.

Surface mocks framing each chart in its real visual context:
- GridRowMock — schema-grid row with column name, type, strip, and a
  distribution slot.
- GalleryCardMock — SchemaGalleryView-style card with the chart as
  primary visual, four-quadrant stats, base/current legend.
- SidebarMock + SidebarRowMock — the \"all columns at a glance\" panel,
  using Recce's existing .row-{added,removed,changed,impacted} tints,
  .schema-change-badge-* for +/-/~/! glyphs, and DataTypeIcon.

11 stories across Visualizations/Paired Histograms/{Low-Card String,
Low-Card Numeric, High-Card Quantitative, Sidebar}, each demonstrating
both cell + baseball-card density plus a sidebar with mixed shapes.

Renames the working title from \"BubbleUp\" (Honeycomb trademark concern)
to \"Paired Histograms\" — folder, files, components, story titles, and
design notes. Branch name and worktree path kept to avoid orphaning
upstream refs; one historical breadcrumb left in the design notes.

Tracked in DRC-3389 (data-pull strategy) and DRC-3390 (live prototype).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…stograms

Backend task that produces per-column paired distribution data for the
schema-view inline-profile experience (DRC-3390). Probes COUNT(DISTINCT)
per column then dispatches to a topk-style query (low cardinality) or a
uniform-bin histogram (high cardinality). Handles single-sided columns
(added/removed) by zero-filling the absent side. Returns a polymorphic
{kind: "topk" | "histogram", ...} per column.

Wired into RunType.PROFILE_DISTRIBUTION + adapter dispatch + run_func
routing so the frontend can submit this run type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Move PairedHistogramDiscreteCell + PairedHistogramContinuousCell from
storybook stories into js/packages/ui/src/components/data so they can
be consumed by the schema view and gallery card. Re-exported from
primitives + data/index. Storybook stories rewired to import from the
package boundary.

Frontend types: add the polymorphic ColumnDistribution shape on run
results + register profile_distribution in the run-type registry to
match the backend's RunType.

Cells include the visual treatment iterations from this branch:
  - Discrete: paired side-by-side bars per slot, gap-on-absent
  - Continuous: stacked overlap with a 2x2 orange/blue checkerboard
    agreement zone (replacing the prior taupe "shmellow" fill)
  - Per-bar native <title> tooltips, format:
      <value> [base: P%, current: P%]
  - useId()-derived pattern IDs so SVG defs don't collide between
    sibling histogram instances

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…le to Compact/Grid

Wires the per-column paired-histogram distribution into the schema
view's inline-profile experience and trims the render-mode toggle to
two options.

useInlineProfile now fires a parallel profile_distribution run alongside
the existing profile_diff. Independent error paths so a failure on one
side doesn't blank the other.

Schema view (Compact mode):
  - Distribution column appears whenever we have data or a fetch is
    in-flight (spinner per column while loading).
  - Wide mode is gone (toggle simplified to Compact | Grid). Stored
    value of "wide" in localStorage falls back to "grid" silently.
  - 5-light "Strip" cell renderer + ProfileStripCard popover removed
    (and its test file deleted). The strip indicator was conveying
    confusing data state (grey squares ≠ "we have data"); the
    Distribution column carries the visual signal more honestly.

Schema gallery view: distribution chart now renders inside each card's
chart slot using the real backend data (no synthetic fallback).

CSS: .schema-column-profile-distribution strips ag-grid's default
horizontal padding + line-height so the 140px SVG fills the cell
width and centers vertically (previously the chart was clipped and
sat low in the row).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
HTML prototypes used to pick the continuous-overlap visual treatment:
  - continuous-overlap-variants.html: six variants (shmellow, hatch,
    stipple, paired thin bars, alpha-blend, outline-on-solid) across
    four divergence scenarios.
  - stipple-variants.html: seven stipple/checkerboard fills with
    big-swatch previews. The 2x2 orange/blue checker (S2) was picked
    and shipped as the continuous-cell agreement zone.

Capture screenshots: phase-1 schema renders + phase-2 real-data and
pending-state snapshots, plus the storybook companion captures.

Verify scripts (verify-*.mjs): headless playwright harnesses that
drove the storybook captures. Saved alongside the screenshots so the
capture flow is reproducible. (Lint errors in these scripts are
pre-existing and unrelated to the source-of-truth code.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
The lineage Code pane silently rendered only the current SQL with no
red/green diff highlights or deletion gutter. Two copies of
@codemirror/view (6.40.0 and 6.41.0) were resolving in pnpm, causing
@codemirror/merge's unifiedMergeView ViewPlugin to register against a
different EditorView class than the one DiffEditor instantiated.
CodeMirror 6 no-ops in this case — no error, no decorations.

Pin @codemirror/view to 6.41.0 in pnpm.overrides so a single copy is
resolved across the tree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
danyelf added a commit that referenced this pull request May 21, 2026
… (DRC-3390 PR 3)

PR 3 of the paired-histograms GA productionization. Adds the frontend
cells, hook, schema-row integration, and unsupported-adapter banner
that consume the per-column payloads PR 2 will produce. Stacks on PR 1
(#1389) which already shipped RunType.PROFILE_DISTRIBUTION + the
discriminated-union plumbing.

Includes:

- PairedHistogramContinuous — quantile-bin, constant-area paired bar
  chart. Heights = density, widths = bin span, so bar area reads as
  the proportion of rows in that bin. Replaces the prototype's
  uniform-bin renderer (closed PR #1380).
- PairedHistogramDiscrete — top-K paired chart with gap-on-absent
  semantics. When a value is missing on one side, that bar isn't drawn,
  leaving a visible empty half-slot that reads as 'absent here'.
- useInlineProfileDistribution — TanStack-Query-backed hook that fires
  a single PROFILE_DISTRIBUTION run per model, parses the discriminated-
  union payload, and returns {distributions, loading, error,
  isUnsupported}. Emits Amplitude analytics events at request and
  result (PostHog wiring noted in DRC-3390 but the codebase uses
  Amplitude — same trail).
- InlineProfileDistributionCell — Compact-mode wrapper that picks
  between loading / error / empty (per-column failure) / continuous /
  discrete renders in a fixed-size slot, so adjacent rows don't reflow.
  Grid mode is intentionally absent at GA (per DRC-3390 lock).
- ProfileDistributionUnsupportedBanner — once-per-task MUI Alert for
  the {status: 'unsupported', reason} envelope (Postgres / MySQL /
  SQLite / SQL Server pre-2022).

Payload contract (frozen — PR 2 produces exactly this):
- Continuous: {kind:'histogram', bin_edges:[12], base_density:[11],
  current_density:[11], base_total, current_total}
- Categorical: {kind:'topk', values, base_counts, current_counts,
  base_total, current_total, trimmed}
- Per-column failure: {kind:null}
- Unsupported envelope: {status:'unsupported', reason:...}

Refines the ProfileDistributionResult type in api/types/run.ts from
the PR 1 stub (Record<string, unknown>) to the full discriminated
union — PR 1 left this as a placeholder for PR 2/3.

Tests + storybook:
- 57 unit tests across 5 files covering both cells, the integration
  wrapper, the banner, and the hook (mocked at the submit boundary so
  PR 2 doesn't need to be merged).
- 20 storybook stories covering both cells (cell + card density,
  light + dark, gap-on-absent, trimmed, empty payload, degenerate
  range) and all four integration-cell states; verified visually on
  port 6011.
- pnpm type:check clean; remaining errors are pre-existing baseline
  (CSS module declarations, lineage hooks) and unrelated to this PR.

Stacks on #1389. Mocks PR 2's payload in tests; rebase onto main once
PR 2 lands. PR 4 (lineage pre-warm + GA flip) consumes the same hook +
cacheKeys.profileDistribution(model) cache key.

Refs DRC-3390.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
@danyelf danyelf closed this May 27, 2026
@danyelf
Copy link
Copy Markdown
Contributor Author

danyelf commented May 27, 2026

closed to be replaced by #1398 and following

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