Skip to content

Report dependencies as dependencies, not autoscaling, in resource relationships - #1978

Merged
nadaverell merged 6 commits into
mainfrom
feature/relationship-dependency-projection
Oct 9, 2026
Merged

nadaverell merged 6 commits into
mainfrom
feature/relationship-dependency-projection

Conversation

@nadaverell

@nadaverell nadaverell commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Radar's relationship view treated every uses edge in the graph that wasn't storage as autoscaling. A Certificate that uses its Issuer would be shown with the Issuer as its Scale Target, and the Issuer would list the Certificate as its Autoscaler, in the drawer, the REST resource details and the MCP resource context given to AI tools. This separates real autoscalers from other dependencies, so those surfaces say "depends on" when something isn't scaling.

Nothing changes visibly on today's main: no edge there is misclassified yet. The edges this fixes arrive in follow-up PRs (Certificate → Issuer in #1979, KEDA → TriggerAuthentication in #1987), which build on this projection.

What changed

Classification of uses edges (pkg/topology/relationships.go). Each edge is put into exactly one group:

Edge source Group
core PersistentVolumeClaim storage (storageRefs / consumers), as before
HorizontalPodAutoscaler (autoscaling), VerticalPodAutoscaler (autoscaling.k8s.io), ScaledObject / ScaledJob (keda.sh), same namespace autoscaling (scaleTarget / scalers), as before
anything else new dependencies / dependents
  • Matching uses the exact API group, so a custom resource that happens to be named ScaledObject or PersistentVolumeClaim is no longer treated as one.
  • A KEDA scaler's TriggerAuthentication is a dependency, not its scale target, so it can't displace the workload the scaler actually scales.

New fields, additive. Relationships and resourceContext gain dependencies and dependents. In resource context they're filtered by the caller's permission to read each target, and dropped ones are reported in the existing omission reasons. docs/mcp.md describes how they differ from scaledBy.

UI. The Related Resources section shows them as Depends On and Required By, with the same badges, navigation and limits as its other groups.

Radar Hub compatibility. Hub embeds a versioned UI that doesn't know the new fields. Issuer and KEDA-authentication dependencies are therefore also reported in the existing configRefs / consumers (Configuration / Used By), so Hub keeps working links. The new UI hides those mirrored entries, so each target appears once.

De-duplication. Reference de-duplication across these groups uses the existing group-aware key from Checks (resource-refs.ts), so same-named resources from different API groups stay distinct. configRefs lists each target once on every path. Each list is de-duplicated once after it is collected, so a PVC shared by thousands of workloads stays linear rather than rescanning the list per consumer.

No new endpoint, permission, graph edge type, or SSE/neighborhood change.

Result on a live cluster

To show the fix, both builds include #1979's Certificate → Issuer edges on top of main. "Before" uses main's version of the projection, "after" uses this PR's. Real cert-manager resources:

Certificate: Scale Target becomes Depends On

Issuer: Autoscaler becomes Required By

On main alone, this branch (merged with main) returned relationships identical to main for all 79 resources on the test cluster, including HPA, VPA, KEDA and PVC fixtures.

Testing

  • Graph fixtures cover Certificate/Issuer direction, KEDA target plus authentication, PVC consumers, exact-group collisions, one configuration target reached by two edges, each autoscaler kind accepted only in its own group and namespace, and a claim shared by 20,000 Jobs plus a duplicate edge (each consumer listed once).
  • Context tests cover target authorization and omission reasons. Rendered-component tests cover dependency-only responses, the directional labels, exact-group navigation and the mirrored projections. Existing Checks identity tests confirm unchanged keys.
  • go test for pkg/topology, pkg/resourcecontext, internal/server and internal/mcp; make tsc; the affected shared UI suites (22 tests).
  • Live: the comparisons above, on a kind cluster.

ðŸĪ– Generated with Claude Code

@nadaverell
nadaverell requested a review from hisco as a code owner October 6, 2026 23:36
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Separate dependency references from autoscaling relationships

🐞 Bug fix âœĻ Enhancement 🧊 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

â€Ē Classify uses references as storage, autoscaling, or dependencies instead of treating every
 non-storage link as scaling.
â€Ē Expose directional dependencies in resource details and authorized resource context without
 changing graph transport.
â€Ē Add regression tests for issuers, KEDA authentication, API-group collisions, authorization, and UI
 labels.
Diagram

graph TD
  E["Uses edges"] --> C{"Classify reference"} --> D["Dependencies"] --> R["Relationships"] --> U["Related Resources"]
  C --> S["Storage references"] --> R
  C --> A["Scaling references"] --> R
  R --> X["Resource context"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Introduce distinct graph edge types
  • ➕ Makes dependency and scaling semantics explicit in the graph itself.
  • ➖ Requires changes to graph producers and consumers, including graph transport, for a projection-focused fix.

Recommendation: Keep the PR's projection-level classification: it corrects resource details and context while preserving the existing graph contract. A distinct edge type is worth considering only if graph-level consumers later need these semantics.

Files changed (11) +157 / -24

Enhancement (6) +30 / -9
drawer-components.tsxShow dependencies in Related Resources +8/-0

Show dependencies in Related Resources

â€Ē Displays dependencies and dependents as directional groups using the existing reference deduplication and navigation presentation. Dependency-only relationships now cause the section to render.

packages/k8s-ui/src/components/ui/drawer-components.tsx

core.tsType directional topology relationships +2/-0

Type directional topology relationships

â€Ē Adds optional dependency and dependent reference arrays to the shared UI Relationships interface.

packages/k8s-ui/src/types/core.ts

build.goProject authorized dependency references into context +5/-0

Project authorized dependency references into context

â€Ē Converts both directional relationship lists into context references through the existing access-checking and omission path.

pkg/resourcecontext/build.go

types.goAdd directional fields to resource context +2/-0

Add directional fields to resource context

â€Ē Adds optional dependencies and dependents arrays to the resource-context response.

pkg/resourcecontext/types.go

types.goExpose dependency fields on topology relationships +11/-9

Expose dependency fields on topology relationships

â€Ē Adds optional dependencies and dependents reference arrays to the serialized Relationships type.

pkg/topology/types.go

diagnoseEvidenceTypes.tsType dependencies for diagnosis context consumers +2/-0

Type dependencies for diagnosis context consumers

â€Ē Adds optional directional reference arrays to the diagnosis resource-context interface so investigation consumers can accept the new context fields.

web/src/components/diagnose/diagnoseEvidenceTypes.ts

Bug fix (1) +30 / -13
relationships.goClassify uses edges without conflating dependencies and scaling +30/-13

Classify uses edges without conflating dependencies and scaling

â€Ē Projects 'uses' edges into storage, scaling, or directional dependency fields. Scaling requires recognized source API groups and matching namespaces; KEDA authentication targets are treated as dependencies, and dependency-only results are retained.

pkg/topology/relationships.go

Tests (3) +91 / -2
drawer-components.test.tsxTest directional dependency rendering +17/-0

Test directional dependency rendering

â€Ē Covers dependency-only rendering, navigation controls, and the Depends On and Required By labels without incorrect scaling labels.

packages/k8s-ui/src/components/ui/drawer-components.test.tsx

build_test.goTest dependency authorization and scaler group identity +28/-2

Test dependency authorization and scaler group identity

â€Ē Verifies permitted and denied dependency references, including the omission reason. Updates the KEDA scaler fixture to assert its observed API group in serialized context.

pkg/resourcecontext/build_test.go

relationships_test.goCover dependency direction and exact-group classification +46/-0

Cover dependency direction and exact-group classification

â€Ē Tests Certificate–Issuer dependencies alongside KEDA workload scaling and authentication references. Excludes similarly named custom resources from scaler and core-PVC classification.

pkg/topology/relationships_test.go

Documentation (1) +6 / -0
mcp.mdDocument dependency references in MCP context +6/-0

Document dependency references in MCP context

â€Ē Explains how dependencies and dependents differ from autoscalers, and clarifies that references are permission-filtered observations rather than proof of reconciliation.

docs/mcp.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) ðŸŽĻ UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Hosted dependency links disappear ✓ Resolved
Description
GetRelationshipsWithObject moves non-scaling uses edges into dependencies and dependents,
while radar-hub-web still embeds version 1.16.2 of the Radar frontend packages. When the agent
serves the new response to that hosted UI, relationships such as Certificate → Issuer no longer
reach the older relationship groups and the pinned UI cannot render the new groups.
Code

pkg/topology/relationships.go[384]

+				rel.Dependencies = appendResourceRef(rel.Dependencies, *ref)
Evidence
The changed classifier puts these edges into new fields, and only the updated drawer renders those
fields. Radar Hub Web passes resource relationships into an embedded frontend while its lockfile
selects older published frontend packages.

radar -> radar-hub-web
pkg/topology/relationships.go[377-384]
pkg/topology/relationships.go[443-449]
packages/k8s-ui/src/components/ui/drawer-components.tsx[880-884]
External repo: skyhook-dev/radar-hub-web, src/pages/fleet/EmbeddedWorkloadView.tsx [217-229]
External repo: skyhook-dev/radar-hub-web, src/pages/ClusterView.tsx [1-12]
External repo: skyhook-dev/radar-hub-web, package-lock.json [1420-1424]
External repo: skyhook-dev/radar-hub-web, package-lock.json [1445-1448]

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 new relationship projection removes dependency edges from fields understood by Radar Hub Web’s pinned frontend packages, so hosted users lose those links.
## Fix Focus Areas
- pkg/topology/relationships.go[377-384]
- packages/k8s-ui/src/components/ui/drawer-components.tsx[880-884]
- /cross_repos/radar-hub-web/package.json[17-19]
## Recommended Fix
Publish the updated Radar frontend packages and upgrade radar-hub-web’s package manifest and lockfile before deploying the new agent projection to hosted clusters, or preserve a compatible presentation during the rollout.

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



Remediation recommended

2. Some dependencies disappear from details ✓ Resolved
Description
RelatedResourcesSection passes the new Depends On and Required By groups through dedupeRefs,
whose identity key omits ref.group. When references share a kind, namespace, and name but have
different API groups, only the first appears in the affected group and retains a navigation control.
Code

packages/k8s-ui/src/components/ui/drawer-components.tsx[R880-884]

+        {relationships.dependencies && relationships.dependencies.length > 0 && (
+          <RelationshipGroup label="Depends On" refs={dedupeRefs(relationships.dependencies)} onNavigate={onNavigate} />
+        )}
+        {relationships.dependents && relationships.dependents.length > 0 && (
+          <RelationshipGroup label="Required By" refs={dedupeRefs(relationships.dependents)} onNavigate={onNavigate} />
Evidence
The new dependency groups call dedupeRefs, which keys references by kind, namespace, and name
without their API group. The references retain their group, and backend relationship deduplication
treats different groups as distinct, so the UI collapses a reference that the backend preserves.

Rule 3036574: Disambiguate Kubernetes resource kinds that share the same Kind name by group-aware GVR and ID prefixes
packages/k8s-ui/src/components/ui/drawer-components.tsx[793-800]
packages/k8s-ui/src/components/ui/drawer-components.tsx[880-884]
pkg/topology/relationships.go[776-780]
packages/k8s-ui/src/components/ui/drawer-components.tsx[796-802]
pkg/topology/relationships.go[770-779]

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 new dependency groups collapse distinct resources from different API groups when their kind, namespace, and name match.
## Fix Focus Areas
- packages/k8s-ui/src/components/ui/drawer-components.tsx[793-802]
- packages/k8s-ui/src/components/ui/drawer-components.tsx[880-884]
## Recommended Fix
Include `ref.group` in `dedupeRefs`' identity key and add a rendered-component test with two otherwise identical references from different API groups.

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


Grey Divider

Tip of the day
ðŸ’Ą Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/k8s-ui/src/components/ui/drawer-components.tsx Outdated
Comment thread pkg/topology/relationships.go Outdated
nadaverell and others added 3 commits October 9, 2026 15:11
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nadaverell nadaverell changed the title Separate resource dependencies from autoscaling relationships Report dependencies as dependencies, not autoscaling, in resource relationships Oct 9, 2026
A PVC shared by many workloads made the per-append duplicate scan quadratic.
@nadaverell
nadaverell merged commit e5473f4 into main Oct 9, 2026
9 checks passed
@nadaverell
nadaverell deleted the feature/relationship-dependency-projection branch October 9, 2026 23:17
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