Skip to content

FE-1547: Isolate the contour and sweep components with dedicated stories - #9421

Draft
kube wants to merge 5 commits into
cf/fe-1549-randomize-sweep-parameter-draws-with-a-seed-derived-shiftfrom
cf/fe-1547-isolate-the-contour-and-sweep-components-with-dedicated
Draft

FE-1547: Isolate the contour and sweep components with dedicated stories#9421
kube wants to merge 5 commits into
cf/fe-1549-randomize-sweep-parameter-draws-with-a-seed-derived-shiftfrom
cf/fe-1547-isolate-the-contour-and-sweep-components-with-dedicated

Conversation

@kube

@kube kube commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Isolates the contour plot and the sweep navigator as self-contained components, so each state and flow (streaming fill, markers, range/point selection, saturation) can be exercised in Storybook against fake providers, without a running optimizer. Stacked on #9419.

🔗 Related links

🔍 What does this change?

  • Adds ContourSurface (src/ui/components/contour-surface.tsx): a purely presentational filled-contour canvas. Callers hand it grid-indexed values and optional ring markers; clicks come back as fractions of the plot area. It knows nothing about parameters, experiments, or optimizations.
  • SweepSurface and OptimizationSurface now render ContourSurface instead of owning their own canvas, raster, and paint effect; the optimization surface projects its trials into ContourSurfaceMarkers (best trial emphasized) during render.
  • Moves the distribution mean/median readout into a shared distributionStats module (src/react/experiments/distribution-stats.ts) with unit tests, replacing the optimization surface's local copy.
  • Decouples SweepNavigator from ExperimentsContext: it now takes axes, selection, status, and onSelectionChange as props, and the experiment drawer wires the context in.
  • Adds dedicated stories with fake providers: ContourSurface (empty, sparse, dense, markers, timer-driven streaming, click reporting), SweepNavigator (ranges, points, mixed, sampling, refining, saturated — all with live sliders), SweepSurface (streaming, slow sampling, no data), and OptimizationSurface (no trials, streaming trials, complete, log-scale, many parameters) over a validated supply-chain study manifest whose fake trials and fake local compute share one synthetic objective, so trial rings land on the contour.
  • FakeExperimentsProvider accepts per-story overrides for sampleSweepCell and sampleDetachedObjective.
  • Adds CPU- and GPU-paced variants of the OptimizationSurface stories: the fake local compute resolves batches at CPU-like cost (scaling with runs) or at near-constant GPU-like cost, to exercise both streaming profiles (the GPU pace previews the planned WebGPU path for point batches).
  • Adds a SweepNavigator story wired to a fake sweep session over the SIR Seasonal Flu scenario: an "Infected" metric timeline streams below the sliders, a committed move restarts the stream for the new selection, and a range selection widens the run distribution the way per-run parameter draws do.
  • Adds dedicated ExperimentMetricTimeline stories: frames streaming in one at a time, whole distributions re-arriving sharper at each refinement rung, a complete run, and the empty state — backed by a deterministic SIR-ish frame generator (sirInfectedFrame) in the story fixtures.
  • Adds real-compute SweepNavigator stories, "Real compute on CPU" and "Real compute on GPU (points)": the navigator runs against the real ExperimentsProvider, simulating a genuine sweep in browser workers over a story-only SIR scenario whose swept parameters feed the transition rates through parameterOverrides (the stock scenarios' parameters only shape the initial marking, which a range holds at its midpoint). The GPU story shows the real backend routing — range selections fall back to the CPU pool (per-run parameter draws cannot run on the GPU) and collapsing both parameters to points hands the batches to the GPU.
  • The real-compute stories take Storybook controls for runCount (up to 100 000 — the refinement ladder already climbs ×5/×2 past 1000), maxTime, and dt; changing one restarts the experiment with the new settings.
  • ExperimentMetricTimeline accepts a timeDomain to pin its x axis (the experiment drawer pins [0, maxTime], so charts no longer rescale while frames stream in or after a selection change) and a label that keeps its full-size shell — header, fixed-height plot area with a "Waiting for metric data" overlay, footer — while no frames exist, so streams and re-streams cause no layout shift.
  • Sweep navigation commits live: a point selection renders a single-thumb Slider (coincident RangeSlider thumbs trapped the drag on the upper one, which could not move left), and slider moves commit per quantized step during the drag instead of on release, so compute follows the thumb. Fixes the ds Slider treating value 0 as unset.
  • Re-streams keep the chart's frame of reference: the timeline remembers that a metric had data (axes and grid stay rendered through the empty moment, "Waiting for metric data" only shows before the first data ever), remembers the last output type (footer controls stop flickering to the scalar defaults), and its y ceiling only ratchets up — never below the maximum a previous run reached in the same view.
  • Fixes useElementSize to follow an element that mounts after the first render: it observed only what the ref held on mount, so a chart mounting once its first frame arrived stayed permanently blank.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • modifies an npm-publishable library and I have added a changeset file(s)

📜 Does this require a change to the docs?

The changes in this PR:

  • are internal and do not require a docs change

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

🐾 Next steps

  • FE-1548 reorganizes the experiment and optimization drawers so the surfaces sit above the event lists and only the lists scroll.

🛡 What tests cover this?

  • New: distribution-stats.test.ts (mean/median/empty-frame cases).
  • The metric-timeline, navigator-with-metrics, and pacing stories were verified live in Storybook (streaming, restream on selection change, distribution widening/sharpening).
  • Existing: sweep-session.test.ts and parameter-grid.test.ts cover the sampling the surfaces feed on; the full petrinaut unit suite passes.
  • The stories themselves are the manual harness for the visual states.

❓ How to test this?

  1. Run yarn workspace @hashintel/petrinaut dev (or yarn dev:petrinaut-optimization --storybook).
  2. Open "Components / ContourSurface" and watch the Streaming story fill in coarse-to-fine; click the Click-to-navigate story and confirm the fractions readout.
  3. Open "Simulate / SweepNavigator" and drag the sliders, toggle Range/Point.
  4. Open "Simulate / OptimizationSurface / Complete study" and confirm the contour fills from fake local compute with amber trial rings, the best ring emphasized, and the readout streaming mean/median.

📹 Demo

(screenshots to be attached)

🤖 Generated with Claude Code

@kube kube self-assigned this Aug 28, 2026
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview Aug 30, 2026 1:22pm
hashdotdesign-tokens Ready Ready Preview Aug 30, 2026 1:22pm
petrinaut Ready Ready Preview Aug 30, 2026 1:22pm
petrinaut-docs Ready Ready Preview Aug 30, 2026 1:22pm

Request Review

@github-actions github-actions Bot added area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/apps > hash.design Affects the `hash.design` design site (app) labels Aug 28, 2026
@kube
kube force-pushed the cf/fe-1547-isolate-the-contour-and-sweep-components-with-dedicated branch from 17c06dd to ac0dd0f Compare August 29, 2026 02:43
@github-actions github-actions Bot added the type/eng > backend Owned by the @backend team label Aug 29, 2026
@kube
kube changed the base branch from cf/fe-1546-stream-range-sweeps-as-one-stochastic-simulation-over-the to cf/fe-1549-randomize-sweep-parameter-draws-with-a-seed-derived-shift August 29, 2026 02:47
@github-actions github-actions Bot removed the type/eng > backend Owned by the @backend team label Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash.design Affects the `hash.design` design site (app) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

1 participant