Retain reviewed main work alongside initial upstream vSphere guidance

Incorporate the upstream frontier present at coordination start without rewriting reviewed candidate history.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-05 03:51:12 +01:00
6 changed files with 134 additions and 1 deletions
@@ -8105,6 +8105,14 @@ read, confirm clear, then release the obsolete response at desktop and phone
widths. Scripted API responses establish component behaviour, not installed
backend deletion or destination delivery.
### Platform overviews may lead with their own incident table
A platform Overview that leads with its provider's health signals reuses
that platform's existing incident table rather than a new summary card:
the vSphere Overview renders `VsphereAlertsTable` with `showToolbar={false}`
above its hosts table, only when signals exist, matching the TrueNAS
Overview's health-alert table. Filtering stays on the platform's Health tab.
### Large platform notices keep the inventory in view
`PlatformOutdatedAgentNotice` is maintenance guidance, not an incident, so it
@@ -5883,3 +5883,16 @@ The Actions page now hides its Patrol Watch only guidance while the
presentation only: the action audit list, approval counts, review dialog,
routing and every recorded action outcome are read and rendered exactly as
before, and no resource or action identity changes.
### vSphere Overview leads with vCenter health signals
The vSphere Overview answers "is anything in trouble?" before inventory, as
the TrueNAS Overview does. While the Overview is active the page also
hydrates the Health tab's resource query (hosts, VMs, datastores and
networks) and, when `buildVmwareIncidentRows` returns any rows from those
resources' canonical `incidents`, renders the shared `VsphereAlertsTable`
above the hosts table without its filter toolbar. Nothing renders when
vCenter reports no signals. The Overview's own model, workload snapshot and
navigation facets still come from the Overview query; the Health tab keeps
the filterable table.
@@ -0,0 +1,34 @@
{
"version": 1,
"base_sha": "efe6fc21ae10a6bf173cc17e6203aaf6c97dc130",
"verified_at": "2026-10-05T00:25:56Z",
"result": "passed",
"changed_paths": [
"frontend-modern/src/features/vmware/VmwarePageSurface.tsx"
],
"content_sha256": {
"frontend-modern/src/features/vmware/VmwarePageSurface.tsx": "cab90be0a9a6868aebac459576f63b93cd4700a8c08fc3ddae4092b7e1fc04bf"
},
"routes": [
"/vmware/overview",
"/vmware/health"
],
"viewports": [
{
"width": 1440,
"height": 1000
},
{
"width": 390,
"height": 844
}
],
"states": [
"Mock Lab vCenter with five warning signals (two datastores, one host, two networks). Before: the Overview showed only hosts and VMs, esxi-07 tinted yellow with no reason; the alarms lived on the fourth tab.",
"After at 1440: a Health Signals table (Resource, Severity, Signal, vCenter, Entity, Started) leads the Overview above Hosts, with no filter toolbar; the yellow esxi-07 row is explained by the Host has VMware overall status yellow signal.",
"Phone 390x844: the same table shows Resource, Signal, VC and Age above the hosts table; rows open the existing detail drawer."
],
"interactions": [
"Loaded the Overview and Health tabs signed in at both widths and inspected the screenshots; component tests cover the toolbar-less table and the overview hydrating the Health query only on Overview and Health."
]
}
@@ -53,6 +53,7 @@ import type { Resource } from '@/types/resource';
import { VsphereHostsTable } from './VsphereHostsTable';
import {
VMWARE_TAB_SPECS,
buildVmwareIncidentRows,
buildVmwarePageModel,
getVmwarePageTabSpecs,
type VmwarePageModel,
@@ -207,7 +208,10 @@ export function VmwarePageSurface() {
const activeTab = createMemo<VmwarePageTabId>(() =>
tabs().some((tab) => tab.id === requestedTab()) ? requestedTab() : 'overview',
);
const shouldHydrateTab = (tab: VmwarePageTabId) => activeTab() === tab;
// The overview leads with vCenter's own health signals, so it reads the
// Health tab's resource set as well as its own hosts and VMs.
const shouldHydrateTab = (tab: VmwarePageTabId) =>
activeTab() === tab || (tab === 'health' && activeTab() === 'overview');
const createTabResources = (tab: VmwarePageTabId) =>
useUnifiedResources({
query: VMWARE_RESOURCE_QUERY_BY_TAB[tab],
@@ -258,6 +262,9 @@ export function VmwarePageSurface() {
},
);
const model = createMemo(() => buildVmwarePageModel(resources(), activityTimeline() ?? []));
const overviewIncidents = createMemo(() =>
activeTab() === 'overview' ? buildVmwareIncidentRows(healthResources.resources()) : [],
);
const agentUpdateTargetVersion = createMemo(
() => updateStore.versionInfo()?.agentUpdateTargetVersion,
);
@@ -343,6 +350,15 @@ export function VmwarePageSurface() {
/>
<Show when={activeTab() === 'overview'}>
<div class="space-y-4">
<Show when={overviewIncidents().length > 0}>
<VsphereAlertsTable
incidents={overviewIncidents()}
emptyIcon={vmwareIcon()}
emptyTitle="No active vSphere health signals"
emptyDescription="vSphere triggered alarms and overall health signals appear here when vCenter reports them."
showToolbar={false}
/>
</Show>
<VmwareOverview
model={model}
metricDisplayMode={metricDisplayMode}
@@ -186,6 +186,50 @@ describe('VmwarePageSurface contract', () => {
);
});
it('leads the overview with vCenter health signals only when vCenter reports any', () => {
const host = makeResource({
id: 'esxi-host-1',
type: 'agent',
vmware: { entityType: 'host', managedObjectId: 'host-1' },
});
const alarmedDatastore = makeResource({
id: 'datastore-1',
type: 'storage',
vmware: { entityType: 'datastore', managedObjectId: 'datastore-1' },
incidents: [{ code: 'vmware_alarm', severity: 'warning', summary: 'Datastore latency' }],
} as Partial<Resource> & Pick<Resource, 'id' | 'type'>);
// Each query gets its own fixture so the test proves the signals come
// from the Health query while the overview model keeps its own resources.
const byCacheKey = (healthResources: Resource[]) =>
mockUseUnifiedResources.mockImplementation((options: { cacheKey?: string }) => {
const resources = options.cacheKey === 'vmware-health' ? healthResources : [host];
return {
resources: () => resources,
loading: () => false,
error: () => null,
refetch: vi.fn().mockResolvedValue(resources),
};
});
byCacheKey([host]);
const { unmount } = render(() => <VmwarePageSurface />);
expect(screen.queryByTestId('alerts-table')).not.toBeInTheDocument();
unmount();
byCacheKey([host, alarmedDatastore]);
render(() => <VmwarePageSurface />);
expect(screen.getByTestId('platform-section-tabs')).toHaveAttribute('data-active', 'overview');
expect(screen.getByTestId('alerts-table')).toBeInTheDocument();
expect(screen.getByTestId('hosts-table')).toHaveAttribute('data-rows', '1');
expect(
mockUseUnifiedResources.mock.calls.some(
([options]) =>
(options as { cacheKey?: string }).cacheKey === 'vmware-health' &&
(options as { enabled: () => boolean }).enabled(),
),
).toBe(true);
});
it('reuses the source-scoped page snapshot for the vSphere workload surface', async () => {
const refetch = vi.fn().mockResolvedValue([]);
const resources = [
@@ -42,6 +42,24 @@ afterEach(() => {
});
describe('VsphereAlertsTable', () => {
it('renders every signal without the filter toolbar for the overview lead-in', () => {
render(() => (
<VsphereAlertsTable
incidents={buildVmwareIncidentRows([makeHost()])}
emptyIcon={<span />}
emptyTitle="No signals"
emptyDescription="No signals"
showToolbar={false}
/>
));
// The overview shows the signals as they are; filtering stays on Health.
expect(screen.queryByRole('button', { name: /Attention/ })).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText('Search vSphere health')).not.toBeInTheDocument();
expect(document.querySelectorAll('[data-vsphere-alert-row]')).toHaveLength(1);
expect(screen.getByText('Health Signals')).toBeInTheDocument();
});
it('renders native vSphere health signal rows with inline details', async () => {
const host = makeHost();
const informational = makeHost({