add Releases tab: bandit VPS release-skew panel#52
Conversation
Renders the per-track convergence state of the bandit VPS fleet: fleet target, total running, converged vs. lagging counts, and per- track stacked bars colored by match/mismatch with the oldest lag timestamp annotated when a track is mid-rollout. Consumes GET /v1/dashboard/release-skew from the lantern-cloud API (paired with lantern-cloud PR #2630). Refreshes every 60s via a new useReleaseSkew hook that preserves the last payload across polls (same isFirst/hasLoaded pattern as useVPSData so the UI doesn't blank out between refreshes). See lantern-cloud docs/design/central-vps-updates.md "Observability" section for the underlying model.
Deploying lantern-dashboard with
|
| Latest commit: |
84d3d6a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://e2935c61.lantern-dashboard.pages.dev |
| Branch Preview URL: | https://fisk-release-skew-panel.lantern-dashboard.pages.dev |
There was a problem hiding this comment.
Pull request overview
Adds a new Releases dashboard tab that visualizes bandit VPS fleet release convergence (“release skew”) using a new polled /release-skew API, including fleet-wide counters and per-track stacked-bar breakdowns.
Changes:
- Added
/release-skewclient types + fetch helper and a polling hook (useReleaseSkew) with “preserve last payload” behavior. - Implemented
ReleaseSkewOverviewUI: fleet summary cards + per-track stacked bars and “oldest lag” indicator. - Wired the new view into
Dashboardas a#releasestab.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/hooks/useReleaseSkew.ts | New polling hook for release-skew data with preserved payload across refreshes. |
| src/components/ReleaseSkewOverview.tsx | New Releases tab UI: summary cards and per-track stacked bar visualization. |
| src/components/Dashboard.tsx | Adds “Releases” tab routing (hash + tab list) and mounts the new overview component. |
| src/api/client.ts | Adds release-skew response types and fetchReleaseSkew() API call. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {buckets.map((b, i) => { | ||
| const pct = totalRunning > 0 ? (b.routeCount / totalRunning) * 100 : 0; | ||
| const color = b.matched ? "#a0c8a0" : "#f0a030"; | ||
| return ( | ||
| <div | ||
| key={i} | ||
| title={bucketTooltip(b)} |
There was a problem hiding this comment.
Using the bucket index (key={i}) as the React key for stacked-bar segments can cause incorrect DOM reuse when bucket ordering changes between polls (e.g., tooltips/colors appearing on the wrong segment). Prefer a stable key derived from the bucket identity, such as ${b.currentTag}→${b.targetTag} (and possibly matched).
| {buckets.map((b, i) => ( | ||
| <span | ||
| key={i} | ||
| style={{ |
There was a problem hiding this comment.
Using key={i} for the per-bucket legend entries has the same React key stability issue as the stacked-bar segments: if buckets re-order between refreshes, labels/counts can appear to “swap” unexpectedly. Use a stable key based on the bucket’s target/current tags instead of the index.
| const deltaMs = Date.now() - then; | ||
| const minutes = Math.floor(deltaMs / 60000); | ||
| if (minutes < 60) return `${minutes}m`; | ||
| const hours = Math.floor(minutes / 60); | ||
| if (hours < 48) return `${hours}h`; | ||
| return `${Math.floor(hours / 24)}d`; |
There was a problem hiding this comment.
formatAge can return negative values (e.g. "-5m") if lagOldest is in the future due to clock skew or backend timestamps. Consider clamping deltaMs to a minimum of 0 (or handling future timestamps explicitly) so the UI never shows a negative age.
Summary
Adds a Releases tab to the dashboard that visualizes the bandit VPS fleet's convergence state under the new central-update orchestration system.
bandit_vps_default_release_tag+ whetherbandit_vps_autoreplace_enabledis on.(current_tag, target_tag)bucket, green if matched (converged) and amber if mismatched. Each track shows its per-track override (if any) vs. the fleet default, plus an oldest lag badge when there's any lagging bucket — that's the "rollout stopped" signal from the design doc.Paired with lantern-cloud PR #2630 which adds the backing
/v1/dashboard/release-skewendpoint and the whole central-orchestration stack (hot-swap + autoreplace workers).Testing
#releasestab with no release-skew data (fresh deploy,bandit_vps_default_release_tagunset) — should show "autoreplace OFF" chip and(unset)target, all routes in a single unmatched bucketbandit_vps_default_release_tagto the current fleet version — converged count should climb as the hot-swap worker populatescurrent_release_tagper routeNotes
Style matches TracksOverview for consistency. No new dependencies. Same
useVPSData-style polling pattern (60s refresh, preserves last payload across refreshes to avoid UI blanking).