From b1ba43e12a48246610d2fcb34a551804579951db Mon Sep 17 00:00:00 2001 From: Ryan Curran Date: Fri, 25 Sep 2026 11:33:19 -0400 Subject: [PATCH] overview: show each fleet number once Running and pending live only in Fleet Load now. The right-now headline leads with whose work the fleet is doing, Kit compares against the usual instead of repeating counts, and the Total workers card is gone since it disagreed with the dots on how many Macs there are. --- frontend/src/components/FleetRightNow.tsx | 39 ++++++---- frontend/src/lib/kit.ts | 14 ++-- frontend/src/pages/Overview.tsx | 91 +---------------------- 3 files changed, 38 insertions(+), 106 deletions(-) diff --git a/frontend/src/components/FleetRightNow.tsx b/frontend/src/components/FleetRightNow.tsx index 86e42dd..88b3e4a 100644 --- a/frontend/src/components/FleetRightNow.tsx +++ b/frontend/src/components/FleetRightNow.tsx @@ -48,10 +48,11 @@ function Constellation({ data, flips }: { data: RightNowData; flips: Set }).filter(c => c.dots.length > 0); const n = data.now; - const pct = n.machines ? Math.round((n.running / n.machines) * 100) : 0; const running = data.projects.running; const totalRunning = Object.values(running).reduce((s, v) => s + v, 0) || 1; - const topProject = order.reduce((a, b) => (running[b] > running[a] ? b : a), order[0]); + const share = (k: RightNowProject) => (running[k] / totalRunning) * 100; + const ranked = [...order].sort((a, b) => running[b] - running[a]); + const [topProject, runnerUp] = ranked; return (
@@ -61,14 +62,24 @@ function Constellation({ data, flips }: { data: RightNowData; flips: Set
Right now
+ {/* The Fleet Load card above owns the running/pending counts; this panel's + headline says the one thing only it knows: whose work the fleet is doing. */}

- - Math.round(v).toLocaleString()} /> - {" "} - of {n.machines.toLocaleString()} machines are building and testing Firefox - ({pct}%), with{" "} - Math.round(v).toLocaleString()} />{" "} - tasks waiting in line. + {running[topProject] > 0 ? ( + <> + {data.projects.labels[topProject]} is the fleet's biggest customer, with{" "} + + `${Math.round(v)}%`} /> + {" "} + of running work + {runnerUp && running[runnerUp] > 0 && ( + , ahead of {data.projects.labels[runnerUp]} at {Math.round(share(runnerUp))}% + )} + . + + ) : ( + No Firefox work running right now. + )}

@@ -118,12 +129,12 @@ function Constellation({ data, flips }: { data: RightNowData; flips: Set {/* whose work it is */}
+
+
Math.round(v).toLocaleString()} />
+
machines
+
Whose work
-

- {data.projects.labels[topProject]} is the biggest customer right now, with{" "} - {Math.round((running[topProject] / totalRunning) * 100)}% of running tasks. -

-
+
{[...order, "other" as const].map(k => { const v = running[k]; const color = k === "other" ? OTHER_WORK : PROJECT_COLOR[k]; diff --git a/frontend/src/lib/kit.ts b/frontend/src/lib/kit.ts index f3ac492..50841fa 100644 --- a/frontend/src/lib/kit.ts +++ b/frontend/src/lib/kit.ts @@ -60,10 +60,14 @@ function median(xs: number[]): number { } const n = (v: number) => Math.round(v).toLocaleString(); +/** Percent difference between a and b, relative to `base` (defaults to b). */ +const pct = (a: number, b: number, base = b) => Math.round(((a - b) / base) * 100); /** How Kit reads the fleet right now. Fleet-wide pending is never small (a couple of * pools are always backed up), so every judgement is against this fleet's own last - * 48 hours rather than a fixed number. Checked in priority order: the first match wins. */ + * 48 hours rather than a fixed number. Checked in priority order: the first match wins. + * The Fleet Load card already shows the running/pending counts, so Kit never repeats + * them; it says how today compares instead. */ export function kitMood(r: FleetReading): KitMood { if (r.shipsToday) return { pose: "proud", says: `Firefox ${r.shipsToday} ships today.` }; if (r.pending == null || r.running == null) return { pose: r.idlePose, says: "Checking on the fleet…" }; @@ -73,16 +77,16 @@ export function kitMood(r: FleetReading): KitMood { const usualRunning = median(r.history.map(h => h.running)); if (enoughHistory && r.pending >= 500 && r.pending >= usualPending * 1.5) { - return { pose: "alert", says: `The queue's long: ${n(r.pending)} waiting, ${(r.pending / usualPending).toFixed(1)}× the usual.` }; + return { pose: "alert", says: `The queue's ${(r.pending / usualPending).toFixed(1)}× its usual length.` }; } if (r.macAttention >= 10 && r.macTotal > 0 && r.macAttention / r.macTotal >= 0.05) { return { pose: "inquisitive", says: `${n(r.macAttention)} Macs need a look.` }; } if (enoughHistory && usualRunning > 0 && r.running >= usualRunning * 1.1) { - return { pose: "juggling", says: `Juggling ${n(r.running)} tasks, busier than usual.` }; + return { pose: "juggling", says: `Busier than usual: ${pct(r.running, usualRunning)}% above the last two days.` }; } if (enoughHistory && usualRunning > 0 && r.running <= usualRunning * 0.6) { - return { pose: "meditating", says: `Quiet hours. ${n(r.running)} tasks running.` }; + return { pose: "meditating", says: `Quiet hours: ${pct(usualRunning, r.running, usualRunning)}% below the usual.` }; } - return { pose: r.idlePose, says: `Keeping watch: ${n(r.running)} running, ${n(r.pending)} waiting.` }; + return { pose: r.idlePose, says: enoughHistory ? "All steady. Nothing unusual in the fleet." : "Keeping watch while the history fills in." }; } diff --git a/frontend/src/pages/Overview.tsx b/frontend/src/pages/Overview.tsx index 374bd9c..17228ba 100644 --- a/frontend/src/pages/Overview.tsx +++ b/frontend/src/pages/Overview.tsx @@ -21,7 +21,6 @@ const yardstickAll = `${YARDSTICK_BASE}&var-provisioner=$__all&var-workerType=$_ // health ring / sparkline accents and leaves emerald free for "healthy"/production. // Android gets its own iconic robot-green — it sits outside the gradient (its devices are // Taskcluster-only, not in the worker DB) and the label disambiguates it from health greens. -const PLATFORM_COLORS = { macOS: "#FF9400", Linux: "#FF1AD9", Windows: "#9059FF", Android: "#3DDC84" } as const; function timeAgo(iso: string | null, now = Date.now()) { if (!iso) return "never"; @@ -93,27 +92,6 @@ function Sparkline({ points, className = "w-full h-16" }: { points: number[]; cl } /** Fleet-health ring with a gradient arc; children render in the center. */ -function HealthRing({ pct, children }: { pct: number; children: React.ReactNode }) { - const r = 54, c = 2 * Math.PI * r, dash = (Math.max(0, Math.min(100, pct)) / 100) * c; - return ( -
- - - - - - - - - - - -
{children}
-
- ); -} - // ── Monitored pools (user-pinned; follows the IAP-authenticated user across devices) ── const MONITOR_KEY = "hangar.monitoredPools"; // legacy per-browser store, migrated once into the backend const MONITOR_MAX = 8; @@ -1105,19 +1083,12 @@ export function Overview() { const [data, setData] = useState(null); const [load, setLoad] = useState(null); const [scale, setScale] = useState(null); - // Android device pools are Taskcluster-only (no worker DB rows), so pull their live - // device total separately to fold into the platform breakdown below. - const [androidTotal, setAndroidTotal] = useState(null); const [error, setError] = useState(""); - const loadAndroid = () => - api.fleet.androidPools().then(d => setAndroidTotal(d.pools.reduce((s, p) => s + p.total, 0))); - useEffect(() => { api.fleet.summary().then(setData).catch(e => setError(e.message)); api.fleet.loadHistory(48).then(setLoad).catch(() => {}); api.fleet.showcase().then(d => setScale(d.scale)).catch(() => {}); - loadAndroid().catch(() => {}); }, []); // Keep the dashboard live: silently refresh everything while the tab is visible. @@ -1125,7 +1096,6 @@ export function Overview() { api.fleet.summary().then(setData).catch(() => {}); api.fleet.loadHistory(48).then(setLoad).catch(() => {}); api.fleet.showcase().then(d => setScale(d.scale)).catch(() => {}); - loadAndroid().catch(() => {}); }, 60_000); if (error) return
{error}
; @@ -1136,26 +1106,9 @@ export function Overview() {
); - const platforms = (() => { - let mac = 0, linux = 0, windows = 0; - Object.entries(data.by_pool).forEach(([name, count]) => { - if (name.includes("osx")) mac += count; - else if (name.includes("linux")) linux += count; - else if (name.includes("win") || name.includes("nuc")) windows += count; - }); - return [ - { name: "macOS" as const, value: mac, color: PLATFORM_COLORS.macOS }, - { name: "Linux" as const, value: linux, color: PLATFORM_COLORS.Linux }, - { name: "Windows" as const, value: windows, color: PLATFORM_COLORS.Windows }, - { name: "Android" as const, value: androidTotal ?? 0, color: PLATFORM_COLORS.Android }, - ].filter(d => d.value > 0); - })(); - const platformTotal = platforms.reduce((s, p) => s + p.value, 0) || 1; - - // Health ring is scoped to macOS hardware — the fleet RelOps actually owns — to cut noise. + // macOS hardware is the fleet RelOps actually owns; Kit reads it. const attention = data.attention_mac.quarantined + data.attention_mac.missing_from_tc; - const macTotal = platforms.find(p => p.name === "macOS")?.value ?? 0; - const healthPct = macTotal > 0 ? Math.round((1 - attention / macTotal) * 100) : 100; + const macTotal = Object.entries(data.by_pool).reduce((sum, [name, count]) => sum + (name.includes("osx") ? count : 0), 0); // Greeting (client-local time) const now = new Date(); @@ -1272,7 +1225,8 @@ export function Overview() {
- {/* The fleet right now — every machine, lit by whose work it's running */} + {/* The fleet right now — every machine, lit by whose work it's running, with + fleet size (this card is the one source for fleet counts) */} {/* Monitored pools — user-pinned */} @@ -1282,43 +1236,6 @@ export function Overview() { {pinned.try && togglePin("try")} />} {pinned.needinfo && togglePin("needinfo")} />} - {/* Total workers breakdown */} -
-
- - {healthPct}% - healthy - - -
-
-
-
-
Total workers
-
-
- -
-
- {platforms.map(p => ( -
- ))} -
-
- {platforms.map(p => ( -
- - {p.name} - {p.value.toLocaleString()} - {Math.round((p.value / platformTotal) * 100)}% -
- ))} -
-
-
-
-
-
); }