Correct VMware platform readiness classification

Move vSphere back to the admitted first-lab-ready stage in the support manifest, regenerate shared projections, and keep onboarding/navigation copy from presenting VMware as fully supported before live vCenter proof.
This commit is contained in:
rcourtman 2026-05-22 09:13:29 +01:00
parent 3cd1517883
commit abf2203307
17 changed files with 111 additions and 93 deletions

View file

@ -290,8 +290,8 @@
"id": "vmware-vsphere",
"family": "VMware",
"surface_kind": "platform",
"governance_state": "supported",
"readiness_stage": "supported",
"governance_state": "admitted",
"readiness_stage": "first-lab-ready",
"primary_mode": "api-backed",
"onboarding_paths": [
"platform-connections"

View file

@ -261,7 +261,6 @@ Rules:
4. `proxmox-pbs`
5. `proxmox-pmg`
6. `truenas`
7. `vmware-vsphere`
### Runtime lenses
@ -269,7 +268,7 @@ Rules:
### Admitted platforms (not yet supported)
(none)
1. `vmware-vsphere`
### Presentation-only platform vocabulary
@ -309,7 +308,6 @@ Support floor fields are recorded in this order: `setup`, `visibility`,
| `proxmox-pbs` | `platform` | `supported` | `supported` | `api-backed` | `platform-connections` | `pbs`, `storage` | `setup=supported`; `visibility=supported`; `workloads=n/a`; `storage=supported`; `recovery=supported`; `alerts=supported`; `assistant_read=supported`; `assistant_control=read-only` |
| `proxmox-pmg` | `platform` | `supported` | `supported` | `api-backed` | `platform-connections` | `pmg` | `setup=supported`; `visibility=supported`; `workloads=n/a`; `storage=n/a`; `recovery=n/a`; `alerts=supported`; `assistant_read=supported`; `assistant_control=read-only` |
| `truenas` | `platform` | `supported` | `supported` | `api-backed` | `platform-connections` | `agent`, `vm`, `app-container`, `network-share`, `storage`, `physical-disk` | `setup=supported`; `visibility=supported`; `workloads=supported`; `storage=supported`; `recovery=supported`; `alerts=supported`; `assistant_read=supported`; `assistant_control=supported` |
| `vmware-vsphere` | `platform` | `supported` | `supported` | `api-backed` | `platform-connections` | `agent`, `vm`, `storage` | `setup=supported`; `visibility=supported`; `workloads=supported`; `storage=supported`; `recovery=n/a`; `alerts=supported`; `assistant_read=supported`; `assistant_control=read-only` |
Presentation-only platform vocabulary uses `governance=presentation-only`,
`surface_kind=presentation-only`, `readiness=presentation-only`,
@ -369,7 +367,6 @@ or runtime platform fallbacks that are not declared in this document.
| `proxmox-pbs` | platform | Proxmox | api-backed | host agent may augment into hybrid | `pbs`, `storage` |
| `proxmox-pmg` | platform | Proxmox | api-backed | none today | `pmg` |
| `truenas` | platform | TrueNAS | api-backed | host agent may augment into hybrid | `agent`, `vm`, `app-container`, `network-share`, `storage`, `physical-disk` |
| `vmware-vsphere` | platform | VMware | api-backed | host or guest agent later, not phase 1 | `agent`, `vm`, `storage` |
| Platform | Setup | Visibility | Workloads | Storage | Recovery | Alerts | Assistant read | Assistant control |
| ---------------- | -------------------------------------- | ---------- | --------- | --------- | --------- | --------- | -------------- | ----------------- |
@ -380,7 +377,6 @@ or runtime platform fallbacks that are not declared in this document.
| `proxmox-pbs` | platform connections | supported | `n/a` | supported | supported | supported | supported | read-only |
| `proxmox-pmg` | platform connections | supported | `n/a` | `n/a` | `n/a` | supported | supported | read-only |
| `truenas` | platform connections | supported | supported | supported | supported | supported | supported | supported |
| `vmware-vsphere` | platform connections | supported | supported | supported | `n/a` | supported | supported | read-only |
## Current Inconsistencies To Treat Explicitly
@ -426,10 +422,10 @@ open-decision or resolved-decision update instead of starting runtime code.
## VMware vSphere Admission Model
Pulse now has a resolved architecture recommendation for any future VMware
vSphere work. This does not admit `vmware-vsphere` into the current support
matrix yet. It defines the only acceptable phase-1 model if implementation
starts.
Pulse now has a resolved architecture recommendation for VMware vSphere work.
This admits `vmware-vsphere` only at the `first-lab-ready` checkpoint; it does
not place VMware in the current support matrix yet. It defines the only
acceptable phase-1 model for implementation and proof.
1. treat VMware as one separate first-class platform id,
`vmware-vsphere`, not as another Proxmox subtype and not as a generic

View file

@ -210,7 +210,11 @@ projections, and support-floor posture.
those facts into source-strategy copy, but it must not turn an admitted
`first-lab-ready` platform such as VMware into a product-level supported
claim, invent platform-local projections, or classify assistant control beyond
the manifest's support-floor row.
the manifest's support-floor row. Admitted API platforms may remain available
inside the add-flow source picker and source-manager summaries, but their
operator-facing badge must be preview/admission language and empty-state copy
must keep them separate from the supported-source list until the governed live
proof promotes the platform.
That same lifecycle-owned helper must keep first-class platform APIs separate
from governed Pulse Agent host profiles. Host/appliance compatibility such as
Unraid is presented as an agent install/profile path sourced from the manifest

View file

@ -766,7 +766,7 @@ not a replacement status card, CTA band, or page-local nested card.
the container-runtime lens and may be labelled `Containers` in the shell,
while shared source badges, filters, and runtime management copy continue
to use `Docker / Podman` where the capability itself is being named.
8. Keep shared source/platform vocabulary on the governed manifest boundary. `frontend-modern/src/utils/platformSupportManifest.generated.ts` must be the tracked frontend projection of `docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json`, `frontend-modern/src/utils/platformSupportManifest.ts`, `frontend-modern/src/utils/sourcePlatforms.ts`, and `frontend-modern/src/utils/sourcePlatformOptions.ts` must consume that generated projection instead of embedding divergent future-label lists, setup/onboarding path allowlists, host-profile labels, surface-kind guesses, or presentation-only guesses, and `frontend-modern/scripts/canonical-platform-audit.mjs` must fail when the generated projection drifts from the governed manifest. The generated `surface_kind` is the machine-readable boundary between owning platform entries and runtime lenses: `docker` is a `runtime-lens`, not a `platform`, even when the container-runtime route stays available as a primary shell destination. The generic `docker` source-platform label is "Docker / Podman" in shared selectors, badges, and filter options so v5 Docker users can find the runtime surface while Podman-backed rows are not mislabeled as Docker-only; "Container runtime" remains the governed runtime family, not the primary customer-facing label. Identity colour is semantic, not page-local decoration: shared source/platform badges, host identity badges, and container runtime badges must use the shared presentation helpers so Docker remains on the Docker/Podman blue runtime tone, Podman uses its distinct runtime tone, Proxmox PVE remains orange, and those meanings do not drift across table rows, filters, drawers, or platform pages. Agent host-profile entries, including Unraid, stay in the generated `agentHostProfiles` projection and shared wrapper helpers; frontend primitives may render those labels for Pulse Agent install/identity copy but must not add them to the first-class platform union.
8. Keep shared source/platform vocabulary on the governed manifest boundary. `frontend-modern/src/utils/platformSupportManifest.generated.ts` must be the tracked frontend projection of `docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json`, `frontend-modern/src/utils/platformSupportManifest.ts`, `frontend-modern/src/utils/sourcePlatforms.ts`, and `frontend-modern/src/utils/sourcePlatformOptions.ts` must consume that generated projection instead of embedding divergent future-label lists, setup/onboarding path allowlists, host-profile labels, surface-kind guesses, readiness-state guesses, or presentation-only guesses, and `frontend-modern/scripts/canonical-platform-audit.mjs` must fail when the generated projection drifts from the governed manifest. The generated governance/readiness split is authoritative: supported platform arrays drive current support claims, while admitted platform arrays may keep route/navigation and add-flow vocabulary available without turning `first-lab-ready` entries such as VMware into supported-source copy. The generated `surface_kind` is the machine-readable boundary between owning platform entries and runtime lenses: `docker` is a `runtime-lens`, not a `platform`, even when the container-runtime route stays available as a primary shell destination. The generic `docker` source-platform label is "Docker / Podman" in shared selectors, badges, and filter options so v5 Docker users can find the runtime surface while Podman-backed rows are not mislabeled as Docker-only; "Container runtime" remains the governed runtime family, not the primary customer-facing label. Identity colour is semantic, not page-local decoration: shared source/platform badges, host identity badges, and container runtime badges must use the shared presentation helpers so Docker remains on the Docker/Podman blue runtime tone, Podman uses its distinct runtime tone, Proxmox PVE remains orange, and those meanings do not drift across table rows, filters, drawers, or platform pages. Agent host-profile entries, including Unraid, stay in the generated `agentHostProfiles` projection and shared wrapper helpers; frontend primitives may render those labels for Pulse Agent install/identity copy but must not add them to the first-class platform union.
The generated host-profile projection also carries runtime platform fallback
metadata for shared explanation and parity with backend normalization, but
frontend primitives must still render host-profile labels through
@ -3316,7 +3316,10 @@ That same shared settings-shell boundary also owns provider parity inside the
inline add flow. Adding VMware may extend the same card, empty-state, dialog,
and summary-shell patterns used by TrueNAS, but it must not introduce a
VMware-only outer page shell, alternate settings route hierarchy, or another
summary vocabulary for connection health and contribution counts.
summary vocabulary for connection health and contribution counts. While VMware
remains admitted rather than supported, shared settings primitives must render
its source-picker card with the manifest-derived preview badge and keep
supported-source empty-state copy from listing VMware as available now.
That same shared filter-presentation boundary also owns infrastructure
route-filter continuity. `frontend-modern/src/features/infrastructure/`
must keep a route-owned canonical source option such as `truenas` visible in

View file

@ -1021,7 +1021,11 @@ must stay generated from
`frontend-modern/src/types/resource.ts` must derive `PlatformType` from that
generated supported-plus-admitted projection rather than hand-maintaining a
second platform union that can drift from the governed manifest or re-admit
presentation-only labels by mistake. Agent host profiles are generated beside
presentation-only labels by mistake. The supported/admitted split in that
projection is also contractual: VMware may stay a valid `PlatformType` and
source-platform alias while admitted, but unified-resource consumers must not
infer current platform support from that union without checking governance
state/readiness. Agent host profiles are generated beside
that platform projection for shared identity/presentation use only; a profile
such as Unraid may label a Pulse Agent host, but it must not enter
`PlatformType`, `PLATFORM_TYPE_KEYS`, unified-resource source filters, or

View file

@ -7,7 +7,7 @@ describe('InfrastructureSourcePicker', () => {
cleanup();
});
it('labels admitted VMware as Early support and keeps Pulse Agent on the host profile path', () => {
it('labels admitted VMware as Preview and keeps Pulse Agent on the host profile path', () => {
render(() => <InfrastructureSourcePicker onSelectStep={vi.fn()} />);
expect(screen.getByPlaceholderText('Search platforms, hosts, services...')).toBeInTheDocument();
@ -17,10 +17,10 @@ describe('InfrastructureSourcePicker', () => {
expect(screen.getByText('Choose how Pulse should connect')).toBeInTheDocument();
expect(screen.getByText('Or pick a specific platform')).toBeInTheDocument();
// VMware lives behind 'Show more platforms' since it is a less common
// homelab choice; expand the long tail to verify the Early support
// badge still renders for it.
// homelab choice; expand the long tail to verify the Preview badge still
// renders for it.
fireEvent.click(screen.getByRole('button', { name: /Show \d+ more platforms?/i }));
expect(screen.getByText('Early support')).toBeInTheDocument();
expect(screen.getByText('Preview')).toBeInTheDocument();
expect(screen.queryByText('Available now')).toBeNull();
expect(screen.getByText('Unraid')).toBeInTheDocument();
expect(screen.getByText('Linux, macOS, Windows host')).toBeInTheDocument();

View file

@ -489,7 +489,10 @@ describe('InfrastructureWorkspace', () => {
expect(
screen.getByText('Choose an infrastructure source to start monitoring your environment.'),
).toBeInTheDocument();
expect(screen.getByText(/Supported source types include VMware vCenter/i)).toBeInTheDocument();
expect(screen.getByText(/Supported source types include TrueNAS SCALE/i)).toBeInTheDocument();
expect(
screen.getByText(/VMware vCenter is available as a preview platform/i),
).toBeInTheDocument();
expect(screen.getByText(/standalone hosts through Pulse Agent/i)).toBeInTheDocument();
});

View file

@ -685,7 +685,7 @@ describe('settings architecture guardrails', () => {
'getInfrastructureSupportSummaryBadges',
);
expect(infrastructureOnboardingPresentationSource).toContain(
'Supported source types include VMware vCenter',
'VMware vCenter is available as a preview platform',
);
expect(nodeCredentialSlotSource).toContain('useNodeModalState(modalProps)');

View file

@ -21,7 +21,7 @@ const resource = (overrides: Partial<Resource>): Resource =>
}) as Resource;
describe('infrastructureNavigationModel', () => {
it('shows primary infrastructure destinations only when supported resource evidence is present', () => {
it('shows primary infrastructure destinations only when supported or admitted resource evidence is present', () => {
expect(buildPrimaryInfrastructureNavigationVisibility([])).toEqual({
proxmox: false,
docker: false,

View file

@ -1,6 +1,7 @@
import type { Accessor } from 'solid-js';
import type { Resource, ResourceType } from '@/types/resource';
import {
ADMITTED_PLATFORM_IDS,
SUPPORTED_PLATFORM_IDS,
getSourcePlatformManifestEntry,
type SourcePlatformManifestEntry,
@ -35,7 +36,10 @@ export const PRIMARY_INFRASTRUCTURE_NAV_SCOPE_IDS: Record<
vmware: ['vmware-vsphere'],
};
const SUPPORTED_PLATFORM_ID_SET = new Set<string>(SUPPORTED_PLATFORM_IDS);
const NAVIGABLE_PLATFORM_ID_SET = new Set<string>([
...SUPPORTED_PLATFORM_IDS,
...ADMITTED_PLATFORM_IDS,
]);
const KUBERNETES_RESOURCE_TYPES = new Set<ResourceType>([
'k8s-cluster',
@ -127,7 +131,7 @@ export function collectResourcePlatformEvidence(resource: Resource): string[] {
return [...ids];
}
export function buildSupportedResourceInfrastructureScopeSet(
export function buildNavigableResourceInfrastructureScopeSet(
resources: readonly Resource[],
): Set<string> {
const present = new Set<string>();
@ -136,7 +140,7 @@ export function buildSupportedResourceInfrastructureScopeSet(
const manifestEntry = getSourcePlatformManifestEntry(
platformId,
) as SourcePlatformManifestEntry | null;
if (!manifestEntry || !SUPPORTED_PLATFORM_ID_SET.has(manifestEntry.id)) continue;
if (!manifestEntry || !NAVIGABLE_PLATFORM_ID_SET.has(manifestEntry.id)) continue;
present.add(manifestEntry.id);
}
}
@ -146,22 +150,22 @@ export function buildSupportedResourceInfrastructureScopeSet(
export function buildPrimaryInfrastructureNavigationVisibility(
resources: readonly Resource[],
): InfrastructureNavigationVisibility {
const presentSupportedScopes = buildSupportedResourceInfrastructureScopeSet(resources);
const presentNavigableScopes = buildNavigableResourceInfrastructureScopeSet(resources);
return {
proxmox: PRIMARY_INFRASTRUCTURE_NAV_SCOPE_IDS.proxmox.some((id) =>
presentSupportedScopes.has(id),
presentNavigableScopes.has(id),
),
docker: PRIMARY_INFRASTRUCTURE_NAV_SCOPE_IDS.docker.some((id) =>
presentSupportedScopes.has(id),
presentNavigableScopes.has(id),
),
kubernetes: PRIMARY_INFRASTRUCTURE_NAV_SCOPE_IDS.kubernetes.some((id) =>
presentSupportedScopes.has(id),
presentNavigableScopes.has(id),
),
truenas: PRIMARY_INFRASTRUCTURE_NAV_SCOPE_IDS.truenas.some((id) =>
presentSupportedScopes.has(id),
presentNavigableScopes.has(id),
),
vmware: PRIMARY_INFRASTRUCTURE_NAV_SCOPE_IDS.vmware.some((id) =>
presentSupportedScopes.has(id),
presentNavigableScopes.has(id),
),
};
}

View file

@ -17,12 +17,12 @@ import {
} from '@/utils/infrastructureOnboardingPresentation';
describe('infrastructureOnboardingPresentation', () => {
it('keeps VMware on the supported vCenter path', () => {
it('keeps VMware on the admitted vCenter path until live proof lands', () => {
const vmware = getInfrastructureOnboardingProductPresentation('vmware');
expect(vmware.label).toBe('VMware vCenter');
expect(vmware.governanceState).toBe('supported');
expect(vmware.readinessStage).toBe('supported');
expect(vmware.governanceState).toBe('admitted');
expect(vmware.readinessStage).toBe('first-lab-ready');
expect(vmware.primaryMode).toBe('api-backed');
expect(vmware.canonicalProjections).toEqual(['agent', 'vm', 'storage']);
expect(vmware.supportFloor).toMatchObject({
@ -87,19 +87,22 @@ describe('infrastructureOnboardingPresentation', () => {
expect(INFRASTRUCTURE_ONBOARDING_PATHS.agent.title).toBe('Install Pulse Agent');
});
it('lists all supported API products alongside vSphere now that VMware is promoted', () => {
it('separates supported API products from admitted preview products', () => {
expect(
getInfrastructureApiProductsByGovernanceState('supported').map((product) => product.label),
).toEqual([
'VMware vCenter',
'TrueNAS SCALE',
'Proxmox VE',
'Proxmox Backup Server',
'Proxmox Mail Gateway',
'Network endpoint',
]);
expect(
getInfrastructureApiProductsByGovernanceState('admitted').map((product) => product.label),
).toEqual(['VMware vCenter']);
expect(getInfrastructureGovernanceBadgeLabel('supported', 'supported')).toBeNull();
expect(getInfrastructureGovernanceBadgeLabel('admitted', 'first-lab-ready')).toBe('Preview');
});
it('derives picker items, auto-detect copy, and landing summaries from the shared helper', () => {
@ -201,7 +204,6 @@ describe('infrastructureOnboardingPresentation', () => {
expect(getInfrastructureSupportSummaryBadges()).toMatchObject({
supportedToday: [
'VMware vCenter',
'TrueNAS SCALE',
'Proxmox VE',
'Proxmox Backup Server',
@ -211,7 +213,7 @@ describe('infrastructureOnboardingPresentation', () => {
'Docker',
'Kubernetes',
],
currentAdmissionPath: [],
currentAdmissionPath: ['VMware vCenter'],
installPath: expect.arrayContaining([
'Linux',
'FreeBSD',
@ -226,7 +228,10 @@ describe('infrastructureOnboardingPresentation', () => {
'Choose an infrastructure source to start monitoring your environment.',
);
expect(getInfrastructureEmptyStateDetail()).toContain(
'Supported source types include VMware vCenter',
'Supported source types include TrueNAS SCALE',
);
expect(getInfrastructureEmptyStateDetail()).toContain(
'VMware vCenter is available as a preview platform',
);
expect(getInfrastructureEmptyStateDetail()).toContain('standalone hosts through Pulse Agent');
expect(getInfrastructureEmptyStateDetail()).toContain('Docker and Kubernetes are discovered');

View file

@ -11,6 +11,7 @@ import {
resolveSourceTypeFromSources,
} from '@/utils/sourcePlatforms';
import {
ADMITTED_PLATFORM_IDS,
AGENT_HOST_PROFILE_IDS,
PLATFORM_TYPE_KEYS,
PRESENTATION_ONLY_PLATFORM_IDS,
@ -160,8 +161,9 @@ describe('sourcePlatforms', () => {
});
describe('governed platform support projection', () => {
it('keeps VMware on the supported vCenter-backed floor', () => {
expect(getSourcePlatformReadinessStage('vmware')).toBe('supported');
it('keeps VMware on the admitted vCenter-backed floor until live proof lands', () => {
expect(ADMITTED_PLATFORM_IDS).toEqual(['vmware-vsphere']);
expect(getSourcePlatformReadinessStage('vmware')).toBe('first-lab-ready');
expect(getSourcePlatformCanonicalProjections('vmware')).toEqual(['agent', 'vm', 'storage']);
expect(getSourcePlatformSupportFloor('vmware')).toMatchObject({
setup: 'supported',
@ -197,6 +199,7 @@ describe('sourcePlatforms', () => {
expect(SUPPORTED_RUNTIME_LENS_IDS).toEqual(['docker']);
expect(SUPPORTED_OWNING_PLATFORM_IDS).toContain('proxmox-pve');
expect(SUPPORTED_OWNING_PLATFORM_IDS).not.toContain('docker');
expect(SUPPORTED_OWNING_PLATFORM_IDS).not.toContain('vmware-vsphere');
});
});

View file

@ -129,11 +129,12 @@ export const getInfrastructureAgentHostProfileSupportText = (): string =>
export const getInfrastructureGovernanceBadgeLabel = (
governanceState: PlatformGovernanceState,
_readinessStage: PlatformReadinessStage,
readinessStage: PlatformReadinessStage,
): string | null => {
if (governanceState === 'supported') return null;
if (governanceState === 'presentation-only') return 'Presentation only';
return null;
if (readinessStage === 'first-lab-ready') return 'Preview';
return 'Preview';
};
const SOURCE_STRATEGY_PRESENTATION: Record<
@ -533,10 +534,9 @@ export const getInfrastructureSupportSummaryBadges = (): {
...getInfrastructureApiProductsByGovernanceState('supported').map((product) => product.label),
...INFRASTRUCTURE_AGENT_DISCOVERY_LABELS,
],
// No platforms currently sit in the admitted staging area; the type is
// narrowed to the live governance states, so the admission-path summary
// is empty until a future platform is admitted-but-not-yet-supported.
currentAdmissionPath: [],
currentAdmissionPath: getInfrastructureApiProductsByGovernanceState('admitted').map(
(product) => product.label,
),
installPath: [...INFRASTRUCTURE_AGENT_HOST_LABELS, ...INFRASTRUCTURE_AGENT_DISCOVERY_LABELS],
});
@ -544,7 +544,7 @@ export const getInfrastructureEmptyStateSummary = (): string =>
'Choose an infrastructure source to start monitoring your environment.';
export const getInfrastructureEmptyStateDetail = (): string =>
'Supported source types include VMware vCenter, TrueNAS SCALE, Proxmox VE, Proxmox Backup Server, Proxmox Mail Gateway, network endpoints, and standalone hosts through Pulse Agent. Docker and Kubernetes are discovered from supported agent hosts.';
'Supported source types include TrueNAS SCALE, Proxmox VE, Proxmox Backup Server, Proxmox Mail Gateway, network endpoints, and standalone hosts through Pulse Agent. VMware vCenter is available as a preview platform pending live support proof. Docker and Kubernetes are discovered from supported agent hosts.';
export const getInfrastructureCoverageCompleteActionPresentation =
(): InfrastructureCoverageCompleteActionPresentation => ({

View file

@ -1,11 +1,11 @@
// This file is generated by scripts/release_control/generate_platform_support_frontend_module.py.
// Do not edit by hand.
// Source: docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json
// Source SHA256: 11c137eb7f586114e9f835bc5429f6cae81906e6ce8eb13413df1e51752f31a0
// Source SHA256: 97b7ac58c2c981867dc4052728587d323fc541221135e6bada1191d9d723206b
export const PLATFORM_SUPPORT_MANIFEST_SOURCE = {
path: 'docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json',
sha256: '11c137eb7f586114e9f835bc5429f6cae81906e6ce8eb13413df1e51752f31a0',
sha256: '97b7ac58c2c981867dc4052728587d323fc541221135e6bada1191d9d723206b',
} as const;
export const PLATFORM_SUPPORT_MANIFEST = {
schemaVersion: 2,
@ -227,8 +227,8 @@ export const PLATFORM_SUPPORT_MANIFEST = {
id: 'vmware-vsphere',
family: 'VMware',
surfaceKind: 'platform',
governanceState: 'supported',
readinessStage: 'supported',
governanceState: 'admitted',
readinessStage: 'first-lab-ready',
primaryMode: 'api-backed',
onboardingPaths: ['platform-connections'],
canonicalProjections: ['agent', 'vm', 'storage'],
@ -412,7 +412,6 @@ export const SUPPORTED_PLATFORM_IDS = [
'proxmox-pbs',
'proxmox-pmg',
'truenas',
'vmware-vsphere',
] as const;
export const SUPPORTED_OWNING_PLATFORM_IDS = [
'agent',
@ -421,10 +420,9 @@ export const SUPPORTED_OWNING_PLATFORM_IDS = [
'proxmox-pbs',
'proxmox-pmg',
'truenas',
'vmware-vsphere',
] as const;
export const SUPPORTED_RUNTIME_LENS_IDS = ['docker'] as const;
export const ADMITTED_PLATFORM_IDS = [] as const;
export const ADMITTED_PLATFORM_IDS = ['vmware-vsphere'] as const;
export const PRESENTATION_ONLY_PLATFORM_IDS = [
'unraid',
'synology-dsm',
@ -601,7 +599,7 @@ export const SOURCE_PLATFORM_READINESS_STAGE = {
'proxmox-pbs': 'supported',
'proxmox-pmg': 'supported',
truenas: 'supported',
'vmware-vsphere': 'supported',
'vmware-vsphere': 'first-lab-ready',
unraid: 'presentation-only',
'synology-dsm': 'presentation-only',
'microsoft-hyperv': 'presentation-only',

View file

@ -172,8 +172,12 @@ func TestPlatformSupportManifestMatchesSupportModel(t *testing.T) {
); diff != "" {
t.Fatalf("agent host profile storage-family drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(supportedSurfaces, manifest.DefaultInfrastructureSourceOrder); diff != "" {
t.Fatalf("default infrastructure source ordering drifted from the canonical supported platform set:\n%s", diff)
defaultInfrastructureSources := uniqueSortedPlatforms(append(
append([]string(nil), supportedSurfaces...),
admitted...,
))
if diff := diffPlatformSets(defaultInfrastructureSources, manifest.DefaultInfrastructureSourceOrder); diff != "" {
t.Fatalf("default infrastructure source ordering drifted from the canonical supported/admitted platform set:\n%s", diff)
}
}
@ -182,14 +186,13 @@ func TestMockCoverageMatchesCurrentSupportedPlatformSet(t *testing.T) {
supported := manifestPlatformsByState(t, manifest, "supported")
checkers := map[string]func(*testing.T, FixtureGraph){
"agent": assertAgentMockCoverage,
"docker": assertDockerMockCoverage,
"kubernetes": assertKubernetesMockCoverage,
"proxmox-pbs": assertProxmoxPBSMockCoverage,
"proxmox-pmg": assertProxmoxPMGMockCoverage,
"proxmox-pve": assertProxmoxPVEMockCoverage,
"truenas": assertTrueNASMockCoverage,
"vmware-vsphere": assertVMwareMockCoverage,
"agent": assertAgentMockCoverage,
"docker": assertDockerMockCoverage,
"kubernetes": assertKubernetesMockCoverage,
"proxmox-pbs": assertProxmoxPBSMockCoverage,
"proxmox-pmg": assertProxmoxPMGMockCoverage,
"proxmox-pve": assertProxmoxPVEMockCoverage,
"truenas": assertTrueNASMockCoverage,
}
if diff := diffPlatformSets(supported, sortedPlatformKeys(checkers)); diff != "" {
@ -203,24 +206,24 @@ func TestMockCoverageMatchesCurrentSupportedPlatformSet(t *testing.T) {
}
}
func TestVMwareFixturesRemainSupportedAtPhase1Floor(t *testing.T) {
func TestVMwareFixturesRemainAdmittedAtPhase1Floor(t *testing.T) {
model := loadPlatformSupportModel(t)
manifest := loadPlatformSupportManifest(t)
supported := manifestPlatformsByState(t, manifest, "supported")
admitted := manifestPlatformsByState(t, manifest, "admitted")
presentationOnly := manifestPlatformsByState(t, manifest, "presentation-only")
if !containsPlatform(supported, "vmware-vsphere") {
t.Fatal("vmware-vsphere must appear in the current supported platform set after promotion from first-lab-ready")
if containsPlatform(supported, "vmware-vsphere") {
t.Fatal("vmware-vsphere must not appear in the current supported platform set before live vCenter proof")
}
if containsPlatform(admitted, "vmware-vsphere") {
t.Fatal("vmware-vsphere must not remain in the admitted set after promotion to supported")
if !containsPlatform(admitted, "vmware-vsphere") {
t.Fatal("vmware-vsphere must remain in the admitted set until live vCenter proof promotes it")
}
if containsPlatform(presentationOnly, "vmware-vsphere") {
t.Fatal("vmware-vsphere must not regress into presentation-only vocabulary once supported")
t.Fatal("vmware-vsphere must not regress into presentation-only vocabulary while admitted")
}
if !strings.Contains(model, "| `vmware-vsphere` |") {
t.Fatal("expected platform support model to keep the vmware-vsphere support row")
if !strings.Contains(model, "| `vmware-vsphere` | VMware | `vCenter` only in phase 1 |") {
t.Fatal("expected platform support model to keep the vmware-vsphere admission row")
}
vmwareOnboardingPaths, ok := manifestPlatformOnboardingPaths(manifest)["vmware-vsphere"]
if !ok {
@ -230,8 +233,11 @@ func TestVMwareFixturesRemainSupportedAtPhase1Floor(t *testing.T) {
t.Fatalf("vmware-vsphere onboarding path drifted from the support model:\n%s", diff)
}
vmwareManifest := requireManifestPlatform(t, manifest, "vmware-vsphere")
if vmwareManifest.ReadinessStage != "supported" {
t.Fatalf("vmware readiness stage = %q, want supported", vmwareManifest.ReadinessStage)
if vmwareManifest.GovernanceState != "admitted" {
t.Fatalf("vmware governance state = %q, want admitted", vmwareManifest.GovernanceState)
}
if vmwareManifest.ReadinessStage != "first-lab-ready" {
t.Fatalf("vmware readiness stage = %q, want first-lab-ready", vmwareManifest.ReadinessStage)
}
if vmwareManifest.PrimaryMode != "api-backed" {
t.Fatalf("vmware primary mode = %q, want api-backed", vmwareManifest.PrimaryMode)
@ -247,15 +253,7 @@ func TestVMwareFixturesRemainSupportedAtPhase1Floor(t *testing.T) {
}
graph := buildFixtureGraph(DefaultConfig, time.Date(2026, time.April, 10, 12, 0, 0, 0, time.UTC))
if len(graph.PlatformFixtures.VMware.Hosts) == 0 {
t.Fatal("expected VMware mock fixtures to include hosts")
}
if len(graph.PlatformFixtures.VMware.VMs) == 0 {
t.Fatal("expected VMware mock fixtures to include VMs")
}
if len(graph.PlatformFixtures.VMware.Datastores) == 0 {
t.Fatal("expected VMware mock fixtures to include datastores")
}
assertVMwareMockCoverage(t, graph)
fixture := DefaultVMwareConnectionFixture()
if fixture.CollectedAt.IsZero() {

View file

@ -1,7 +1,7 @@
// Code generated by scripts/release_control/generate_platform_support_backend_module.py.
// DO NOT EDIT.
// Source: docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json
// Source SHA256: 921181082d41829ac4884d00175791157ac500a27888ca2b81f09a9e97989bda
// Source SHA256: 97b7ac58c2c981867dc4052728587d323fc541221135e6bada1191d9d723206b
package platformsupport
@ -13,7 +13,7 @@ const ManifestSourcePath = "docs/release-control/v6/internal/PLATFORM_SUPPORT_MA
// ManifestSourceSHA256 is the sha256 of the manifest bytes used to produce
// this projection.
const ManifestSourceSHA256 = "921181082d41829ac4884d00175791157ac500a27888ca2b81f09a9e97989bda"
const ManifestSourceSHA256 = "97b7ac58c2c981867dc4052728587d323fc541221135e6bada1191d9d723206b"
// ManifestSchemaVersion is the schema_version field of the manifest.
const ManifestSchemaVersion = 2

View file

@ -377,6 +377,9 @@ def normalize_manifest(raw_manifest: dict[str, Any]) -> dict[str, Any]:
supported_ids = [
platform["id"] for platform in platforms if platform["governanceState"] == "supported"
]
admitted_ids = [
platform["id"] for platform in platforms if platform["governanceState"] == "admitted"
]
supported_owning_platform_ids = [
platform["id"]
for platform in platforms
@ -387,11 +390,11 @@ def normalize_manifest(raw_manifest: dict[str, Any]) -> dict[str, Any]:
for platform in platforms
if platform["governanceState"] == "supported" and platform["surfaceKind"] == "runtime-lens"
]
supported_id_set = set(supported_ids)
infrastructure_source_id_set = set([*supported_ids, *admitted_ids])
for platform_id in default_order:
if platform_id not in supported_id_set:
if platform_id not in infrastructure_source_id_set:
raise ValueError(
"default_infrastructure_source_order may only contain supported platforms; "
"default_infrastructure_source_order may only contain supported or admitted platforms; "
f"got {platform_id}"
)
@ -458,9 +461,6 @@ def normalize_manifest(raw_manifest: dict[str, Any]) -> dict[str, Any]:
storage_family_by_id = {
platform["id"]: platform["storageFamily"] for platform in platforms
}
admitted_ids = [
platform["id"] for platform in platforms if platform["governanceState"] == "admitted"
]
presentation_only_ids = [
platform["id"]
for platform in platforms