Remove contract prose and badge helpers for deleted surfaces

PR 2612 deleted the never-mounted UnifiedResourceTable, and abb6f86aeb
retired the shared workloads-link helper. The unified-resources and
performance-and-scalability contracts still described both as live: the
table's system column, row policy chips, label resolver and drawer
reveal, and the helper's Kubernetes-cluster labels and TrueNAS workload
handoffs. Each clause now names its live owner, becomes a conditional
rule, or goes. The live owners are the Docker hosts System column and
the resource drawer header for system identity,
createPlatformResourceLabelResolver for drawer labels,
useWorkloadSelectionState for same-route drawer reveal, agentResources
for the Kubernetes cluster helpers, and buildWorkloadsRouteSearch for
workload platform scoping.

The label resolver clause no longer calls its labels governed. The
resolver returns getPreferredInfrastructureDisplayName, the operator's
local name, and a table that passes no resolver leaves the raw ID.

With infrastructureSelectors.ts gone (PR 2649),
getInfrastructureSystemIdentitySortLabel has no production caller, and
getInfrastructureSystemTitleBadges has had none since PR 2612. Both are
removed, along with the private identity-label helpers that only the
title helper used. Tests that covered them either drop the duplicate
assertion or now assert the identity badges directly.
This commit is contained in:
rcourtman
2026-10-07 11:52:58 +01:00
parent dcd409f9da
commit bf29201b19
9 changed files with 152 additions and 248 deletions
@@ -2595,26 +2595,29 @@ across provider-backed history. Row hover and focus plus top-card isolation on
workload surfaces must resolve against the same canonical workload ID even
when the backing history is stored under a provider metrics target, so
provider-backed VM rows do not silently drop out of summary emphasis.
The shared infrastructure table hot path now also treats operator-facing
resource identity as a protected boundary: sorting, searching, summary-series
matching, and row titles on the infrastructure page must use the canonical
local instance identity rather than governed AI-summary text, so performance
work cannot “optimize” the table into ambiguous labels that collapse multiple
resources into the same visible name.
The same protected table path treats the visible system column as
identity-first presentation over canonical merged-source data. Sort derivation
for that column must use the same displayed system identity as the render path,
while the render path may keep full merged-source detail in tooltips. When a
row contains both `agent` and a provider/API platform such as Proxmox, the table
must render the provider platform as the compact visible badge rather than
adding extra Agent badge width or sorting primarily by the telemetry method.
When a row is only known through an agent or container runtime, the table must
prefer reported OS/appliance identity before falling back to Docker/runtime
capability labels.
When the displayed system badge already includes the platform version, row title
metadata must not spend extra badge/title budget repeating that same platform as
an unversioned source; it may keep non-duplicate collection context such as
Pulse Agent.
Platform-table hot paths treat operator-facing resource identity as a
protected boundary: sorting, searching, summary-series matching, and row
titles must use the canonical local instance identity (the resource's own name or
`getPreferredInfrastructureDisplayName(...)`) rather than governed AI-summary
text, so performance work cannot “optimize” a table into ambiguous labels that
collapse multiple resources into the same visible name.
A visible system column is identity-first presentation over canonical
merged-source data. The Docker hosts table's System column is the live
instance: `frontend-modern/src/features/docker/DockerHostsTable.tsx` sorts and
renders the same `getDockerHostSystemBadge(...)` result, and the render path
may keep full merged-source detail in tooltips. When a row contains both
`agent` and a provider/API platform such as Proxmox, the system badge must show
the provider platform rather than adding extra Agent badge width or sorting
primarily by the telemetry method. When a row is only known through an agent or
container runtime, the shared resolver prefers reported OS/appliance identity
before falling back to Docker/runtime capability labels; the Docker hosts
column drops that runtime-only fallback, since every row there is a Docker
host, and shows `—` instead.
A surface that shows source badges beside a versioned system badge must not
spend extra badge/title budget repeating that same platform as an unversioned
source; it may keep non-duplicate collection context such as Pulse Agent. The
resource drawer header avoids the repeat by showing system identity badges in
place of source badges.
That derived workload owner now also routes grouped row windowing through
`frontend-modern/src/components/Workloads/useGroupedTableWindowing.ts`, which
owns row-window thresholds, overscan behavior, reveal-index clamping, and
@@ -2991,17 +2994,17 @@ the timeline/history path can resolve through the canonical catalog without addi
detail-only lookup branch to the hot-row path.
The same detail drawer also uses that resolver for correlation dependency and
dependent chips, so the investigation path shows catalog labels in the drawer
and keeps the raw ID only when the catalog cannot resolve one, while the AI
page keeps its broader no-catalog fallback.
That same workloads-link path and the workload projection now also
share the canonical cluster-name helpers in the shared agent-resource layer,
so route labels, pod grouping, and cluster-name fetch keys keep using the
same source of truth instead of rebuilding the `clusterName`/`context`/
`clusterId` prefix locally.
The infrastructure host-table hot path now also suppresses the default
`Internal` + `Cloud Summary` policy pair in row chrome. That baseline posture
still belongs to the canonical policy contract, but repeating it on every host
burns row-density budget without adding operator-grade signal.
and keeps the raw ID only when the catalog cannot resolve one.
The workload projection and the resource drawer share the canonical
cluster-name helpers in `frontend-modern/src/utils/agentResources.ts`, so pod
grouping and the drawer's cluster-name fetch keys keep using the same source
of truth instead of rebuilding the `clusterName`/`context`/`clusterId` prefix
locally.
A platform table that adds policy chips to row chrome must suppress the
default `Internal` + `Cloud Summary` policy pair. That baseline posture still
belongs to the canonical policy contract, but repeating it on every row burns
row-density budget without adding operator-grade signal. No platform table
renders policy chips today.
The shared node adapter also uses that same cluster-name helper for the
infrastructure summary surface, so Proxmox node projections stay aligned with
the same canonical cluster label instead of carrying a raw adapter-local
@@ -3394,10 +3394,14 @@ this health-state projection: it must derive counts only from the resources
accessor already available to the summary state, not from a separate API call or
a re-projection of websocket state outside the shared summary pipeline.
`resourceBadgePresentation.ts` now owns the Infrastructure table system
identity resolver. That resolver must prefer provider/API platform identity,
then reported host OS or appliance identity, before falling back to Docker or
other runtime capability labels.
`resourceBadgePresentation.ts` owns the canonical system identity resolver,
`getInfrastructureSystemIdentityBadges(...)`, which feeds the resource drawer's
header badges and, through `getDockerHostSystemBadge(...)` in
`frontend-modern/src/features/docker/dockerPageModel.ts` (which drops
runtime-only Docker/Podman labels), the Docker hosts table's System column.
That resolver must prefer provider/API platform
identity, then reported host OS or appliance identity, before falling back to
Docker or other runtime capability labels.
This subsystem now sits under the dedicated core monitoring runtime lane so
canonical resource identity, discovery normalization, and platform-runtime
@@ -4006,16 +4010,23 @@ across backend clone/merge and frontend transport. The owning resource remains
the canonical `app-container`; the TrueNAS facet explains the platform-native
app shape, and Docker metadata remains secondary runtime compatibility rather
than the source of truth for the TrueNAS overview.
That same infrastructure focus boundary now also owns deliberate drawer reveal.
When an infrastructure row opens inline detail on the same route, the page must
tag that detail with the canonical active resource ID. Direct row toggles that
already have the row in view must capture the current `.app-scroll-shell`
position through `frontend-modern/src/utils/appShellScrollRestoration.ts`, so
the remounted root shell in `frontend-modern/src/App.tsx` can stay anchored,
and then still hand off to the shared summary-table/contextual-focus helpers
when the opened drawer would otherwise fall below the fold. That reveal must
scroll only enough of the infrastructure table to keep the row header plus the
start of the detail visible, not leave the drawer clipped and not hard-center
Deliberate drawer reveal after a same-route selection write is owned by the
Workloads table
(`frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts`,
governed in `performance-and-scalability`). Platform resource tables open their
inline drawer through local component state, most through
`createPlatformResourceDetailState(...)` in
`frontend-modern/src/features/platformPage/PlatformResourceDetailTableRow.tsx`
while the Docker hosts and Proxmox nodes tables keep their own selection
signal, and write no route state. An infrastructure table that moves drawer selection
into route state must tag the opened detail with the canonical active resource
ID; direct toggles of a row already in view must capture the current
`.app-scroll-shell` position through
`frontend-modern/src/utils/appShellScrollRestoration.ts`, so the remounted root
shell in `frontend-modern/src/App.tsx` stays anchored, and then hand off to the
shared summary-table/contextual-focus helpers when the opened drawer would fall
below the fold. That reveal scrolls only enough to keep the row header plus the
start of the detail visible, never leaving the drawer clipped or hard-centering
the selected row.
That same unified-resource boundary now also owns stored metrics-target
continuity for provider-backed resources. When registry rebuild cannot derive a
@@ -4650,11 +4661,10 @@ The frontend now also consumes those facet reads through
drawer, which keeps the presentation surface aligned with the governed API
contract instead of rebuilding the relationship and timeline inline.
The canonical routing owner now also lives in
`frontend-modern/src/routing/resourceLinks.ts`, including the
workload-to-infrastructure href builder used by Workloads row and drawer
consumers. Future workload-to-resource navigation changes must extend through
that shared routing contract instead of reintroducing workload-local path
builders.
`frontend-modern/src/routing/resourceLinks.ts`. Its workload-to-infrastructure
href builder was retired with the legacy `/infrastructure` route, so future
workload-to-resource navigation must extend that shared routing contract
instead of reintroducing workload-local path builders.
That same shared routing boundary now also owns the canonical Patrol
destination used by cross-surface findings and drill-down links. Shared
dashboard, alert, and settings referrals may target `/patrol` through
@@ -4672,9 +4682,10 @@ resolve the deep-link source from the canonical merged source set before
falling back to raw `platformType`, so TrueNAS-backed hybrid systems do not
lose their storage context just because agent telemetry is also present.
That same routing contract now also owns workload platform scoping without
restoring the retired `/workloads` top-level route. Shared workloads links,
dashboard URL-sync state, and infrastructure drill-down helpers must preserve
`platform=<owned-source-key>` for API-backed workloads such as TrueNAS
restoring the retired `/workloads` top-level route. Workloads URL-sync state,
and any workload link built through the shared `buildWorkloadsRouteSearch(...)`
helper, must preserve `platform=<owned-source-key>` for API-backed workloads
such as TrueNAS
app-containers instead of collapsing those routes back to generic agent or
Docker-only semantics when host telemetry is also present. Runtime-local
filters like `agent` or cluster context may still be added as secondary scope,
@@ -5072,25 +5083,20 @@ block entirely.
The shared frontend resource identity helper now owns that policy-aware
display contract, so infrastructure surfaces that ask for the preferred
resource label no longer need to re-encode the governed summary boundary by
hand. Settings quick-picks, infrastructure selectors, and the connected-
infrastructure / monitored-system projections now all stay on that same
preferred-label helper instead of carrying a separate raw-name fallback fork.
hand. Provider-bound frontend labels, such as Assistant chat resource labels
and organization sharing, stay on that same preferred-label helper instead of
carrying a separate raw-name fallback fork. Operator-local pickers such as the
Settings `ResourcePicker` use `getPreferredInfrastructureDisplayName(...)` on
purpose, under the operator-local resource search obligation above.
That same helper also owns platform-id redundancy suppression for
infrastructure drawers, so surfaces only render platform IDs when they add
identity context beyond the canonical display name or hostname instead of
repeating the same identifier chrome in both runtime and identity sections.
The shared workloads-link helper now also uses that preferred-label helper
for Kubernetes-cluster navigation fallbacks, so drawer/table navigation
context stays inside the same governed resource-label boundary instead of
repeating a raw display-name fork.
That same shared workloads-link path now also owns top-level TrueNAS system
handoffs to the canonical app-container workloads route, so infrastructure
drawers and related-link surfaces do not strand canonical `truenas` resources
on infrastructure-only navigation while API-backed apps already exist in the
shared workloads model. That same helper must also honor merged
`agent`+`truenas` source sets for top-level systems, so hybrid TrueNAS
surfaces keep the same workload handoff even when the resource shape presents
through the canonical host path instead of a raw `type='truenas'` row.
The shared workloads-link helper, which labelled Kubernetes-cluster
navigation fallbacks and handed top-level TrueNAS systems to the workloads
route, was retired with the drawer's related-links injection described above.
New drawer or table links follow the local operator identity rule for
infrastructure links and land inside the owning platform page.
The resource drawer's Kubernetes namespace and deployment tabs use the
canonical cluster-name helper for backend fetch keys, keeping lookup identity
separate from the governed display-label contract.
@@ -5128,10 +5134,12 @@ cluster name, namespace, or surface-local aliases.
The drawer's discovery mapper also reuses that helper for pod fallback agent
IDs, so the resource-detail path and the Workloads path stay aligned on the
same cluster-name source of truth.
The workload projection and workloads-link route helpers also share
the same Kubernetes context prefix helper in the shared agent-resource
layer, so pod grouping and cluster navigation keep the same cluster-context
prefix before any surface-specific display fallback is applied.
The workload projection and the resource drawer also share the Kubernetes
context helper `getPreferredResourceKubernetesContext(...)` in
`frontend-modern/src/utils/agentResources.ts`, directly in `useWorkloads` and
through `getPreferredResourceClusterName(...)` in the drawer, so pod grouping
and drawer cluster context keep the same cluster-context prefix before any
surface-specific display fallback is applied.
The unified-resource projection also reuses that same prefix helper for
projected Kubernetes `clusterId`, so the shared resource store stays aligned
with the Workloads and detail surfaces on the same canonical cluster-context
@@ -5397,14 +5405,14 @@ owners instead of rebuilding ad hoc platform-data parsing or action-target
fallback logic inside alerts, settings, recovery, AI, or infrastructure-local
state owners.
Those runtime adapters must preserve the same operator-facing local identity
boundary as the infrastructure tables and selectors: node, PBS, and PMG view
boundary as the platform tables: node, PBS, and PMG view
models keep canonical local instance labels for summary rows, drawers, and
settings selectors, while governed summaries remain available for policy/detail
surfaces rather than replacing per-instance operator identity.
`ResourceFacetSummary` now consumes the shared
`frontend-modern/src/utils/resourceChangePresentation.ts` label helper for
canonical change kinds, source types, and adapter provenance, so the chip
wording stays aligned across table, drawer, and intelligence surfaces.
wording stays aligned with the drawer's recent-change cards and timeline.
Timeline cards in that drawer surface change metadata when it is present, so
the history view preserves the richer provenance already carried by the
unified-resource model instead of flattening those fields away.
@@ -5414,14 +5422,17 @@ and `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.ts
cards from the Patrol page, resource drawer, and problem-resource dashboard
panels, so canonical resource-filter path construction stays owned by the
shared summary cards rather than being duplicated per surface.
The unified resource table now also supplies a canonical resource-label
resolver into the resource drawer, so related-resource timeline chips can use
the same governed display labels as the table without adding a new
detail-local lookup path.
Platform tables supply the resource-label resolver to the resource drawer
through `PlatformResourceDetailTableRow`. `createPlatformResourceLabelResolver(...)`
in `frontend-modern/src/features/platformPage/PlatformResourceDetailTableRow.tsx`
resolves an ID against the page's own resources through
`getPreferredInfrastructureDisplayName(...)`, so related-resource timeline
chips show the operator-local display name rather than a raw ID, without adding
a detail-local lookup path. A table that passes no resolver leaves those chips
on the raw resource ID.
The resource drawer now also passes that same resolver into the shared
correlation summary, so dependency and dependent chips stay on governed
labels in the investigation path while the AI summary page keeps its broader
raw-ID fallback.
correlation summary, its only consumer, so dependency and dependent chips stay
on those labels in the investigation path.
The same timeline and facet-bundle reads now also accept governed `kind` and
`sourceType` filters, plus a governed `sourceAdapter` filter for adapter-level
provenance drill-down, so history can narrow by canonical change class and
@@ -5670,12 +5681,12 @@ because the legacy backend resource projection leaves `platformType` empty on
several canonical resource types; the helper falls back to the resource's
`sources` array via the existing source-platform normalization so mock
fixtures and live backends produce the same client-side platform grouping.
That same boundary also owns the infrastructure table's operator-facing system
identity vocabulary. `frontend-modern/src/utils/resourceBadgePresentation.ts`,
`frontend-modern/src/components/Infrastructure/resourceBadges.ts`, and the
unified resource table sections may preserve full merged-source detail in
tooltips and accessibility metadata, but visible table headers, sort keys, and
row badges must answer what system the operator is looking at. Provider/API
That same boundary also owns the operator-facing system identity vocabulary.
`frontend-modern/src/utils/resourceBadgePresentation.ts` and its consumers, the
Docker hosts table's System column through `getDockerHostSystemBadge(...)` and
the resource drawer header, may preserve full merged-source detail in tooltips
and accessibility metadata, but visible headers, sort keys, and badges must
answer what system the operator is looking at. Provider/API
platforms such as Proxmox, TrueNAS, VMware, and Kubernetes outrank collection
methods; reported agent OS or appliance identity such as Unraid or Ubuntu
outranks a generic container-runtime capability; and the shared generic source
@@ -5968,8 +5979,8 @@ hydration and unsupported filtered queries, but canonical live freshness for
supported resource snapshots must flow from websocket `state.resources`
instead of layering confirmatory REST refresh loops on top of already-owned
resource updates.
Shared infrastructure consumers such as the unified resource table and detail
drawer must present that owned metadata through shared helpers instead of
Shared infrastructure consumers such as platform tables and the resource
detail drawer must present that owned metadata through shared helpers instead of
reconstructing privacy posture from display names, source types, or other
incidental runtime hints.
@@ -0,0 +1,40 @@
{
"version": 1,
"base_sha": "dcd409f9da0a7a7f35a2a4ad5a4354e0b1d3ee44",
"verified_at": "2026-10-07T10:52:19Z",
"result": "passed",
"changed_paths": [
"frontend-modern/src/utils/resourceBadgePresentation.ts"
],
"content_sha256": {
"frontend-modern/src/utils/resourceBadgePresentation.ts": "ff0409fed88a69acf8acba49ab66a7fd46147d3ba65e8433f0f758fc50a405ca"
},
"routes": [
"/docker",
"/standalone/machines"
],
"viewports": [
{
"width": 1440,
"height": 900
},
{
"width": 390,
"height": 844
}
],
"states": [
"Embedded production build of this tree on a private demo-mode mock stack (port 7697, fresh data dir)",
"Docker hosts table System column shows getInfrastructureSystemIdentityBadges-derived labels: Ubuntu, Alpine, Debian, and LXC with the \"Docker running inside Proxmox LXC <vmid>\" tooltip",
"Docker host inline drawer opens with its Overview/History/Manage tabs",
"Machines inline drawer header shows the type badge plus the system identity badge (Agent, Ubuntu 24.04 LTS)",
"Phone width: Docker hosts table hides the System column, Machines drawer header keeps both badges",
"No page errors; only console errors are the pre-existing inline-style CSP reports that PR 2642 fixes on main, which this base predates"
],
"interactions": [
"Clicked the System column header: aria-sort became ascending and rows ordered Alpine, Debian, LXC, LXC, Ubuntu x3, matching the rendered badges",
"Expanded the first Docker host row through its disclosure button at both widths",
"Expanded the first Machines row through its disclosure button at both widths",
"Production bundle-size check passed with the same chunk set"
]
}
@@ -1,12 +1,11 @@
import { describe, expect, it } from 'vitest';
import {
getInfrastructureSystemIdentityBadges,
getInfrastructureSystemIdentitySortLabel,
getSourceBadge,
getTypeBadge,
getUnifiedSourceBadges,
} from '@/utils/resourceBadgePresentation';
import type { PlatformType, Resource, ResourceType, SourceType } from '@/types/resource';
import type { Resource, ResourceType, SourceType } from '@/types/resource';
const makeResource = (overrides: Partial<Resource>): Resource => ({
id: 'resource-1',
@@ -485,38 +484,3 @@ describe('getDockerHostOsIdentityBadge (via getInfrastructureSystemIdentityBadge
]);
});
});
describe('getInfrastructureSystemIdentitySortLabel — branch coverage', () => {
it('returns the first badge label when identity badges exist', () => {
const resource = makeResource({
type: 'k8s-node',
platformType: 'kubernetes',
sourceType: 'api',
sources: ['kubernetes'],
platformData: {
sources: ['kubernetes'],
kubernetes: { version: '1.28.4' },
},
});
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('K8s 1.28.4');
});
it('returns empty string when no badges and platformType is falsy', () => {
const resource = makeResource({
type: 'storage',
platformType: '' as unknown as PlatformType,
sourceType: 'agent',
});
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('');
});
it('returns the platform badge label when no system identity is found', () => {
const resource = makeResource({
type: 'agent',
platformType: 'truenas',
sourceType: 'agent',
platformData: { sources: ['agent'] },
});
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('TrueNAS');
});
});
@@ -5,8 +5,6 @@ import {
getContainerRuntimeBadgeForRuntime,
getInfrastructurePlatformBadges,
getInfrastructureSystemIdentityBadges,
getInfrastructureSystemIdentitySortLabel,
getInfrastructureSystemTitleBadges,
getPlatformBadge,
getSourceBadge,
getTypeBadge,
@@ -19,8 +17,8 @@ import {
// `getHostIdentityAgentProfile`, `getHostIdentityPlatform`, `getAgentSystemIdentityBadge`,
// `getDockerHostOsIdentityBadge`, `getAvailabilitySystemIdentityBadge`,
// `getStorageSystemIdentityBadge`, `getStoragePlatformSource`, `getSystemSourceVersion`,
// `getVersionedSourceBadge`, `proxmoxLxcDockerBadge`, `proxmoxLxcDockerVmid`,
// `badgeIdentityLabels`) are exercised transitively through the exported entry points
// `getVersionedSourceBadge`, `proxmoxLxcDockerBadge`, `proxmoxLxcDockerVmid`) are
// exercised transitively through the exported entry points
// below, asserting on their observable outputs.
const makeResource = (overrides: Partial<Resource> = {}): Resource =>
@@ -211,28 +209,6 @@ describe('dedupeResourceBadges — branch coverage', () => {
});
});
describe('badgeIdentityLabels via getInfrastructureSystemTitleBadges — branch coverage', () => {
it('treats a system badge with no title as its label-only identity (value ? ... : "" arm)', () => {
// badgeIdentityLabels maps [badge.label, badge.title]; when title is undefined
// the `(value ? normalizeBadgeIdentityLabel(value) : '')` ternary takes the ""
// arm and the entry is dropped by filter(Boolean). The label identity survives,
// so a source badge sharing it is deduped away.
const result = getInfrastructureSystemTitleBadges(
[{ label: 'PVE', classes: 'type-cls' }],
[{ label: 'pve', classes: 'type-cls', title: 'pve' }],
);
expect(result).toEqual([{ label: 'PVE', classes: 'type-cls' }]);
});
it('retains a source badge whose identity is absent from the system identity set', () => {
const result = getInfrastructureSystemTitleBadges(
[{ label: 'PVE', classes: 'type-cls' }],
[{ label: 'K8s', classes: 'type-cls', title: 'K8s' }],
);
expect(result.map((b) => b.label)).toEqual(['PVE', 'K8s']);
});
});
describe('withBadgeVersion via getInfrastructureSystemIdentityBadges — branch coverage', () => {
it('strips the Proxmox pve-manager wrapper format down to the dotted version', () => {
const badges = getInfrastructureSystemIdentityBadges(
@@ -923,36 +899,3 @@ describe('getInfrastructureSystemIdentityBadges — end-to-end branch coverage',
expect(getInfrastructureSystemIdentityBadges(resource)).toEqual([]);
});
});
describe('getInfrastructureSystemIdentitySortLabel — branch coverage', () => {
it('returns the first identity badge label when one resolves', () => {
const resource = makeResource({
type: 'k8s-node',
platformType: 'kubernetes',
sourceType: 'api',
sources: ['kubernetes'],
platformData: { sources: ['kubernetes'], kubernetes: { version: '1.28.4' } },
});
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('K8s 1.28.4');
});
it('returns the platform badge label when no system identity is found', () => {
const resource = makeResource({
type: 'agent',
platformType: 'truenas',
sourceType: 'agent',
platformData: { sources: ['agent'] },
});
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('TrueNAS');
});
it('returns "" when no identity, no platform badge, and no platformType resolve', () => {
const resource = makeResource({
type: 'storage',
platformType: '' as Resource['platformType'],
sourceType: 'agent',
sources: [],
});
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('');
});
});
@@ -5,7 +5,6 @@ import {
getContainerRuntimeBadgeForRuntime,
getInfrastructurePlatformBadges,
getInfrastructureSystemIdentityBadges,
getInfrastructureSystemIdentitySortLabel,
getPlatformBadge,
getTypeBadge,
getUnifiedSourceBadges,
@@ -131,16 +130,8 @@ describe('resourceBadgePresentation — dead-arm reachability characterization (
expect(badges.map((b) => b.label)).toEqual(['Docker / Podman']);
});
it('getInfrastructureSystemIdentitySortLabel derives a platform label for an unknown platformType, so the L701 truthy-stop of the `||` chain cannot fire', () => {
// Reaching the L701 operand requires the identity badge list to be empty
// (badges[0]?.label falsy). But the list is only empty when no identity at
// all resolves — which requires platformType to be absent/falsy (a non-empty
// platformType always yields a platform badge at L690). With a falsy
// platformType, getPlatformBadge returns null, so L701's `?.label` is
// always falsy when reached and never stops the chain. A non-empty (even
// unknown) platformType instead produces a non-empty identity list, so the
// sort label resolves at L700 and L701 is never reached.
const label = getInfrastructureSystemIdentitySortLabel(
it('getInfrastructureSystemIdentityBadges derives a platform badge for an unknown platformType', () => {
const badges = getInfrastructureSystemIdentityBadges(
makeResource({
type: 'vm',
platformType: 'fictional-hypervisor-xyz' as Resource['platformType'],
@@ -148,7 +139,7 @@ describe('resourceBadgePresentation — dead-arm reachability characterization (
sources: [],
}),
);
expect(label).toBe('Fictional Hypervisor Xyz');
expect(badges.map((b) => b.label)).toEqual(['Fictional Hypervisor Xyz']);
});
it('treats a whitespace-only agent.platform as absent, so the `if (!normalized) return null` guards at L271/L287 in the host-identity helpers cannot fire', () => {
@@ -3,7 +3,6 @@ import {
getContainerRuntimeBadgeForRuntime,
getInfrastructurePlatformBadges,
getInfrastructureSystemIdentityBadges,
getInfrastructureSystemIdentitySortLabel,
getPlatformBadge,
getTypeBadge,
getUnifiedSourceBadges,
@@ -498,7 +497,7 @@ describe('getVersionedSourceBadge via getInfrastructureSystemIdentityBadges (bra
});
});
describe('getInfrastructureSystemIdentityBadges and getInfrastructureSystemIdentitySortLabel (branch coverage)', () => {
describe('getInfrastructureSystemIdentityBadges fallbacks (branch coverage)', () => {
it('falls back to the platform badge for a bare agent resource with no other identity', () => {
const resource = makeResource({
type: 'agent',
@@ -507,10 +506,9 @@ describe('getInfrastructureSystemIdentityBadges and getInfrastructureSystemIdent
sources: [],
});
expect(getInfrastructureSystemIdentityBadges(resource).map((b) => b.label)).toEqual(['Agent']);
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('Agent');
});
it('returns an empty badge set and empty sort label when nothing resolves', () => {
it('returns an empty badge set when nothing resolves', () => {
const resource = makeResource({
id: 'lonely',
name: 'lonely',
@@ -520,6 +518,5 @@ describe('getInfrastructureSystemIdentityBadges and getInfrastructureSystemIdent
sources: [],
});
expect(getInfrastructureSystemIdentityBadges(resource)).toEqual([]);
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('');
});
});
@@ -4,8 +4,6 @@ import {
getContainerRuntimeBadgeForRuntime,
getInfrastructurePlatformBadges,
getInfrastructureSystemIdentityBadges,
getInfrastructureSystemIdentitySortLabel,
getInfrastructureSystemTitleBadges,
getPlatformBadge,
getSourceBadge,
getTypeBadge,
@@ -158,7 +156,6 @@ describe('resourceBadgePresentation', () => {
expect(
getInfrastructureSystemIdentityBadges(unraidStorage).map((badge) => badge.label),
).toEqual(['Unraid']);
expect(getInfrastructureSystemIdentitySortLabel(unraidStorage)).toBe('Unraid');
const pbsDatastore = makeResource({
type: 'storage',
@@ -278,7 +275,6 @@ describe('resourceBadgePresentation', () => {
expect(getInfrastructureSystemIdentityBadges(resource).map((badge) => badge.label)).toEqual([
'PVE',
]);
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('PVE');
});
it('shows platform versions when the reported version belongs to the platform identity', () => {
@@ -319,7 +315,7 @@ describe('resourceBadgePresentation', () => {
);
});
it('does not repeat the unversioned platform source after a versioned system identity', () => {
it('takes the platform version from the agent report when the platform facet has none', () => {
const agentDiscoveredPve = makeResource({
type: 'agent',
platformType: 'proxmox-pve',
@@ -343,9 +339,6 @@ describe('resourceBadgePresentation', () => {
expect(systemBadges.map((badge) => badge.label)).toEqual(['PVE 8.3.3']);
expect(sourceBadges.map((badge) => badge.label)).toEqual(['PVE', 'Agent']);
expect(
getInfrastructureSystemTitleBadges(systemBadges, sourceBadges).map((badge) => badge.label),
).toEqual(['PVE 8.3.3', 'Agent']);
});
it('prefers platform-facet version over agent OS version on hybrid-source resources', () => {
@@ -391,7 +384,6 @@ describe('resourceBadgePresentation', () => {
expect(getInfrastructureSystemIdentityBadges(resource).map((badge) => badge.label)).toEqual([
'PVE',
]);
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('PVE');
});
it('renders Proxmox LXC Docker hosts with a canonical LXC badge and surfaces the VMID in the tooltip', () => {
@@ -449,7 +441,6 @@ describe('resourceBadgePresentation', () => {
expect(getInfrastructureSystemIdentityBadges(resource)[0]?.title).toBe(
'TCP availability probe power-meter-01.lab.local:1883',
);
expect(getInfrastructureSystemIdentitySortLabel(resource)).toBe('TCP');
});
it('uses ICMP identity for ping-only availability endpoints', () => {
@@ -159,20 +159,6 @@ const withBadgeVersion = (badge: ResourceBadge, version: string): ResourceBadge
};
};
const VERSIONED_BADGE_SUFFIX_PATTERN = /\s+v?\d+(?:[._-]\d+)*(?:[-+~:][0-9a-z][0-9a-z._:+~-]*)?$/i;
const normalizeBadgeIdentityLabel = (value: string): string =>
value.trim().toLowerCase().replace(VERSIONED_BADGE_SUFFIX_PATTERN, '').trim();
const badgeIdentityLabels = (badge: ResourceBadge): string[] =>
Array.from(
new Set(
[badge.label, badge.title]
.map((value) => (value ? normalizeBadgeIdentityLabel(value) : ''))
.filter(Boolean),
),
);
const normalizeUnifiedSourceKeys = (sources?: string[] | null): KnownSourcePlatform[] => {
if (!sources || sources.length === 0) return [];
const normalized = sources
@@ -695,15 +681,6 @@ export function getInfrastructureSystemIdentityBadges(resource: Resource): Resou
return getInfrastructurePlatformBadges(rawSources);
}
export function getInfrastructureSystemIdentitySortLabel(resource: Resource): string {
return (
getInfrastructureSystemIdentityBadges(resource)[0]?.label ||
getPlatformBadge(resource.platformType)?.label ||
resource.platformType ||
''
);
}
export function dedupeResourceBadges(
badges: Array<ResourceBadge | null | undefined>,
): ResourceBadge[] {
@@ -717,19 +694,6 @@ export function dedupeResourceBadges(
});
}
export function getInfrastructureSystemTitleBadges(
systemBadges: ResourceBadge[],
sourceBadges: ResourceBadge[],
): ResourceBadge[] {
const systemIdentities = new Set(systemBadges.flatMap(badgeIdentityLabels));
return dedupeResourceBadges([
...systemBadges,
...sourceBadges.filter((badge) =>
badgeIdentityLabels(badge).every((identity) => !systemIdentities.has(identity)),
),
]);
}
const getContainerRuntimeLabel = (runtime?: string | null): string => {
const raw = (runtime || '').trim();
const normalized = raw.toLowerCase();