close
Skip to content

add Releases tab: bandit VPS release-skew panel#52

Open
myleshorton wants to merge 1 commit intomainfrom
fisk/release-skew-panel
Open

add Releases tab: bandit VPS release-skew panel#52
myleshorton wants to merge 1 commit intomainfrom
fisk/release-skew-panel

Conversation

@myleshorton
Copy link
Copy Markdown
Contributor

Summary

Adds a Releases tab to the dashboard that visualizes the bandit VPS fleet's convergence state under the new central-update orchestration system.

  • Fleet target card: the value of bandit_vps_default_release_tag + whether bandit_vps_autoreplace_enabled is on.
  • Running / Converged / Lagging counters across the whole fleet.
  • Per-track rows: stacked bar where each segment is a (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-skew endpoint and the whole central-orchestration stack (hot-swap + autoreplace workers).

Testing

  • Merge lantern-cloud PR first
  • Visit #releases tab with no release-skew data (fresh deploy, bandit_vps_default_release_tag unset) — should show "autoreplace OFF" chip and (unset) target, all routes in a single unmatched bucket
  • Set bandit_vps_default_release_tag to the current fleet version — converged count should climb as the hot-swap worker populates current_release_tag per route
  • Bump the target; watch the "lagging" count rise, then fall as hot-swap converges. Lag-oldest timestamp should advance (if it doesn't, workers are stuck)

Notes

Style matches TracksOverview for consistency. No new dependencies. Same useVPSData-style polling pattern (60s refresh, preserves last payload across refreshes to avoid UI blanking).

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.
Copilot AI review requested due to automatic review settings April 22, 2026 19:09
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying lantern-dashboard with  Cloudflare Pages  Cloudflare Pages

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

View logs

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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-skew client types + fetch helper and a polling hook (useReleaseSkew) with “preserve last payload” behavior.
  • Implemented ReleaseSkewOverview UI: fleet summary cards + per-track stacked bars and “oldest lag” indicator.
  • Wired the new view into Dashboard as a #releases tab.

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.

Comment on lines +142 to +148
{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)}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +165
{buckets.map((b, i) => (
<span
key={i}
style={{
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +204
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`;
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
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.

2 participants