Skip to content

Risk

The Risk tab ranks every workload in the cluster riskiest-first, on a 0–100 score where higher is worse. Zero-risk workloads are included too — the list is the full inventory, not just the problems, so an absent workload means “not collected”, never “fine”.

  • A severity summary band — counts by level across the cluster.
  • A ranked table, one row per workload, with a vertical score badge, a level pill, and severity-tinted chips naming the top reasons it scored what it did.
  • An expandable drill-down per row: every contributing factor, a root-cause evidence block, and an on-demand AI root-cause analysis panel.
Score Level
0–30 Low
31–60 Medium
61–80 High
81–100 Critical

The same four levels and colours are used on every other cluster tab, so a Critical here means the same thing as a Critical on Security.

59 rules, each contributing to the composite. They fall into recognisable families:

Family Examples What it catches
Runtime (wrs-rt-*) crashloop, OOM, restarts, pending, image pull, init failure, rollout stuck, availability What the cluster is doing right now.
Spec hygiene (wrs-spec-*) privileged, host access, run-as-root, :latest, no liveness, no readiness, single replica What the manifest asks for.
HPA (wrs-hpa-*) capacity vs demand, denominator mismatch, duplicate targeting, missing requests, sidecar dilution Autoscaling that cannot actually scale.
Right-sizing (wrs-rs-*) unset, under, no limit, BestEffort Requests that do not match reality.
CVE (wrs-cve-*) critical, high, medium, low The security axis, joined by workload.
History (wrs-hist-*) high change-failure rate, frequent rollback, slow MTTR Whether this workload has a track record of failing.
Churn (wrs-churn-*) high, medium Instability over the churn window.

Plus targeted rules for conditions that are individually diagnostic: CPU throttling, noisy neighbour, pool exhaustion, scaling ceiling, unschedulable pods, PDB blocks drain, rollout stall, liveness probe spiral, timeout budget vs observed, probe timeout vs latency, startup vs observed boot, shutdown grace vs observed, grace period vs in-flight requests, replica drain vs load, node selector vs matching nodes, network policy vs observed edges, single-node topology concentration, downstream attribution, change regression, and config-reference drift.

Each row expands into its factors. A factor carries a category — SECURITY, RELIABILITY, COST, OBSERVABILITY, GOVERNANCE, PERFORMANCE, CHANGE_DIFF, RUNTIME — a severity, a title and detail, its score contribution, and a recommendation.

Some factors carry extra structure where it is useful:

  • Right-sizing factors add proposed limit, observed peak, and QoS before/after, so you can see what the change would do to the workload’s QoS class before making it.
  • Network-policy factors add blocked edges — the other service, the direction, and the call count that would be severed.

Four flags ride the response, and they change what the score can mean:

Flag When false
cveScanAvailable No CVE scan has run. CVE rules contribute nothing.
metricsAvailable No usage metrics. Right-sizing signals excluded.
runtimeAvailable Cluster never synced. Runtime signals contribute nothing.
networkSignalsAvailable No network telemetry. Network signals excluded.

This is the part worth reading carefully, because the obvious arithmetic is wrong.

Counter What it means
Degraded The workload was scored, but at least one rule’s signal was suppressed. The score is real; one input is missing. These rows can land in any level.
Unevaluable Scoring failed entirely. The row is forced to score 0 / LOW as a placeholder — “Risk evaluation incomplete”, not an assessment.
Unmeasured The workload’s owned-pods read failed — a query error, not “genuinely no pods”. Carries a “Runtime health not measured” factor.

Beyond the current score, workloads can be evaluated against scenarios — MAINTENANCE (a node drain) and AZ_FAILURE (losing an availability zone) — answering “would this workload survive that?” rather than “is it healthy now?”

Expanding a row gives evidence; asking for an explanation gives a narrative.

22 collectors gather evidence independently — warning events, log tail, termination, recent changes, CPU throttle, memory-kill context, node saturation, metrics near failure, probe budget, HPA status, pod schedulability, app health, dependency edges, dependency latency, self time, hop self-time path, spans in flight, exceptions, HTTP status breakdown, retry structure, arrival shortfall, and timeout budget.

Which ones run depends on the symptom class: SLOW, ERRORS_5XX, RESTARTING, WONT_SCALE, STUCK_ROLLOUT, CANNOT_CONNECT, MEMORY_GROWTH. Collection runs under a time budget, so an answer arrives even when a dependency is slow.

The synthesis returns a summary, ranked causes, what changed in the pre-failure window, and a single recommended next action. Ranked causes carry a confidence of HIGH / MED / LOW and the evidence IDs they rest on.

Alongside it, a causal chain: an ordered ROOT_CAUSE → PROPAGATION → SYMPTOM narrative. That is “what happened” — the account the synthesis stands behind — as distinct from ranked causes, which are the residual alternatives. Every step cites evidence IDs, and every ID must exist in the emitted bundle. An empty chain is itself a signal: thin evidence, or a deterministic path.

The response states this explicitly, because the prose alone will not tell you:

  • llmUsed — false means the result is deterministic and gate-derived, not generated.
  • fromCache — an earlier synthesis replayed, at no new token cost.
  • gateReason — the deterministic decision point, always set.
  • modelId and token usage — present only when a model actually ran.

Every terminal report carries an accounting of the reasoning session: what was checked, what it could not see, and what it ruled out.

The invariant is the useful part: checked + could-not-see always equals the total number of evidence sources the session could have consulted. Every source lands in exactly one list — never omitted, never collapsed into a bare count. So an empty could-not-see list is a claim of complete visibility, and it has to be true.

ruledOut lists only causes with a fired, available refuting signal. A cause that was merely untested or whose signal was unavailable is indeterminate, not ruled out.

On a cache hit the ledger is still built fresh from the current call — it describes this call’s visibility, while fromCache describes the narrative.

RCA declines rather than guesses. With no trace data for a workload it says so, instead of reporting a 0% error rate that would read as healthy.

Terminal window
rtz risk # the same ranking, worst first
rtz risk workload payments/checkout # why this one scored what it did
rtz rca payments/checkout # the evidence block
rtz rca explain payments/checkout # the narrative

See CI gates for --fail-on.