feat(ui): distinguish cancel-from-hold as "Failed (cancelled)"
CI / Lint + build + test (push) Successful in 1m33s
Release / release (push) Successful in 12m41s

Before, a run that failed, held for operator review, and was then
cancelled showed up on the tile and run header as plain "Cancelled"
with an idle-grey mood — indistinguishable from a mid-stage cancel of
a healthy run. That hides the actual failure from the dashboard.

Now: when State=Cancelled with FailedStage still set (the hold-cancel
signature the heartbeat handler already uses to pick reboot vs
cancel_stage), the badge reads "Failed (cancelled)" with a fail-
colored mood. Mid-stage cancels keep reading as plain "Cancelled".
This commit is contained in:
2026-04-20 18:54:04 -04:00
parent 73f727b4c1
commit 599fd156d0
2 changed files with 57 additions and 0 deletions
+15
View File
@@ -113,6 +113,9 @@ func tileStatus(r *model.Run) string {
case model.StateWaitingReboot:
return "Waiting for reboot"
}
if cancelledFromHold(r) {
return "Failed (cancelled)"
}
return string(r.State)
}
@@ -120,6 +123,9 @@ func tileMood(r *model.Run) string {
if r == nil {
return "idle"
}
if cancelledFromHold(r) {
return "fail"
}
switch r.State {
case model.StateCompleted:
return "pass"
@@ -131,6 +137,15 @@ func tileMood(r *model.Run) string {
return "active"
}
// cancelledFromHold is true when a FailedHolding run was later Cancelled
// by the operator (tracked by State=Cancelled with FailedStage still
// set — mid-stage cancels don't stamp FailedStage). These deserve a
// fail-colored tile because the run did fail; the cancel was just the
// operator choosing not to recover.
func cancelledFromHold(r *model.Run) bool {
return r != nil && r.State == model.StateCancelled && r.FailedStage != ""
}
func sshInvocation(keyPath, ip string) string {
if keyPath == "" {
return "ssh root@" + ip + " (hold key not yet recorded)"