FE-591: Add basic Metrics with native timeline views#8633
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Collaborator
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
4 tasks
9033e93 to
7e958f5
Compare
ea7cc42 to
332c07d
Compare
5fe5a8a to
265f689
Compare
Introduces user-authored metric functions that run over each simulation frame and plot a single number on the timeline chart. - Schema: Metric type on SDCPN with Zod validation; round-trips via the file format (legacy files default to an empty list). - Compiler: compileMetric mirrors compileScenario's hardening (strict mode, shadowed globals, frozen prototype-less state), plus a buildMetricState helper that reshapes a frame's buffer into named tokens per place. - Timeline: metric picker in the header (Default + each metric) with Create/Edit/Manage IconButtons matching the scenarios picker pattern. When a metric is picked, useStreamingData streams a single series computed per frame; runtime errors render as NaN gaps, compile errors replace the chart with a message. - Drawers: CreateMetricDrawer and ViewMetricDrawer with a multi-line CodeEditor for the function body and live compile preview in the footer. Metrics CRUD added to MutationContext under the scenarioMutate guard so edits work in simulate mode. - SimulateView: Metrics tab enabled with a MetricList; sidebar mode lifted to EditorContext so the Manage button can open the tab. - Example: SIR model gains an "Infected Fraction" metric.
- Add a "Metric" label and widen the timeline picker to match the Scenario picker in simulation settings. - Truncate Select dropdown labels with ellipsis on overflow instead of wrapping, by applying min-width:0 on items and an ItemText style that clips the default label. - Dedent the SIR "Infected Fraction" example code so it renders at column 0 in the editor.
Introduces a second built-in view alongside the existing per-place
breakdown, aggregating token counts by color type. Places with no color
are collapsed into a single "Untyped" series.
- Replaces `timelineMetricId` with a discriminated `TimelineView` union
so native views and user metrics share one state field.
- Refactors useStreamingData to drive all three modes off a single
`{ series, extract }` config, unifying the frame loop.
- Picker now shows "Tokens per place", "Tokens per type", then each
metric; the Edit icon is only relevant for user-defined metrics.
- Legend is gated on series count (>= 2) rather than mode, so a
single-series per-type run also hides it.
Shows per-transition firing activity as a smoothed time-series: - Cumulative firingCount is interpolated between firings using timeSinceLastFiringMs and the last observed inter-firing interval, producing a continuous piecewise-linear ramp instead of a step function. - A 4 s trailing-window delta converts the interpolated cumulative into a local firing rate. - An EWMA low-pass (α = 0.15) on the output damps residual stochastic noise from variable inter-firing intervals. - All per-transition state (cumulative histories, interval estimates, EWMA accumulators) resets automatically on simulation restart via time-regression detection.
332c07d to
0ae8dcc
Compare
- Lift the .constructor-chain block (`runSandboxed`) and shadowed-globals
list to a shared `simulation/sandbox.ts` and apply them to compileMetric
so it matches compileScenario's hardening. Adds a regression test that
`({}).constructor.constructor("…")()` is rejected.
- Reject empty metric code via a form-level validator so the Save button
surfaces "Metric code is required" instead of letting the form save a
metric that compileMetric would later reject at runtime.
- Gate ViewMetricDrawer's `open` on the selected metric still existing —
switching the timeline picker while the edit drawer is open no longer
leaves an empty overlay behind.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
alex-e-leon
previously approved these changes
Apr 27, 2026
`summarizeMetricLspErrors` previously matched any URI under the global `_temp/metrics/` prefix, so a sibling drawer's session (e.g. a Create drawer still mounted during a View drawer's open animation) could surface diagnostics that block Save in the wrong drawer. The helper now requires a sessionId and filters on the per-session prefix instead. To plumb the ID, `useMetricLspSession` is now called in the drawer parents (CreateMetricDrawer, ViewMetricContent) rather than inside MetricFormBody, and the session ID flows to both the body (for the editor `path`) and the footer (for the diagnostics summary). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Could not find data on the previous version of this PR; see action logs at https://github.com/hashintel/hash/actions/runs/25010681726
alex-e-leon
approved these changes
Apr 27, 2026
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d373a64. Configure here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


🌟 What is the purpose of this PR?
Adds a first iteration of Metrics — user-authored functions that run over each simulation frame and plot a number on the timeline chart. The model and authoring surface are intentionally simple: this format is expected to change once Stochastic Simulation / Experiments and the Intermediate Representation land, at which point metrics will be re-expressed against the IR rather than raw place state. This iteration unblocks UI and product feedback in the meantime.
Alongside metrics, the timeline gets two built-in views ("Tokens per type", "Transition firings") so the chart is useful out of the box, and the metric code editor is wired to a temporary LSP session for live type-checking.
🔗 Related links
🔍 What does this change?
Metrictype onSDCPN(Zod-validated) with file-format round-tripping; legacy files default to an empty list.compileMetricmirrorscompileScenario's hardening (strict mode, shadowed globals, frozen prototype-less state).buildMetricStatereshapes a frame buffer into named tokens per place so author code can read e.g.state.places.Infected.count.TimelineViewunion so native views and user metrics share one state field.useStreamingDatadrives all three modes off a single{ series, extract }config.firingCountbetween firings with a 4 s trailing-window delta and a small EWMA, producing a continuous rate instead of a step function; per-transition state resets on simulation restart.CreateMetricDrawer/ViewMetricDrawerwith a multi-line code editor, live compile preview, and CRUD wired intoMutationContextunder thescenarioMutateguard so edits work in simulate mode._temp/metrics/{sessionId}/code.tsvirtual file wrapped asfunction __metric(state: MetricState): number { … }.MetricStateis generated from the current SDCPN so place names and colored token shapes are type-checked. The drawer footer surfaces LSP diagnostics alongside form/compile errors and gates Save until they're resolved.MetricList; sidebar mode lifted toEditorContextso the Manage button can open the tab.state.places(no defensive optional chaining).Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
📜 Does this require a change to the docs?
🕸️ Does this require a change to the Turbo Graph?
Metricshape (raw JS body overstate.places) is provisional and will be re-expressed once Stochastic Simulation / Experiments and the Intermediate Representation are in place.🐾 Next steps
🛡 What tests cover this?
compileMetric(hardening, NaN / non-finite rejection, shadowed globals).useStreamingDatarefactor.❓ How to test this?
state.places.Infectshould autocomplete; introducing a type error should disable Save and show a red diagnostic in the footer.