Skip to content

Show workload logs, alert rules, Helm releases and permissions in Findings - #1681

Merged
nadaverell merged 15 commits into
mainfrom
feat/findings-adapters
Sep 9, 2026
Merged

nadaverell merged 15 commits into
mainfrom
feat/findings-adapters

Conversation

@nadaverell

@nadaverell nadaverell commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Findings dropped four results the agent already fetches: workload logs (read four times in the saved cloud-provisioner run, never shown), Prometheus alert rules, Helm release state, and ServiceAccount permissions. Each now becomes Radar-authored evidence built strictly from the producer's documented payload. Collapsed events cards lead with what happened instead of a group count, changes cards open the Timeline for the resource they are about, and a citation can promote any single-subject card about another resource.

Track A of the Findings next layer; frontend only.

What changed

  • get_workload_logs reuses the logs kind: one observation per pod/container from one source, so a multi-container read stays one citable result. logsError is a limitation, a zero-pod read is a receipt scoped to the workload, narrowHint is a coverage note. Relevance comes from the workload the producer names (every kind it resolves, Rollouts included); rows relate to a target workload the way diagnose relates to the pods it resolved.
  • get_prometheus_rules adds the alerts kind (rule, instances, annotations). An instance names the target only through an exact workload label (deployment=, statefulset=, workload=…) or a pod label naming a pod a producer already tied to the target in this projection; rule-level labels reach only producer-related. Re-reads of the same rule advance the card, so a rule that goes inactive shows as resolved. Rules are keyed by expression plus static labels; identical rows within one response stay scoped to that read. A receipt is emitted only for an unfiltered state=firing|pending query that is complete, fully evaluated, and either empty or with every instance placed in another namespace. Any evaluation health other than a reported ok is a limitation row.
  • get_helm_release adds the helm kind, adverse when status is not deployed, a health issue is set, or the last operation failed or is stuck. Storage namespace is part of the identity; the resource link is withheld when it differs because the ref cannot carry it. An owned resource without an API version establishes nothing; owned rows keep the producer's summary and message. Every comparison the producer could not finish becomes a coverage limitation — values, values diff, notes diff and resource diff — because three of those are role denials, and a release card that quietly omits a withheld diff reads as a complete one.
  • get_subject_permissions adds the permissions kind: access checks render the verdict and attributes, subject reads render bindings and effective-rule counts. Target relevance requires the newest same-turn resource observation of the target to name the ServiceAccount (from resourceContext.uses.serviceAccount or a PodSpec on a kind that carries one); any other subject is broader context. No blast-radius inference.
  • Cited broader cards: helm, alerts and permissions join resource, logs and crash as single-subject kinds a citation can promote into the main list. The pane states how many broader results of any kind it withheld and that they appear when the assessment cites them.
  • Events cards: summary leads with the newest warning's reason and message; counts are secondary in the body too. A zero-event get_events result (serialized as null) is an empty result, not a malformed one; the same for get_changes.
  • Changes cards carry the producer's subject and a Timeline button. Repeated get_changes reads of one resource are revisions of one card whose title names the window. App.tsx moves namespace state and the server pick together with the URL, as the Timeline's own namespace prompt does, so the URL write/read effects cannot alternate; a cluster-scoped subject keeps the current scope. That is a known gap rather than a design: the Timeline drops every event whose namespace is outside the filter and a cluster-scoped event has none, so the destination can be a view the cited change cannot appear in. Widening means moving local state, the URL and the server pick together, each with its own ordering rule, and belongs in a change made against that machinery rather than this one. The relationships card already opened its root via the existing subject ref, now pinned by a test.
  • Coverage strip: a group with one limitation renders it once, with the source link on the summary row.
  • Labels, icons, body renderers, source excerpts, one groupKey builder and a latestRelevance accessor on the projection builder. Shared-file edits are new cases and entries.

Testing

make tsc                          → pass
cd web && npm test                → 85 files, 957 tests pass
cd packages/k8s-ui && npm test    → 174 files, 3,582 pass, 1 skipped
go test ./... (both modules)      → pass
make build                        → pass
git diff --check                  → clean

internal/mcp/tools.go gains the accessDenied marker and tools_filter_test.go covers it, so the Go suites are part of this PR's verification rather than a formality.

New coverage per adapter: success, error, truncated, empty, relevance, and malformed payloads. The decisive case — one workload-logs result with several containers staying one source with several observations, so the assessment's source link still resolves — is keeps one multi-container result as one source with several observations. The cited-broader cards, merged change reads, null results and single-row coverage groups each have a regression test, as do the four Helm comparison failures and the cluster-scoped Timeline scope.

Visually checked in light, dark and at 480 px against a saved run, and against a copy of that run with alert-rule, Helm, permissions and changes results injected into a fixture copy of the run store — a second Radar instance launched against the copy, leaving the real store untouched. Workload-logs cards, the events lead, and the new alert, Helm, access-check and permissions bodies render in both themes and at narrow width, and the Timeline button navigated to the expected scoped view.

web/src/api/client.authRedirect.test.ts is flaky in this repo — a timing-sensitive 401 single-flight test, unrelated to this PR. It passes on rerun.

Scope and tradeoffs

  • Recording rules are not cards: they carry no state and cannot describe a resource. They stay in Activity.
  • An alert instance whose only locator is a pod name is broader unless a diagnose, pod-logs or workload-logs read earlier in the projection tied that pod to the target. Running get_prometheus_rules before diagnose therefore yields context, never attribution.
  • Alert rule evaluation health (ok/err/unknown) is Prometheus's own enum about evaluation, not a resource health, so it is shown as reported rather than mapped through the resource HealthLevel vocabulary.
  • Evidence bodies render problems as compact text like the existing log and resource bodies; the drawer-scale AlertBanner is not used anywhere in the investigation pane.
  • The plan wrote the Timeline link as scopeNamespaces; that parameter only acts alongside app in TimelineView, so the link uses the app-wide namespaces parameter.
  • The plan's "else producer-related" for permission subjects was tightened to "else broader": a producer response never establishes that relationship.
  • Blast-radius detection stays parked on the producer change that returns per-binding rules.

Note

Medium Risk
Touches investigation evidence projection and MCP response shape (accessDenied), which affects how operators interpret empty events and cited evidence; changes are heavily tested but span UI navigation and RBAC-sensitive read paths.

Overview
Investigation Findings now turns four MCP tool results the agent already calls—get_workload_logs, get_prometheus_rules, get_helm_release, and get_subject_permissions—into first-class evidence cards with dedicated bodies, relevance rules, and citation support alongside existing resource/log/crash types.

UX and navigation: Events summaries lead with the newest warning text; changes cards expose a Timeline deep link (namespace scope kept in sync with URL in App.tsx); withheld “broader” results are counted and can be promoted when the assessment cites them; coverage limitations dedupe when a single row is the whole message.

API honesty: get_events sets accessDenied: true when the requested namespace is outside the caller’s allowed set, so an empty list is not mistaken for “no events” (documented in docs/mcp.md, tested in Go).

Evidence semantics (frontend projection): Workload logs split per pod/container but stay one citable source; alert rules match the target only via strict label/pod linkage; Helm releases surface operations, health, and comparison gaps as limitations; permissions distinguish unevaluated vs decided access checks and bind to the workload’s ServiceAccount from same-turn resource reads. Startup blockers can merge across pods; denied events read as coverage errors, not empty receipts.

Reviewed by Cursor Bugbot for commit e412428. Bugbot is set up for automated code reviews on this repo. Configure here.

@nadaverell
nadaverell requested a review from hisco as a code owner September 7, 2026 23:23
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Surface logs, alerts, Helm, and permissions in Findings

🐞 Bug fix ✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Adds Findings evidence for workload logs, Prometheus alerts, Helm releases, and permissions.
• Preserves honest relevance, receipts, limitations, and state transitions from producer payloads.
• Improves event summaries and opens resource-scoped change cards in Timeline.
Diagram

graph TD
  Results["Tool Results"] --> Adapters["Strict Adapters"] --> Projection["Evidence Projection"] --> Findings["Findings Pane"]
  Findings --> Cards["Evidence Cards"] --> Resources["Resource Views"]
  Findings --> Changes["Changes Cards"] --> Timeline["Radar Timeline"]
Loading
High-Level Assessment

The payload-specific adapter approach is appropriate because each producer has materially different identity, relevance, completeness, and limitation semantics. A generic schema-driven adapter was considered but would obscure these provenance rules and make false relevance or absence claims more likely; the extensive focused tests appropriately protect the chosen design.

Files changed (8) +3888 / -25

Enhancement (6) +1711 / -24
App.tsxOpen Timeline with synchronized namespace scope +18/-0

Open Timeline with synchronized namespace scope

• Adds the Timeline navigation callback used by Findings change cards. Namespace state, server selection, and URL parameters move together to prevent competing URL synchronization effects.

web/src/App.tsx

DiagnoseSurface.tsxRoute evidence Timeline navigation through the diagnose surface +30/-5

Route evidence Timeline navigation through the diagnose surface

• Generalizes destination reveal behavior for both resource and Timeline navigation. The surface closes an overlaying panel or restores docked mode before forwarding a Timeline scope.

web/src/components/diagnose/DiagnoseSurface.tsx

InvestigationEvidencePane.tsxRender alerts, Helm releases, permissions, and Timeline actions +509/-2

Render alerts, Helm releases, permissions, and Timeline actions

• Adds specialized card bodies, icons, badges, caveats, managed-resource links, and permission details for the new evidence kinds. Change cards gain an accessible Timeline action, while event counts become secondary to the event reason and message.

web/src/components/diagnose/InvestigationEvidencePane.tsx

InvestigationView.tsxPass Timeline navigation into Findings +5/-0

Pass Timeline navigation into Findings

• Accepts the Timeline navigation callback and forwards it to the evidence pane for active investigations. Navigation remains disabled for stale investigation data.

web/src/components/diagnose/InvestigationView.tsx

investigationEvidence.tsProject four previously omitted tool results into Findings +1145/-17

Project four previously omitted tool results into Findings

• Introduces strict adapters and evidence models for workload logs, Prometheus alert rules, Helm releases, and subject permissions. It implements payload validation, conservative target relevance, stable identities, state transitions, coverage receipts and limitations, and resource/change navigation subjects; event summaries now lead with the newest warning.

web/src/components/diagnose/investigationEvidence.ts

investigationSourceFocus.tsExpose Helm and permission source excerpts +4/-0

Expose Helm and permission source excerpts

• Uses Helm health issues and permission-check reasons as focused source excerpts when revealing the originating investigation activity.

web/src/components/diagnose/investigationSourceFocus.ts

Tests (2) +2177 / -1
InvestigationEvidencePane.test.tsxTest new evidence bodies and deep links +432/-0

Test new evidence bodies and deep links

• Adds rendering coverage for workload logs, alert rules, Helm releases, permission checks and bindings, Timeline actions, relationship links, and event-first summaries.

web/src/components/diagnose/InvestigationEvidencePane.test.tsx

investigationEvidence.test.tsValidate strict adapter provenance and coverage semantics +1745/-1

Validate strict adapter provenance and coverage semantics

• Adds comprehensive tests for workload-log streams, alert matching and transitions, Helm identity and health, ServiceAccount relevance, malformed payloads, honest receipts, and limitations. It also verifies event leads and producer-scoped change subjects.

web/src/components/diagnose/investigationEvidence.test.ts

@qodo-code-review

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

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. Rollout logs never appear in Findings ✓ Resolved 🐞 Bug ≡ Correctness
Description
WORKLOAD_LOG_KINDS omits the rollouts payload key, causing workloadLogsSubject to return
undefined and the workload-log adapter to classify the result as an invalid payload. This triggers
whenever get_workload_logs is called for an Argo Rollout, because the backend normalizes both
rollout and rollouts to rollouts before emitting the workload identifier.
Code

web/src/components/diagnose/investigationEvidence.ts[R3420-3423]

+const WORKLOAD_LOG_KINDS: Readonly<Record<string, string>> = {
+  deployments: "Deployment",
+  statefulsets: "StatefulSet",
+  daemonsets: "DaemonSet",
Relevance

●●● Strong

The mapping omits a backend-emitted valid kind, making rollout logs systematically disappear; this
is a deterministic correctness fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The adapter only accepts workload prefixes listed in the newly added mapping. The backend's
normalizer explicitly produces rollouts, which is absent from that mapping, so the valid producer
response cannot pass adapter validation.

web/src/components/diagnose/investigationEvidence.ts[3420-3436]
internal/mcp/tools_workloads.go[234-238]
internal/mcp/tools_workloads.go[263-268]
internal/mcp/tools_workloads.go[813-824]

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

## Issue description
Add the producer's `rollouts` workload key to the Findings workload-log kind mapping so valid Argo Rollout log results are projected.

## Issue Context
`get_workload_logs` normalizes Rollout inputs to `rollouts` and serializes that plural key in its `workload` response field. The frontend currently rejects that response before log rows can be added.

## Fix Focus Areas
- web/src/components/diagnose/investigationEvidence.ts[3420-3426]
- internal/mcp/tools_workloads.go[813-824]

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



Remediation recommended

2. Later readers inherit change history ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The comment above workloadLogsPayload names a saved run and describes how Findings behaved before
this change. Anyone maintaining this fixture encounters PR-era implementation history instead of a
durable explanation of the payload’s relevant constraints.
Code

web/src/components/diagnose/investigationEvidence.test.ts[R3114-3115]

+// Shape taken from a saved cloud-provisioner investigation (run-1f386db525),
+// where the agent read workload logs four times and Findings showed none.
Relevance

●●● Strong

The comment preserves run-specific history rather than documenting durable fixture constraints;
cleanup is trivial and maintainability-focused.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 3036538 prohibits code comments that preserve explicit diff or change history. The
added comment cites run-1f386db525 and records that Findings previously omitted logs.

Rule 3036538: Disallow references to tickets or PR history in code comments
web/src/components/diagnose/investigationEvidence.test.ts[3114-3115]

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

## Issue description
Remove the saved-run identifier and description of prior Findings behavior from the test comment.

## Issue Context
The fixture may retain a durable comment describing the payload characteristics it exercises, but it must not preserve ticket, run, PR, or change-history context.

## Fix Focus Areas
- web/src/components/diagnose/investigationEvidence.test.ts[3114-3115]

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


3. Users see inconsistent alert health ✓ Resolved 📘 Rule violation ≡ Correctness
Description
alertRule accepts the producer’s arbitrary health string and stores it directly on
InvestigationAlertRule without calling mapHealthToTone. When Prometheus returns an unrecognized
spelling, the raw value reaches AlertsBody, which renders it as a neutral badge while the receipt
logic may treat it as successfully evaluated.
Code

web/src/components/diagnose/investigationEvidence.ts[R3698-3700]

+      state: candidate.state as string | undefined,
+      health: candidate.health as string | undefined,
+      query: candidate.query,
Relevance

●●● Strong

Raw external health values bypass the repository’s required normalization path and can produce
incorrect downstream health presentation.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 3036741 requires external health strings to pass through a central mapper before
storage, comparison, or downstream exposure. The parser stores candidate.health unchanged, and
both projection and rendering logic subsequently compare and display that raw value.

Rule 3036741: Use only the standard HealthLevel tones and normalize external values
web/src/components/diagnose/investigationEvidence.ts[3658-3660]
web/src/components/diagnose/investigationEvidence.ts[3698-3700]
web/src/components/diagnose/InvestigationEvidence.tsx[2082-2109]

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

## Issue description
Normalize external Prometheus health values before storing, comparing, or rendering them.

## Issue Context
Use `mapHealthToTone` or an equivalent centralized mapper and keep the original producer value separately only if it is needed for display. Update receipt and badge logic to consume the normalized value.

## Fix Focus Areas
- web/src/components/diagnose/investigationEvidence.ts[3658-3660]
- web/src/components/diagnose/investigationEvidence.ts[3698-3700]
- web/src/components/diagnose/investigationEvidence.ts[3836-3846]
- web/src/components/diagnose/InvestigationEvidencePane.tsx[2082-2109]

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


4. Users see inconsistent problem displays ✗ Dismissed 📘 Rule violation ⚙ Maintainability
Description
HelmBody renders release.healthIssue as a custom warning-colored paragraph instead of an
AlertBanner, ProblemAlerts, or ConditionsSection. Whenever a Helm release reports a health
issue, its problem presentation bypasses the shared layout and can drift independently from other
problem displays.
Code

web/src/components/diagnose/InvestigationEvidencePane.tsx[R2263-2266]

+      {release.healthIssue ? (
+        <p className="text-xs leading-relaxed text-warning-text">
+          {release.healthIssue}
+        </p>
Relevance

●●● Strong

Recent repository precedent accepts replacing custom status styling with shared standard components.

PR-#1585

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 3036704 requires user-facing problems in changed renderers to use one of the
standard problem components. The added Helm renderer instead constructs its own warning paragraph
for release.healthIssue.

Rule 3036704: Use standard problem and label selector components in renderers
web/src/components/diagnose/InvestigationEvidencePane.tsx[2263-2266]

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

## Issue description
Replace the custom Helm health-issue paragraph with an approved shared problem component.

## Issue Context
Select `AlertBanner`, `ProblemAlerts`, or `ConditionsSection` according to the health issue’s shape and preserve the existing warning semantics.

## Fix Focus Areas
- web/src/components/diagnose/InvestigationEvidencePane.tsx[2263-2266]

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


View medium (2)
5. Helm resource diagnostics stay hidden ✓ Resolved 🐞 Bug ≡ Correctness
Description
InvestigationHelmOwnedResource omits the producer's message and summary fields, and HelmBody
renders only issue, status, and ready for each owned resource. Releases whose owned resources
report their failure details through message or summary therefore lose those diagnostics in
Findings even though the Helm result contains them.
Code

web/src/components/diagnose/investigationEvidence.ts[R282-285]

+  status?: string;
+  ready?: string;
+  issue?: string;
+}
Relevance

●●● Strong

Missing producer diagnostics is a clear data-loss bug in a newly added renderer aligned with the
PR’s evidence-display intent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Helm producer explicitly emits message and summary on every owned-resource row, but the
newly added frontend interface does not declare them and the new card has no rendering path for
either value.

internal/helm/types.go[185-196]
web/src/components/diagnose/investigationEvidence.ts[277-285]
web/src/components/diagnose/investigationEvidence.ts[3891-3907]
web/src/components/diagnose/InvestigationEvidencePane.tsx[2357-2366]

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 Helm evidence model and card do not expose the `message` and `summary` fields supplied for owned resources. This hides producer-provided failure context from Findings.

## Issue Context
The backend `OwnedResource` contract includes both fields in addition to `issue`, `status`, and `ready`. Add them to the frontend model and validation, then display them without duplicating identical issue text.

## Fix Focus Areas
- web/src/components/diagnose/investigationEvidence.ts[277-285]
- web/src/components/diagnose/investigationEvidence.ts[3891-3907]
- web/src/components/diagnose/InvestigationEvidencePane.tsx[2357-2366]

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


6. Alert histories can swap between rules ✓ Resolved 🐞 Bug ≡ Correctness
Description
adaptPrometheusRules assigns otherwise-identical rules an ordinal identity based only on their
position in each response, then uses that identity to retrieve prior relevance. When Prometheus
returns duplicate definitions in a different order, one rule inherits the other rule's target
relevance and revision history, including an incorrect resolved state.
Code

web/src/components/diagnose/investigationEvidence.ts[R3786-3788]

+    const ordinal = seenRuleIdentities.get(ruleDefinition) ?? 0;
+    seenRuleIdentities.set(ruleDefinition, ordinal + 1);
+    const ruleIdentity = `${ruleDefinition}:${ordinal}`;
Relevance

●● Moderate

Potential history swapping is plausible, but duplicate definitions lack an obvious stable identity
and evidence is limited.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Identical definitions receive ordinals from their current iteration order, while both relevance
restoration and observation history are keyed by the resulting identity. A response-order change
therefore leaves the key stable but changes which physical rule it represents.

web/src/components/diagnose/investigationEvidence.ts[3779-3789]
web/src/components/diagnose/investigationEvidence.ts[3790-3803]
web/src/components/diagnose/investigationEvidence.ts[1401-1416]
web/src/components/diagnose/investigationEvidence.ts[1444-1450]

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

## Issue description
Duplicate Prometheus rule definitions are distinguished by their current response order. If their order changes between reads, Findings merges each rule with the other rule's previous observation and can show incorrect relevance or resolution history.

## Issue Context
The producer does not expose the rule file, so there is no stable unique identifier for duplicate definitions. Detect ambiguous duplicate definitions and avoid carrying history or previous relevance across reads for those rows; source-scoped identities are safer than incorrectly merging them.

## Fix Focus Areas
- web/src/components/diagnose/investigationEvidence.ts[3779-3803]
- web/src/components/diagnose/investigationEvidence.ts[1444-1461]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 41 rules
✅ Cross-repo context — repo relationships
  Explored: repo: skyhook-dev/radar-hub-web (sha: a79fb2e8)
Review mode: 🧠 Deep: This is a logic-dense frontend change spanning multiple evidence adapters, relevance/identity rules, card behavior, and navigation, with many independent edit sites where subtle defects could be missed in a single pass.

Grey Divider

Tip of the day
💡 Did you know, you can commit Qodo's fix in one click with committable suggestions (GitHub & GitLab)

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread web/src/components/diagnose/investigationEvidence.test.ts Outdated
Comment thread web/src/components/diagnose/investigationEvidence.ts
Comment thread web/src/components/diagnose/InvestigationEvidencePane.tsx
Comment thread web/src/components/diagnose/investigationEvidence.ts Outdated
Comment thread web/src/components/diagnose/investigationEvidence.ts
Comment thread web/src/components/diagnose/investigationEvidence.ts

@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/diagnose/investigationEvidence.ts
@nadaverell

Copy link
Copy Markdown
Contributor Author

bugbot run

@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.

@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/diagnose/InvestigationEvidencePane.tsx
Comment thread web/src/components/diagnose/parts.tsx

@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/diagnose/investigationEvidence.ts
@nadaverell

Copy link
Copy Markdown
Contributor Author

Review round (Cursor Bugbot, head 602c190):

# Source Issue Verdict Reasoning Action
1 Bugbot Grouped startup cards hide pod names Fix evidenceHasDetails returned false for startup, so the pod list in StartupBody never rendered Grouped cards (more than one pod) now open; test added. Live review also showed the grouped card next to the workload's classified Unschedulable issue with the same message, so a startup group that repeats an issue's reason and message now folds into the issue card, which lists the pods
2 Bugbot Commands copied in the wrong order Fix Fenced blocks were collected before inline spans One pass in reading order; test added

@nadaverell
nadaverell force-pushed the feat/findings-adapters branch from 7a99859 to 03efaa2 Compare September 8, 2026 14:52

@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/diagnose/investigationEvidence.ts
…dings evidence

Findings dropped four results the agent already fetches. Each now becomes
Radar-authored evidence from the producer's documented payload:

- get_workload_logs reuses the logs kind, one observation per pod/container
  from one source, so a multi-container read stays citable as one result.
  logsError is a limitation, a zero-pod read is a receipt scoped to the
  workload, narrowHint is a coverage note.
- get_prometheus_rules adds the alerts kind. Relevance comes from each
  instance's labels (target namespace plus a workload label or a
  controller-shaped pod name); rule-level labels only reach
  producer-related. A receipt is emitted only for a complete, evaluated
  firing/pending query that named nothing about the target, worded with
  the actual filters. Failing evaluation health is a limitation row.
- get_helm_release adds the helm kind, adverse when the status is not
  deployed, a health issue is set, or the last operation failed.
- get_subject_permissions adds the permissions kind: access checks render
  the verdict, subject reads render bindings and effective-rule counts.
  Target relevance requires the same turn's resource observation to name
  the ServiceAccount; no blast-radius inference from a payload without
  per-binding rules.

Collapsed events cards lead with the newest warning's reason and message
with counts secondary. Changes cards carry the producer's subject and open
the Timeline filtered to it; the relationships card already opened its root.
Alert instances relate to the target only through a workload label or a pod
a producer already tied to the workload in this projection; a controller-
shaped pod name alone also fits a sibling such as api-worker. A re-read of
the same rule advances the card, so a firing rule that goes inactive shows
as resolved instead of the stale firing observation, and rules are keyed by
expression and static labels since names repeat within a group. The alert
receipt now requires an unfiltered active-state query whose every instance
sits in another namespace.

Permission subjects that no captured resource ties to the target are broader
context, not supporting evidence, and the newest same-turn read of the
target decides its ServiceAccount. Helm releases keep their storage
namespace in identity and withhold a resource link the ref cannot express;
an owned resource without an API version establishes nothing. Rule, count,
truncation, effective-rule and zero-pod payload shapes are validated before
projection.

The Timeline link moves the namespace scope in state together with the URL,
as the Timeline's own namespace prompt does, so the URL write and read
effects cannot alternate.
Namespace exclusion can only prove an alert instance is elsewhere when the
target itself is namespaced, so cluster-scoped targets no longer receive a
receipt from other namespaces' instances. Identical rule rows within one
response are distinct rules (group names repeat across rule files the
producer does not return), told apart by order rather than merged as reads.
A PodSpec is read only from kinds that carry one, so an ApplicationSet's
template no longer implies the default ServiceAccount. A cluster-scoped
changes subject opens the Timeline under the current namespace scope.
latestRelevance carried a raw NUL byte where observe spells the separator
as an escape; both now go through groupKey, so a re-read of an inactive
rule finds its firing card. The workload-logs subject map mirrors every
kind the producer resolves, including rollouts. Owned Helm resources keep
the producer's summary and message so a row without an issue still says
what state it is in. The workload-logs fixture comment describes the shape
rather than a run.
Two identical rule definitions in one response cannot be told apart, so
they no longer share an identity with rows from an earlier read in
arrival order; each read keeps its own cards and carries no history
across. A rule whose evaluation health is anything but a reported ok,
including an unrecognized value, counts as unevaluated for a negative.
… reads

A citation can promote any single-subject broader card, so helm, alerts
and permissions join resource, logs and crash in the focused set, and the
pane says how many broader results of any kind it withheld. Repeated
get_changes reads of one resource are revisions of one card whose title
names the window, instead of look-alike cards per call. The producer
serializes an empty events or changes result as null; both adapters read
it as empty rather than malformed, which removed a false "couldn't
summarize" coverage row. A coverage group with one limitation renders it
once, with the source link on the summary row.
A DaemonSet that cannot schedule produced a classified Unschedulable
issue for the workload and a startup card for its pods with the same
reason and message. They are one fact seen from two sides, so the issue
card now carries the pod list and the startup card is not emitted; a
blocker no issue repeats keeps its own card.
… order

A merged startup card listed its pods only in a body the card never
opened, and a step's copy buttons put fenced blocks before the inline
commands that preceded them in the text.
…ne where the change is

Two ways this pane could show a complete-looking answer over an incomplete read.

The Helm adapter recorded only the values error. The producer reports four —
values, values diff, notes diff and resource diff — and three of them are Cloud
role denials, so a viewer saw a release card whose diff had been withheld with
nothing saying so. All four now become coverage limitations.

Opening the Timeline for a cluster-scoped change carried the current namespace
filter with it. The Timeline drops every event whose namespace is not in that
filter, and a cluster-scoped event has none, so the destination could not
contain the change that was clicked and read as "nothing happened". A
cluster-scoped subject now widens to all namespaces, moving scope and URL
together the way the namespaced branch already does.

Both found by Codex in the merge sign-off review.
@nadaverell
nadaverell force-pushed the feat/findings-adapters branch from 79e0941 to 7d8db5b Compare September 9, 2026 13:43

@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/diagnose/investigationEvidence.ts
Comment thread web/src/App.tsx Outdated
… it is

The producer reports five failures, not four: values, values diff, manifest
diff, notes diff and resource diff. The values diff was dropped, so a withheld
values comparison still read as a complete card, and the manifest diff was
labelled "Helm values diff", pointing a reader at the wrong comparison.

Found by Cursor Bugbot.
A SubjectAccessReview whose authorizer returned partial data establishes
neither answer. The card titled it "<subject> cannot <verb> <resource>" and
summarized it as "No RBAC rule allows it", which is a denial. The evaluation
error reached the coverage strip, but the card is what an operator acts on, and
it was sending them after a grant that may already exist.

An unresolved check now says so in the title and the summary, and is no longer
counted as denied for tone or tier. The coverage limitation stays.

Found by Codex in the merge sign-off review.
@nadaverell

Copy link
Copy Markdown
Contributor Author

Triaged every open comment against the rebased branch. Two were real and are fixed; the rest are stale, false on current code, or decisions this PR documents.

Fixed

  • Helm diff errors mapped incorrectly (Bugbot) — correct, and worse than reported: the producer emits five error fields and the loop covered four. valuesDiffError was dropped entirely, and diffError (the manifest diff) was labelled "Helm values diff". Now all five, with the producer's own names. Codex found the same thing independently.
  • An unevaluated access check read as a denial (Codex) — a SubjectAccessReview whose authorizer returns partial data has established neither answer, but the card said " cannot " and "No RBAC rule allows it". The evaluation error reached the coverage strip; the card is what an operator acts on. It now says it could not be checked and is not counted as denied for tone or tier.

Not fixed — false on the current code

  • Rollout logs never appear — rollouts: "Rollout" is in WORKLOAD_LOG_KINDS.
  • Resolved alerts lose target provenance — latestRelevance and observe both key through groupKey; alerts are not relevance-partitioned.
  • Alert histories can swap between rules — indistinguishable rules are keyed by definition plus the read and explicitly skip prior relevance (ambiguous ? undefined : latestRelevance(...)), so one cannot inherit another's.
  • Helm resource diagnostics stay hidden — message and summary are on the type and rendered as the fallback when there is no issue.
  • Comment carries PR-era history — the comment describes the payload shape; the flagged text is not on this branch.
  • Persisted pre-marker denied event reads look clean — the receipt's own message names that ambiguity ("a namespace you cannot read also returns nothing") and receipt messages are rendered.
  • Commands copied in the wrong order — already a single pass in reading order.

Not fixed — documented decisions in the PR description

  • Alert-rule evaluation health is Prometheus's enum about evaluation, not resource health, so it is shown as reported rather than mapped through HealthLevel.
  • Evidence bodies render problems as compact text; the drawer-scale AlertBanner is deliberately not used in the investigation pane.

Deferred, tracked for the next round

@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/diagnose/investigationEvidence.ts
Two corrections to the fixes above.

The cluster-scoped Timeline handoff set local namespace state and mutated the
server in the same turn. clearAllNamespaces exists to avoid exactly that: it
moves local state only once the server confirms, so queries cannot refetch
under the empty key while the server still returns the previous pick, and it
refuses to widen a namespace-locked session at all. The handoff now goes
through it.

An access check was treated as unresolved whenever an evaluation error was
present. Kubernetes returns that error alongside a real verdict too — one
webhook failing while another allows — so a decided allow or deny was being
rewritten as "could not check", losing the answer and putting an allow in the
warning list. Unresolved now means the authorizer decided nothing.

Both found by Cursor Bugbot on the previous push.

@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/App.tsx Outdated
Opening the Timeline for a cluster-scoped change keeps the current namespace
filter, which can hide the very change that was clicked: the Timeline drops
every event whose namespace is outside the filter, and a cluster-scoped event
has none.

Widening instead is the right answer and this is the wrong place to do it.
Scope lives in three coupled places — local state, the URL, and the server
pick — each with its own ordering rule: flipping state before the server
confirms caches scoped data under the cluster-wide key, and writing the URL
before state follows sets the two sync effects against each other. Two attempts
here hit one hazard each. The behaviour returns to what it was, and the gap is
recorded for a change made against that machinery rather than beside it.

@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.

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 e412428. Configure here.

Comment thread web/src/App.tsx
@nadaverell
nadaverell merged commit a5f1b4e into main Sep 9, 2026
9 checks passed
@nadaverell
nadaverell deleted the feat/findings-adapters branch September 9, 2026 18:39
nadaverell added a commit that referenced this pull request Sep 9, 2026
The pane heads two lines with these counts — "results about other resources"
and "broader metric results" — and a withheld chart was incrementing both, so
one withheld result was announced twice and the two numbers stopped summing to
what was actually held back.

The counters are now disjoint: a metrics group goes to the metrics count and
everything else to the broader one, which is what each line already claims.

Introduced resolving this branch's conflict with #1681, where the two counters
first met. Found by Cursor Bugbot.
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.

1 participant