Skip to content

refactor(app): declarative Infrastructure tab correlation list#2462

Open
alex-fedotyev wants to merge 3 commits into
mainfrom
alex/infra-declarative-correlation
Open

refactor(app): declarative Infrastructure tab correlation list#2462
alex-fedotyev wants to merge 3 commits into
mainfrom
alex/infra-declarative-correlation

Conversation

@alex-fedotyev

@alex-fedotyev alex-fedotyev commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

The Infrastructure tab in the log and trace row side panel correlates infrastructure metrics to the opened span. Until now it was hardcoded to exactly two Kubernetes resource types, Pod and Node, with three fixed charts each, and it used a single attribute both to decide whether a group should appear and to filter that group's metrics.

This refactors that hardcoding into a declarative descriptor list and splits the "detect" attribute (decides whether a group shows up) from the "correlate" attribute (filters the group's metrics). Kubernetes behavior is unchanged: the same tab, the same gate, the same Pod and Node charts, the Pod Timeline, the time and size toggles, and the Event marker all render identically.

There is no functional or visual change. The value is internal. The gate and the renderer now read from one shared descriptor list, so they cannot drift apart, and adding a new resource type later (host metrics, serverless functions) becomes a matter of appending a descriptor rather than adding a parallel code path.

What changed

  • New packages/app/src/components/infraCorrelations.ts: an InfraCorrelation descriptor type (detect attribute, correlate attribute, field prefix, chart specs, optional timeline), the built-in Kubernetes Pod and Node descriptors, and a getActiveInfraCorrelations(resourceAttributes) selector.
  • DBInfraPanel.tsx: InfraSubpanelGroup maps over a descriptor's chart specs instead of three hardcoded cards, and the panel maps over the active descriptors instead of two hardcoded Pod and Node blocks. The Pod Timeline is driven by a descriptor flag.
  • DBRowDataPanel.tsx: rowHasK8sContext, the gate shared by the row side panel and the trace panel, now delegates to getActiveInfraCorrelations(...).length > 0. For the Kubernetes descriptor set this is identical to the previous k8s.pod.uid != null || k8s.node.name != null check.
  • The detect and correlate attributes are equal for Kubernetes today, so the output is byte-identical. Keeping them separate is what lets a later resource type detect on one attribute and correlate on another.

One structural note: each correlation group is now wrapped in a container div (previously only the Pod group was, to keep its charts and timeline together). The wrapper has no styling, so the rendered output is unchanged; it also avoids a stray gap that the old code could leave when a group had nothing to show.

Verification

  • No styling, color, or layout changed, so light and dark themes render the same and the layout is identical at any viewport.
  • The refactor is a 1:1 code path for Kubernetes. The descriptors reproduce the exact metric queries, field names (k8s.pod.cpu.utilization - Gauge and the rest), chart titles, number formats, card data-testids, and the Pod Timeline query, and the gate keeps the same k8s.pod.uid != null || k8s.node.name != null truth table. I went through the before and after of every generated string to confirm byte-for-byte equality.
  • New unit test infraCorrelations.test.ts locks the selector's gate semantics (Pod-only, Node-only, both, neither, unrelated attributes, null handling) and pins the built-in descriptor identity (field prefixes, metric fields, card test ids, Pod-only timeline).
  • e2e coverage: the existing positive case asserts the Pod and Node charts render on a k8s row, the existing correlated-metric-source spec covers the "no correlated metric source" path, and a new negative case (Infrastructure tab is hidden for non-Kubernetes rows) asserts the gate hides the tab on a non-k8s row.
  • Verified by a side-by-side Playwright capture: opened a Kubernetes pod row on both the released demo and this PR's preview build and confirmed the Infrastructure tab renders identically (Pod and Node CPU/Memory/Disk charts with avg(k8s.pod.*) / avg(k8s.node.*), the Pod Timeline, the Event reference line, and the 30m/1h/1d + SM/MD/LG toggles). The only differences were the underlying demo data. Preview: https://hyperdx-oss-git-alex-infra-declarative-correlation-hyperdx.vercel.app/search

Test plan

  • eslint clean on the changed files
  • tsc --noEmit clean
  • infraCorrelations.test.ts (10 cases) green
  • gate caller tests (DBRowDataPanel, DBTracePanel) green
  • before and after of every generated metric query, field name, test id, and the gate reviewed for byte-for-byte equality
  • e2e green in CI (existing infra-tab positive + no-metric-source coverage, plus the new non-k8s negative case)
  • released demo vs preview parity confirmed via Playwright on a k8s span (Pod and Node charts, Pod Timeline, Event marker, toggles identical)

[ui-states: allow] no new empty, loading, or error states; the existing alert and loading paths are unchanged.
[viewport: allow] no layout change; the SimpleGrid column logic is untouched.
[no-changeset: allow] internal refactor with no user-visible effect.

Turn the hardcoded Pod and Node correlation blocks in DBInfraPanel and
the matching gate in rowHasK8sContext into a shared declarative
descriptor list (detect attribute, correlate attribute, field prefix,
chart specs, optional timeline). The gate and the renderer now read from
the same list, so they cannot drift apart, and the detect attribute is
separated from the correlate attribute so resource types that key on
different attributes can be added as data rather than new code paths.

No behavior change: the same Infrastructure tab, gate, Pod and Node
charts, Pod Timeline, time and size toggles, and Event marker render
identically for Kubernetes rows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Jun 13, 2026 2:54am
hyperdx-storybook Ready Ready Preview, Comment Jun 13, 2026 2:54am

Request Review

@changeset-bot

changeset-bot Bot commented Jun 13, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6bcec12

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Jun 13, 2026
@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 364 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 3
  • Production lines changed: 364 (+ 105 in test files, excluded from tier calculation)
  • Branch: alex/infra-declarative-correlation
  • Author: alex-fedotyev

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR refactors the Infrastructure tab from two hardcoded Kubernetes blocks (Pod and Node) into a descriptor-driven loop backed by a new infraCorrelations.ts module, with no user-visible change.

  • infraCorrelations.ts introduces InfraCorrelation and InfraChartSpec descriptor types (fully readonly), the built-in K8s descriptors, and getActiveInfraCorrelations — now the single source of truth for both the tab gate and the renderer.
  • DBInfraPanel maps over active correlations instead of two hardcoded blocks; InfraSubpanelGroup receives chart specs as props and maps over them instead of rendering three fixed cards.
  • DBRowDataPanel.rowHasK8sContext delegates to getActiveInfraCorrelations, ensuring the gate and renderer can never drift apart. A new unit test suite pins gate semantics and descriptor identity; a new e2e case verifies the Infrastructure tab is hidden for non-Kubernetes rows.

Confidence Score: 5/5

Safe to merge. The refactor is a 1:1 behavioral replacement for all current Kubernetes paths; the descriptor types are fully readonly; the gate and renderer now share a single function.

All current Kubernetes code paths are reproduced byte-identically through the descriptor list. The readonly modifiers on InfraChartSpec[] and InfraCorrelation prevent the shared-array mutation risk. The only open item is a forward-looking gap where a future descriptor with a distinct timeline.queryAttribute could produce a silent "undefined" in the KubeTimeline query — but no such descriptor exists today and the current output is unchanged.

No files require special attention for this merge. The timeline query attribute observation in DBInfraPanel.tsx is only relevant when adding a new descriptor in the future.

Important Files Changed

Filename Overview
packages/app/src/components/infraCorrelations.ts New declarative descriptor file. InfraChartSpec[] and InfraCorrelation are fully readonly; K8S_CHART_SPECS shared array ref is typed readonly InfraChartSpec[] so mutation is prevented. getActiveInfraCorrelations correctly uses != null semantics matching the prior gate.
packages/app/src/components/DBInfraPanel.tsx Refactors hardcoded Pod/Node blocks into a descriptor-driven loop. Has one forward-looking gap: the timeline query attribute is not independently guarded — if a future descriptor sets timeline.queryAttribute to something other than correlateAttribute, the KubeTimeline query could embed a literal "undefined" string.
packages/app/src/components/DBRowDataPanel.tsx rowHasK8sContext now delegates to getActiveInfraCorrelations, eliminating the risk of gate/renderer drift. Change is logically equivalent to the prior `k8s.pod.uid != null
packages/app/src/components/tests/infraCorrelations.test.ts Good unit-test coverage for gate semantics and descriptor identity. Tests pin null-handling, unrelated attributes, and the Pod-only timeline. Minor gap: numberFormat and title on chart specs are not asserted, but this doesn't affect runtime correctness.
packages/app/tests/e2e/features/search/search.spec.ts New e2e test verifies the Infrastructure tab is hidden for non-Kubernetes rows. Uses api-server which is present in seed data without k8s resource attributes, and relies on the trace tab which is unconditionally rendered in the tab bar.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Row opened in side panel] --> B{rowHasK8sContext}
    B -->|calls| C[getActiveInfraCorrelations\nresourceAttributes]
    C --> D{Filter INFRA_CORRELATIONS\nby detectAttribute != null}
    D -->|length > 0| E[Show Infrastructure tab]
    D -->|length === 0| F[Hide Infrastructure tab]
    E --> G[DBInfraPanel renders]
    G --> H[activeCorrelations loop]
    H --> I{correlateAttribute\nvalue truthy?}
    I -->|no| J[skip / return null]
    I -->|yes| K[InfraSubpanelGroup\ncharts.map → DBTimeChart]
    K --> L{timeline\n&& Log source?}
    L -->|yes| M[KubeTimeline]
    L -->|no| N[metrics only]
Loading

Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (3): Last reviewed commit: "test(app): assert Infrastructure tab is ..." | Re-trigger Greptile

Comment on lines +218 to +221
const value = resourceAttributes?.[correlation.correlateAttribute];
if (!value) {
return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The render guard uses falsy semantics (!value) while getActiveInfraCorrelations (the gate) uses != null. They should match. For the current Kubernetes descriptors where detectAttribute === correlateAttribute and the value is always a non-empty string, this is harmless — but the declared purpose of separating detectAttribute from correlateAttribute is to let a future descriptor detect on one attribute and correlate on another. If that future correlateAttribute holds an empty string, 0, or false, the gate would pass (the Infrastructure tab would appear) while the render guard would silently skip every group, leaving the panel empty.

Suggested change
const value = resourceAttributes?.[correlation.correlateAttribute];
if (!value) {
return null;
}
const value = resourceAttributes?.[correlation.correlateAttribute];
if (value == null) {
return null;
}

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

Comment thread packages/app/src/components/infraCorrelations.ts Outdated
@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 200 passed • 3 skipped • 1307s

Status Count
✅ Passed 200
❌ Failed 0
⚠️ Flaky 4
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

Mark the InfraCorrelation / InfraChartSpec descriptor data (including the
shared K8S_CHART_SPECS array reused by the Pod and Node groups) as
readonly so the shared reference cannot be mutated through one descriptor
and affect the other. No behavior change.

Also document that the render-time truthiness guard intentionally mirrors
the prior Pod/Node render blocks; the tab gate uses != null, and the two
only diverge once a descriptor splits its detect and correlate
attributes, which is handled when such a descriptor is added.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alex-fedotyev

Copy link
Copy Markdown
Contributor Author

Handled the two notes from the automated review in cb274b8:

  • The descriptor types and the shared K8S_CHART_SPECS array (reused by both the Pod and Node groups) are now readonly, so the shared reference can't be mutated through one descriptor and affect the other.
  • The render-time truthiness guard is intentional, so I left it and added a comment marking the decision point. It mirrors the previous {podUid && ...} / {nodeName && ...} render blocks, while the tab gate uses != null. For the built-in k8s descriptors detect and correlate are the same attribute, so the two are equivalent today. They only diverge once a descriptor splits its detect and correlate attributes, and that case is where the empty-correlate behavior should be designed rather than guessed here.

Adds an e2e negative case: open a non-Kubernetes log row and confirm the
Infrastructure tab is not offered. This locks the gate (the tab appears
only when a built-in correlation's detect attribute is present) at the
rendered-UI level, complementing the existing positive case that asserts
the Pod and Node charts render for a k8s row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant