diff --git a/internal/web/templates/host_tile_templ.go b/internal/web/templates/host_tile_templ.go index 17e7434..0f4bc24 100644 --- a/internal/web/templates/host_tile_templ.go +++ b/internal/web/templates/host_tile_templ.go @@ -308,6 +308,9 @@ func tileStatus(r *model.Run) string { case model.StateWaitingReboot: return "Waiting for reboot" } + if cancelledFromHold(r) { + return "Failed (cancelled)" + } return string(r.State) } @@ -315,6 +318,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" @@ -326,6 +332,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)"