Skip to content

Feature: Single Sandbox Resource Metrics#246

Merged
ben-fornefeld merged 94 commits intomainfrom
feature/single-sandbox-resource-metrics
Mar 12, 2026
Merged

Feature: Single Sandbox Resource Metrics#246
ben-fornefeld merged 94 commits intomainfrom
feature/single-sandbox-resource-metrics

Conversation

@ben-fornefeld
Copy link
Copy Markdown
Member

@ben-fornefeld ben-fornefeld commented Feb 25, 2026

Note

Medium Risk
Moderate risk: introduces a new monitoring route with substantial client-side charting/URL-state logic and changes sandbox details polling behavior, which could affect dashboard performance and data freshness if mis-tuned.

Overview
Adds a new single-sandbox Monitoring view under /dashboard/.../sandboxes/[sandboxId]/monitoring that charts CPU/RAM/Disk utilization over a selectable timeframe (presets + custom range), supports brush-zoom with reset, and overlays lifecycle event markers plus paused-interval line breaks.

Updates sandbox details state management to derive and expose a sandboxLifecycle model (created/paused/ended + events), switches sandbox details polling to an aligned interval that continues briefly after kill until the killed event is observed, and adjusts header/logs timestamps to use the lifecycle-derived fields. Removes the configurable refresh/polling cookie + API route and replaces header “usage” widgets with static CPU/memory/disk spec items.

Adds extensive unit tests for timeframe parsing/normalization, lifecycle derivation, chart-model generation (including paused gaps and markers), and a dev-only E2B stress test to generate lifecycle events; bumps e2b to ^2.14.0 (lockfile updates included).

Written by Cursor Bugbot for commit 12baba7. This will update automatically on new commits. Configure here.

- Removed redundant wrapper in SandboxDetailsHeader for SandboxDetailsTitle.
- Updated DashboardLayoutHeader to ensure consistent rendering of ThemeSwitcher.
- Refactored logs handling in VirtualizedLogsBody to utilize state for scroll container, enhancing performance and readability.
- Adjusted event listeners for scroll handling to improve responsiveness.
Comment thread src/features/dashboard/sandbox/context.tsx

chart.group = SANDBOX_MONITORING_CHART_GROUP
echarts.connect(SANDBOX_MONITORING_CHART_GROUP)
}, [isMobile])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Brush config set once, never updates on resize

Low Severity

handleChartReady dispatches the brush action based on isMobile, but onChartReady is only invoked once by echarts-for-react on mount. Although isMobile is in the dependency array of useCallback, the new callback reference won't be called again when isMobile changes. The key prop is resolvedTheme, not isMobile, so a viewport change from desktop to mobile (or vice versa) leaves the brush configuration stale — brush/zoom stays enabled on mobile or stays disabled on desktop.

Fix in Cursor Fix in Web

const end = urlParams.end ?? now

return { start, end }
}, [urlParams.start, urlParams.end, activePresetId, lifecycleBounds])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sandbox context derives lifecycle but never updates polling on preset changes

Medium Severity

The timeframe memo depends on urlParams.start, urlParams.end, activePresetId, and lifecycleBounds. When a preset is active and lifecycleBounds changes (e.g., sandbox goes from running to killed), preset.getValue() is called which returns a new timeframe. However, the setPreset callback on line 145 writes both preset and start/end to URL params, so on the next render the timeframe memo will see non-null urlParams.start/urlParams.end and a non-null activePresetId — the preset branch wins, which is correct. But the isPolling check on line 92 uses timeframe.end compared to Date.now(), meaning if the preset produces a stale end value (e.g. lifecycle settled), polling may incorrectly continue or stop.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

invalid

isPolling,
lifecycleBounds,
setUrlParams,
])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Live polling fetches data but never advances visible timeframe

Medium Severity

When a user zooms into a chart (which sets activePresetId to null via setCustomTimeframe), the live polling tick effect exits early on line 162 (!activePresetId), so the URL timeframe stops advancing. However, isPolling on line 87-90 can still be true (it doesn't depend on activePresetId), so the React Query refetchInterval on line 228 keeps re-fetching data for the same frozen fetchTimeframe. This means the chart appears stuck while unnecessary network requests continue in the background. Either isPolling needs to account for the absence of an active preset, or the tick effect needs to handle custom timeframes as well.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

invalid

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

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.

!isLifecycleSettled &&
(lifecycleBounds?.isRunning ?? false) &&
timeframe.end + SANDBOX_MONITORING_OVERFETCH_MIN_MS >= Date.now()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Live polling condition may keep polling indefinitely for non-killed ended sandboxes

Medium Severity

The isPolling flag relies on isLifecycleSettled which requires hasKilledEvent to be true when the state is killed. But lifecycleBounds?.isRunning must also be true for polling to occur. For a sandbox that's killed but awaiting the killed event, isRunning would be false (since getSandboxLifecycleBounds likely returns isRunning: false for killed state), so isPolling would be false anyway, making the isLifecycleSettled check partially redundant for the metrics polling path. The sandbox context polling (lines 94-104 in context.tsx) separately handles this case, but the metrics controller polling may not fire when the killed event is still pending.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

invalid

@ben-fornefeld ben-fornefeld merged commit 2aaa3c9 into main Mar 12, 2026
11 checks passed
@ben-fornefeld ben-fornefeld deleted the feature/single-sandbox-resource-metrics branch March 12, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants