v2.0.15 — oumta-dashboard: dot color follows cluster outcome

The per-cluster dot now reflects how the workload ended up after the
cluster's events, not the worst thing that happened inside it: if the
most recent state transition in the cluster was "-> healthy", the dot
turns green even when the cluster contains earlier degraded/warning
events. Falls back to the existing dominantSev logic for clusters
without any state transition (e.g. rollout-only clusters).

Matches the reality that a rollout which briefly degrades and then
recovers is a normal, healthy outcome — not a "warning" forever.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
tuandClaude Opus 4.7 committed 2026-04-19 14:25:38 +02:00
1 parent 0bf7137428
commit bd5a462a1e
2 files changed
+22 -2

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
<?php <?php
define('APP_NAME', 'hyprcrd'); define('APP_NAME', 'hyprcrd');
define('APP_VERSION', '2.0.14'); define('APP_VERSION', '2.0.15');
// APP_DEBUG: gates verbose error responses (curl traces, upstream bodies). // APP_DEBUG: gates verbose error responses (curl traces, upstream bodies).
// Off by default; enable via env for local troubleshooting only. // Off by default; enable via env for local troubleshooting only.
@@ -290,6 +290,26 @@ function dominantSev(events) {
return best || 'info'; return best || 'info';
} }
/** Scan the cluster (events are newest-first) for the most recent state
* transition and map it to a dot severity. This reflects how the
* workload ended up after the cluster, not the worst thing that
* happened inside it — so a cluster that ends in "-> healthy" shows a
* green dot even if it contains degraded/warning events along the way.
* Returns null when no state-change event is present; callers fall
* back to dominantSev in that case (e.g. rollout-only clusters). */
function clusterOutcomeSev(events) {
for (const e of events) {
const m = /->\s*(\w+)\s*$/.exec(e.message || '');
if (!m) continue;
switch (m[1]) {
case 'healthy': return 'recovered';
case 'degraded': return 'warning';
case 'unavailable': return 'error';
}
}
return null;
}
function sameDay(a, b) { function sameDay(a, b) {
const da = new Date(a), db = new Date(b); const da = new Date(a), db = new Date(b);
return da.getFullYear() === db.getFullYear() return da.getFullYear() === db.getFullYear()
@@ -336,7 +356,7 @@ function renderTimeline() {
let lastDate = null; let lastDate = null;
body.innerHTML = clusters.map(cluster => { body.innerHTML = clusters.map(cluster => {
const sev = dominantSev(cluster.events); const sev = clusterOutcomeSev(cluster.events) || dominantSev(cluster.events);
const showDate = !lastDate || !sameDay(lastDate, cluster.ts); const showDate = !lastDate || !sameDay(lastDate, cluster.ts);
lastDate = cluster.ts; lastDate = cluster.ts;
const d = new Date(cluster.ts); const d = new Date(cluster.ts);