diff --git a/internal/unifiedresources/health.go b/internal/unifiedresources/health.go index ca20899f3..48eeff063 100644 --- a/internal/unifiedresources/health.go +++ b/internal/unifiedresources/health.go @@ -89,6 +89,13 @@ func EvaluateResourceHealth(resource Resource, alerts []ResourceHealthAlert, now switch strings.ToLower(strings.TrimSpace(string(resource.Status))) { case "online", "running", "ready", "healthy", "available", "active", "up": + // A positive provider status without an observation timestamp is not + // enough to prove current health. Some compatibility and synthetic + // producers do not populate SourceStatus, so keep this guard at the + // canonical verdict boundary rather than relying on that map alone. + if resource.LastSeen.IsZero() { + return healthWithReason(HealthUnknown, "telemetry_missing", "") + } return ResourceHealth{Verdict: HealthOK, Reasons: []ResourceHealthReason{}} case "warning", "degraded", "unhealthy", "pending": return healthWithReason(HealthAttention, "degraded", "") diff --git a/internal/unifiedresources/health_test.go b/internal/unifiedresources/health_test.go index 3898a94a6..c51de693e 100644 --- a/internal/unifiedresources/health_test.go +++ b/internal/unifiedresources/health_test.go @@ -59,6 +59,11 @@ func TestEvaluateResourceHealthPrecedenceAndVerdicts(t *testing.T) { resource: Resource{ID: "host-3", Type: ResourceTypeAgent, Status: StatusOnline, LastSeen: now}, want: HealthOK, }, + { + name: "positive status without telemetry cannot be healthy", + resource: Resource{ID: "host-missing", Type: ResourceTypeAgent, Status: StatusOnline}, + want: HealthUnknown, code: "telemetry_missing", + }, { name: "informational alert does not invent attention", resource: Resource{ID: "host-info", Type: ResourceTypeAgent, Status: StatusOnline, LastSeen: now},