Skip to content

Say when rightsizing could not read a workload's pods - #1746

Merged
nadaverell merged 4 commits into
mainfrom
fix/rightsizing-live-inventory
Sep 14, 2026
Merged

nadaverell merged 4 commits into
mainfrom
fix/rightsizing-live-inventory

Conversation

@nadaverell

@nadaverell nadaverell commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1726.

Rightsizing read a workload's pods for two things — the pod names, and whether those pods have been OOM-killed — then discarded the error when the read failed. An empty answer was indistinguishable from a workload whose pods are healthy, so a recommendation could silently drop "these pods have been OOM-killed" and still present itself as complete.

Availability is decided per workload

The question is never "can Radar list pods" but "can Radar list this workload's pods". Three different failures answer no, and they are not alike:

  • A warming informer clears on its own within seconds, so reporting it would attach a caveat the next refresh removes. Both paths wait for it, bounded, and use the real pods. The single-workload read retries WorkloadPods; the scan waits once for the whole scan rather than per workload, and only for the informers it will actually read — a scan holding no Deployments never waits on the deferred ReplicaSet informer.
  • A denial never clears and is reported.
  • A namespace the informer does not watch is the subtle one. A synced, non-nil lister still answers only for its own namespaces, which is how Radar runs whenever cluster-wide list is denied. A successful empty list from an unwatched namespace proves nothing, so coverage is checked per workload — pods for every kind, and ReplicaSets as well for a Deployment, which reaches its pods only through them. A StatefulSet in the same scan is unaffected and stays unflagged.

Rows carry liveInventoryUnavailable — unavailable rather than denied, because an exhausted warming wait lands there too and is not a permission decision.

What the reader sees

The caveat qualifies whichever explanation applies rather than standing in for it: a row can both be unable to check its pods and have a limit conflict, and hiding either behind the other loses something. It survives the staged-reduction path too — the one suggestion that asks you to reduce in steps is exactly where an unchecked pod matters most.

When there is no retained ownership and the pods cannot be listed, the response says so directly instead of reporting an absence of samples. The selection falls back to the current pods and then matches nothing by construction, so "no samples available" would send the reader hunting for missing metrics rather than an unreadable inventory. That reason is set server-side, so every consumer gets it rather than one UI.

What does not change

The recommendation itself. Its safety gate is OOMEvidenceAvailable, computed from the Prometheus termination window, which never depended on the live pod read. liveInventoryUnavailable means currentPodOOM is unknown rather than false — a qualification, not a suppression.

A cancelled request stops rather than recording an unreadable inventory and continuing into Prometheus on a dead context.

Testing

Unit. Go tests drive the scan against a real ResourceCache over a fake clientset: pods readable, pod lister denied, ReplicaSet ownership unreadable (Deployments flagged, StatefulSets not), and a namespace-scoped ResourceScopes cache where one workload sits inside the watched namespace and one outside. A further test pins that a DaemonSet-only scan does not spend the warming budget on ReplicaSets. Frontend tests cover the caveat composing with a suppression reason and surviving a staged reduction.

go build ./..., go vet, go test ./internal/prometheus/ (including -race), tsc --noEmit, and the rightsizing suites pass.

Live, against a GKE cluster with real Prometheus and kube-state-metrics. Binaries built from this branch and from main were run against the same cluster and the same Prometheus, and their responses compared:

  • Single workload (Deployment staging/billing): identical — ownerCoverage: ksm_history, 2 rows, fit=oversized, matching OOM-evidence flags. liveInventoryUnavailable is absent, which is correct for a readable inventory.
  • Cluster-wide scan: 26 workloads discovered and evaluated on both, 25 with data. Across 52 rows, every decision field is identical — fit, recommendationReason, hpaManaged, hpaEvidenceAvailable, oomEvidenceAvailable, currentPodOOM, windowOomEvidence, limitConflict, confidence, reductionLimited, recommendedRequest. The only payload differences were measured values (observed, peak, throttle ratio, sample counts) drifting between two runs seconds apart.

That exercises the per-workload coverage check, the ReplicaSet ownership path and the warming waits against real informers.

Live, under a genuinely denied pod inventory. A throwaway ServiceAccount was granted everything rightsizing needs except pods — kubectl auth can-i list pods returned no, deployments and replicasets returned yes — and both builds were run under it against the same cluster:

main this branch
Single workload, currentPodOOM false false
Single workload, liveInventoryUnavailable field absent true
Cluster scan 100 workloads · 316 rows · 0 flagged 100 workloads · 316 rows · 316 flagged

On main the response is indistinguishable from a workload whose pods have simply never been OOM-killed. The recommendation is unchanged between the two builds — fit=oversized, same ownerCoverage: ksm_history, same row and workload counts — which is the point: the pod read was never what the recommendation rested on, only what the OOM check needed. The ServiceAccount, ClusterRole, ClusterRoleBinding and namespace were deleted afterwards and verified gone.

Visual-test skipped: the UI delta is copy inside existing components, no new surface.

Follow-up

The same class of bug exists on the HPA paths, where it is worse: HPAEvidenceAvailable gates whether a reduction is suppressed at all, so an uncovered namespace can let one through on a workload whose autoscaler was never seen. It is pre-existing and outside these lines, tracked in #1749.

Both rightsizing components predate the current Prettier config and are not formatted to it on main; this change matches each file's existing style rather than reformatting ~1,000 unrelated lines.

🤖 Generated with Claude Code

https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW

Rightsizing read the workload's pods for two things — the pod names and
whether those pods have been OOM-killed — and discarded the error if the read
failed. An empty answer then looked exactly like a workload whose pods are
healthy, so a recommendation could silently omit "these pods have been
OOM-killed" and present itself as complete.

Two cases reach that error and they are not alike. A warming informer clears
on its own within seconds, so reporting it would attach a caveat that the next
refresh removes; the read now waits for it, bounded, and gets the real pods. A
denial never clears, and is reported: rows carry liveInventoryDenied, and the
UI qualifies the suggestion rather than presenting a partial one as whole.

The cluster scan had the same hole by a different route — it lists pods
directly and returned early on a denied lister, and never checked informer
sync at all, so a warming cache read as a cluster with no OOM-killed pods
anywhere. It now waits once for the whole scan and marks every workload when
the pods stay unreadable.

The recommendation itself is unchanged in either case: its safety gate is
OOMEvidenceAvailable, computed from the Prometheus termination window, which
never depended on the live pod read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW
@nadaverell
nadaverell requested a review from hisco as a code owner September 13, 2026 20:27
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Report unavailable live pod inventory in rightsizing results

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Distinguishes unreadable pod inventories from workloads with no current OOM events.
• Waits briefly for warming pod informers before qualifying affected recommendations.
• Surfaces memory-specific caveats in workload and scan views, with regression coverage.
Diagram

graph TD
  A["Rightsizing flow"] --> B["Pod informer"] --> C{"Cache synced?"}
  C -->|No| D["3-second wait"] --> E{"Pods readable?"}
  C -->|Yes| E
  E -->|Yes| F["OOM enrichment"] --> G["API row"] --> H["UI explanation"]
  E -->|No| G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Suppress recommendations when inventory is unavailable
  • ➕ Avoids presenting any recommendation based on incomplete live state.
  • ➕ Provides a simpler fail-closed user experience.
  • ➖ Discards otherwise valid recommendations derived from independent Prometheus evidence.
  • ➖ Conflates unavailable current-pod context with an unsafe recommendation.
2. Fail the entire rightsizing request
  • ➕ Makes inventory failures explicit at the API boundary.
  • ➕ Avoids introducing a row-level availability field.
  • ➖ Reduces rightsizing availability for a nonessential enrichment failure.
  • ➖ Prevents unaffected evidence and recommendations from reaching users.

Recommendation: Keep the PR's row-level qualification approach. Current-pod OOM state is supplemental while the recommendation safety gate uses independent historical evidence, so preserving results and explicitly marking unknown inventory is more accurate than suppressing or failing them. The bounded, context-aware wait also prevents transient informer warming from producing unnecessary caveats.

Files changed (7) +183 / -20

Bug fix (5) +103 / -19
rightsizing.goTrack unreadable pod inventory for workload rightsizing +44/-12

Track unreadable pod inventory for workload rightsizing

• Adds a bounded, context-aware retry for warming pod caches and records when live pods remain unreadable. Propagates that state into each rightsizing API row without changing recommendation safety logic.

internal/prometheus/rightsizing.go

rightsizing_scan.goHandle unavailable pod inventory across rightsizing scans +41/-5

Handle unavailable pod inventory across rightsizing scans

• Waits once for pod informer synchronization before scan-wide OOM enrichment. If synchronization fails or the pod lister is unavailable, every affected workload is marked with unknown live inventory state.

internal/prometheus/rightsizing_scan.go

client.tsExpose live inventory availability in the API model +2/-0

Expose live inventory availability in the API model

• Extends RightsizingRow with the optional liveInventoryDenied field and documents that currentPodOOM is unknown when pod inventory cannot be read.

web/src/api/client.ts

RightsizingStrip.tsxAppend unreadable inventory caveats to explanations +9/-2

Append unreadable inventory caveats to explanations

• Adds a memory-specific explanation when live pod OOM state is unavailable. The caveat is appended to existing recommendation or suppression reasons rather than replacing them.

web/src/components/resource/RightsizingStrip.tsx

RightsizingScanView.tsxQualify scan suggestions with inventory availability +7/-0

Qualify scan suggestions with inventory availability

• Displays a memory-specific evidence note when pod inventory was unreadable, clarifying that the suggestion relies on the metrics window alone.

web/src/components/rightsizing/RightsizingScanView.tsx

Tests (2) +80 / -1
rightsizing_scan_test.goCover readable and denied scan pod inventories +56/-1

Cover readable and denied scan pod inventories

• Adds ResourceCache-backed tests proving readable OOM-killed pods enrich workloads and denied pod listers set the inventory flag without claiming OOM evidence.

internal/prometheus/rightsizing_scan_test.go

RightsizingStrip.test.tsTest rightsizing inventory caveat presentation +24/-0

Test rightsizing inventory caveat presentation

• Verifies memory rows disclose unreadable pod inventories, preserve stronger suppression explanations, and omit the caveat when pods were read successfully.

web/src/components/resource/RightsizingStrip.test.ts

@qodo-code-review

qodo-code-review Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Scan rows omit one of two warnings ✓ Resolved 🐞 Bug ≡ Correctness
Description
EvidenceNote returns immediately for each condition instead of composing the new live-inventory
note with existing reasons. Memory scan rows therefore hide the new caveat behind autoscaling or
restart-history reasons, while the caveat itself hides a simultaneous limit-conflict explanation.
Code

web/src/components/rightsizing/RightsizingScanView.tsx[R827-830]

+  if (row.liveInventoryDenied && row.resource === 'memory')
+    return (
+      <p className="mt-1 text-xs text-theme-text-tertiary">
+        Radar could not read this workload&apos;s pods, so it could not check them for
Relevance

●●● Strong

Early return suppresses simultaneous evidence; accepted UI precedents favor preserving and composing
caveats.

PR-#753

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Autoscaling and OOM-related branches return before the new branch, so they suppress the inventory
caveat. The new branch returns before limitConflict, suppressing that explanation, whereas the
resource strip explicitly appends both messages.

web/src/components/rightsizing/RightsizingScanView.tsx[801-839]
web/src/components/resource/RightsizingStrip.tsx[335-347]
web/src/components/rightsizing/presentation.ts[38-69]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The scan evidence renderer selects one warning by branch precedence, contrary to the intended behavior of appending the live-inventory caveat to stronger reasons.

## Fix Focus Areas
- web/src/components/rightsizing/RightsizingScanView.tsx[801-839]
- web/src/components/resource/RightsizingStrip.tsx[335-347]

## Recommended Fix
Build the primary evidence reason and live-inventory caveat independently, then render both when applicable. Preserve the existing primary reason first and append the inventory qualification, including for OOM, autoscaling, and limit-conflict states; add scan-view tests covering combined conditions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unreadable scans still look healthy ✓ Resolved 🐞 Bug ≡ Correctness
Description
enrichScanCurrentOOM marks denial only when pod sync or lister setup fails, then ignores pod-list
errors and proceeds without requiring readable ReplicaSet ownership for Deployments. A pod list
failure leaves every affected workload’s OOM state falsely authoritative, while a missing or failed
ReplicaSet lister silently drops the same evidence from Deployment rows.
Code

internal/prometheus/rightsizing_scan.go[R797-799]

+	if !waitForPodsSynced(ctx, cache) || cache.Pods() == nil {
+		markScanLiveInventoryDenied(workloads)
		return
Relevance

●●● Strong

Scan-specific list failures still create false healthy OOM state; closely matches the repository’s
accepted rightsizing error-surfacing precedent.

PR-#753
PR-#1694

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new gate marks denial only for failed synchronization or a nil pod lister. Subsequent Pod list
errors are discarded, and ReplicaSet availability and list errors are likewise ignored even though
Deployment attribution depends on that map; emitted rows then copy the unchanged false flag.

internal/prometheus/rightsizing_scan.go[793-825]
internal/prometheus/rightsizing_scan.go[828-843]
internal/prometheus/rightsizing_scan.go[443-446]
internal/prometheus/rightsizing.go[96-99]
PR-#755

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Scan OOM enrichment only marks inventory denial before listing resources, so pod-list failures and unavailable ReplicaSet ownership data can still make unknown OOM state look authoritative.

## Fix Focus Areas
- internal/prometheus/rightsizing_scan.go[793-843]
- internal/prometheus/rightsizing_scan_test.go[347-391]

## Recommended Fix
Check every Pod and ReplicaSet lister result. Mark all affected workloads when a cluster-wide pod list fails, mark only workloads in a failed namespace for namespace-scoped lists, and mark affected Deployment workloads when ReplicaSet ownership cannot be listed; continue enriching unaffected namespaces and workload kinds, and add tests for nil and failing listers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Failed queries appear metrics-backed ✓ Resolved 🐞 Bug ≡ Correctness
Description
EvidenceNote says the suggestion rests on the metrics window whenever liveInventoryDenied is
true, without checking queryError or whether a recommendation exists. When the metrics query also
fails, the row is labeled “Metrics query failed” elsewhere but its expanded evidence claims support
from the unavailable metrics window.
Code

web/src/components/rightsizing/RightsizingScanView.tsx[R829-832]

+      <p className="mt-1 text-xs text-theme-text-tertiary">
+        Radar could not read this workload&apos;s pods, so it could not check them for
+        out-of-memory restarts. The suggestion rests on the metrics window alone.
+      </p>
Relevance

●●● Strong

Unconditionally claiming metrics support after query failure misrepresents partial results, matching
the accepted PR 753 precedent.

PR-#753

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The request presentation explicitly identifies queryError as a failed metrics query. The added
evidence text nevertheless unconditionally says the suggestion rests on that metrics window whenever
the inventory flag is set.

web/src/components/rightsizing/RightsizingScanView.tsx[827-832]
web/src/components/rightsizing/presentation.ts[30-33]
PR-#753

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The live-inventory caveat always claims that a suggestion rests on the metrics window, including rows where the metrics query failed and no usable suggestion was produced.

## Fix Focus Areas
- web/src/components/rightsizing/RightsizingScanView.tsx[827-833]
- web/src/components/rightsizing/presentation.ts[30-33]
- internal/prometheus/rightsizing.go[567-590]

## Recommended Fix
Use neutral inventory wording when `queryError` is present or no recommendation exists, and reserve the metrics-window statement for rows with valid metrics-derived guidance. Add a test combining `liveInventoryDenied` with `queryError`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Cloud users miss inventory caveat 🔗 Cross-repo conflict ≡ Correctness
Description
liveInventoryDenied is rendered only by the newly changed Radar UI, while Radar Hub Web builds its
embedded RadarApp from the locked 1.13.5 package rather than this branch. Until a new package is
published and Hub's lockfile and deployment are updated, cloud users receiving the new backend field
still see recommendations without the caveat.
Code

web/src/components/resource/RightsizingStrip.tsx[R342-344]

+    row.liveInventoryDenied && row.resource === 'memory'
+      ? "Radar could not read this workload's pods, so it could not check them for out-of-memory restarts."
+      : undefined
Relevance

●● Moderate

Cross-repository release coordination is plausible, but no repository precedent establishes
accepting packaged-UI deployment findings.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR adds the caveat inside Radar's packaged UI, and that package is published only from an
explicit version tag. Radar Hub Web imports RadarApp, but both its lockfile and reproducible `npm
ci` build select version 1.13.5, so its deployed bundle cannot contain this new rendering until
coordinated separately.

web/src/components/resource/RightsizingStrip.tsx[339-347]
.github/workflows/publish-radar-app.yml[12-36]
External repo: skyhook-dev/radar-hub-web, src/pages/ClusterView.tsx [5-12]
External repo: skyhook-dev/radar-hub-web, package-lock.json [1223-1226]
External repo: skyhook-dev/radar-hub-web, Dockerfile [19-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Radar Hub Web embeds a lockfile-pinned release of `@skyhook-io/radar-app`, so merging and deploying the backend field alone does not make the new inventory caveat visible to cloud users.

## Fix Focus Areas
- web/src/components/resource/RightsizingStrip.tsx[339-347]
- .github/workflows/publish-radar-app.yml[12-36]
- /cross_repos/radar-hub-web/package-lock.json[1223-1226]

## Recommended Fix
Publish a new `@skyhook-io/radar-app` version containing this change, update Radar Hub Web's dependency and lockfile to that version, and coordinate the Hub deployment alongside or before agents that emit `liveInventoryDenied`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 44 rules
✅ Cross-repo context — repo relationships
  Explored: repo: skyhook-dev/radar-hub-web (sha: 8a663915)
  Explored: repo: skyhook-dev/deployment (sha: 5ef6353a)
Review mode: ⚖️ Balanced: This changes rightsizing’s Go cache/informer error handling and frontend qualification behavior across multiple paths, with meaningful recommendation correctness and API-contract risk, but not enough independent logic density to warrant extended review.

Grey Divider

Tip of the day
💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread internal/prometheus/rightsizing_scan.go Outdated
Comment thread web/src/components/rightsizing/RightsizingScanView.tsx Outdated
Comment thread web/src/components/rightsizing/RightsizingScanView.tsx Outdated
Comment on lines +342 to +344
row.liveInventoryDenied && row.resource === 'memory'
? "Radar could not read this workload's pods, so it could not check them for out-of-memory restarts."
: undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Cloud users miss inventory caveat 🔗 Cross-repo conflict ≡ Correctness

liveInventoryDenied is rendered only by the newly changed Radar UI, while Radar Hub Web builds its
embedded RadarApp from the locked 1.13.5 package rather than this branch. Until a new package is
published and Hub's lockfile and deployment are updated, cloud users receiving the new backend field
still see recommendations without the caveat.
Agent Prompt
## Issue description
Radar Hub Web embeds a lockfile-pinned release of `@skyhook-io/radar-app`, so merging and deploying the backend field alone does not make the new inventory caveat visible to cloud users.

## Fix Focus Areas
- web/src/components/resource/RightsizingStrip.tsx[339-347]
- .github/workflows/publish-radar-app.yml[12-36]
- /cross_repos/radar-hub-web/package-lock.json[1223-1226]

## Recommended Fix
Publish a new `@skyhook-io/radar-app` version containing this change, update Radar Hub Web's dependency and lockfile to that version, and coordinate the Hub deployment alongside or before agents that emit `liveInventoryDenied`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread web/src/components/resource/RightsizingStrip.tsx Outdated
The scan marked a denied inventory only when the pod informer was unusable,
and still discarded the errors from the list calls themselves. It also treated
a missing ReplicaSet lister as ordinary: a pod names the ReplicaSet that owns
it, never the Deployment, so without that map every Deployment quietly
collected no OOM evidence while reporting none had any. Both list paths now
report failure, and a lost ownership path marks Deployments alone — a
StatefulSet owns its pods directly and loses nothing.

The scan's evidence note also composes now. The inventory caveat sat in the
early-return chain ahead of the limit-conflict branch, so a row that could not
read its pods hid its own limit conflict; it renders alongside whichever reason
applies instead. Its claim that the suggestion "rests on the metrics window
alone" is gone, since the metrics query can have failed too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 40a2600. Configure here.

Comment thread internal/prometheus/rightsizing_scan.go
@nadaverell

Copy link
Copy Markdown
Contributor Author

On the remaining cross-repo item ("Cloud users miss inventory caveat") — accurate, but not actionable here, so it is deliberately not addressed in this PR.

Radar Hub builds its embedded RadarApp from the published @skyhook-io/radar-app package, so any frontend change in this repo reaches cloud users only after that package is published and Hub's lockfile moves. That is the standing condition for every UI change, not something this branch introduces, and publishing is gated on explicit approval naming the version.

The backend field is additive and optional (liveInventoryDenied?), so an older embedded build ignores it and renders exactly as it does today. Nothing breaks in the interim; the caveat simply arrives with the next package bump.

Also worth noting for anyone reading the checks: Cursor Bugbot reported NEUTRAL (skipped) on both pushes here, so Qodo was the only automated reviewer on this PR.

nadaverell and others added 2 commits September 14, 2026 00:00
…eplicaSets

Two paths still lost the live-inventory caveat.

The strip returned a staged reduction's copy before it reached the caveat, so
the one suggestion that asks the reader to reduce in steps was also the one
that never said its pods had gone unchecked. The caveat now qualifies every
message the strip returns.

The scan waited for the pod informer but took the ReplicaSet lister at face
value. ReplicaSets load on the deferred path, so a warming lister is ordinary
right after startup and answers an empty list with no error — resolving every
Deployment's pods to nothing while reporting the inventory as read. It is now
waited on exactly as pods are.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW
An informer that is synced and non-nil still answers only for the namespaces it
watches, which is how Radar runs whenever cluster-wide list is denied. The scan
took a successful empty list as proof, so a workload in an unwatched namespace
reported no OOM-killed pods with full confidence. Availability is now decided
per workload, against the namespaces its pods and — for a Deployment — its
ReplicaSets actually cover.

A scan holding no Deployments no longer waits on the deferred ReplicaSet
informer it never reads, and a cancelled request stops instead of recording an
unreadable inventory and continuing into Prometheus on a dead context.

When there is no retained ownership and the pods cannot be listed, the response
now says so rather than reporting an absence of samples: the selection falls
back to the current pods and matches nothing by construction, which is a fact
about the inventory, not about the metrics.

The field is liveInventoryUnavailable rather than Denied, because an exhausted
warming wait lands here too and is not a permission decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW
@nadaverell nadaverell changed the title Say when rightsizing could not read the live pod inventory Say when rightsizing could not read a workload's pods Sep 13, 2026
@nadaverell
nadaverell merged commit 18d77ba into main Sep 14, 2026
9 checks passed
@nadaverell
nadaverell deleted the fix/rightsizing-live-inventory branch September 14, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rightsizing reports an empty pod inventory when the cache could not answer

1 participant