Land SMART/SSH temperature feature, rc.6 finalization, and post-IA-revert governance reconciliation

This commit is contained in:
rcourtman 2026-05-27 15:27:25 +01:00
parent affc1c76ed
commit 8769f07eea
25 changed files with 922 additions and 3033 deletions

View file

@ -1 +1 @@
6.0.0-rc.5
6.0.0-rc.6

View file

@ -8,22 +8,25 @@
1. The latest shipped Pulse v6 prerelease tag is `v6.0.0-rc.5`.
2. That shipped prerelease tag resolves to commit `e32db04543c7fb20f2cac782dafbf3422a13dfe3`.
3. The governed release profile in `docs/release-control/control_plane.json`
3. The selected remote ref `origin/pulse/v6-release` is still behind the current
local governed branch state, so `Release Dry Run` would exercise stale remote
control-plane metadata instead of the intended candidate.
4. The governed release profile in `docs/release-control/control_plane.json`
currently declares both `prerelease_branch` and `stable_branch` as
`pulse/v6-release`.
4. The active control-plane target is still `v6-product-lane-expansion`, not
5. The active control-plane target is still `v6-product-lane-expansion`, not
`v6-ga-promotion`.
5. The active local `pulse/v6-release` branch currently reports `VERSION=6.0.0-rc.5`, so the
6. The active local `pulse/v6-release` branch currently reports `VERSION=6.0.0-rc.6`, so the
working line is still prerelease and there is not yet a governed local stable
`6.0.0` candidate.
6. There is still no governed `Prerelease-to-GA Rehearsal Record` proving a successful
7. There is still no governed `Prerelease-to-GA Rehearsal Record` proving a successful
non-publish `Release Dry Run` for the eventual stable `6.0.0` candidate.
7. `docs/releases/RELEASE_NOTES_v6.md` and
8. `docs/releases/RELEASE_NOTES_v6.md` and
`docs/release-control/v6/internal/V5_MAINTENANCE_SUPPORT_POLICY.md` now carry the
currently proposed exact dates for the eventual GA notice:
- `v6` GA date: `2026-04-20`
- `v5` end-of-support date: `2026-07-19`
8. There is still no governed `Release Dry Run` artifact or rehearsal record
9. There is still no governed `Release Dry Run` artifact or rehearsal record
exercising stable inputs for:
- `version=6.0.0`
- `promoted_from_tag=v6.0.0-rc.5`
@ -40,7 +43,7 @@
The blocker is no longer missing governance text. The remaining problem is that
the control plane still holds v6 on the pre-GA prerelease line, the working
version is still prerelease (`6.0.0-rc.5`), and there is still no exercised
version is still prerelease (`6.0.0-rc.6`), and there is still no exercised
`Release Dry Run` record proving the eventual stable `6.0.0`
candidate is ready for GA-style promotion. Until that rehearsal exists, stable
users would still be the first real cohort for the final promotion path.

View file

@ -562,11 +562,6 @@
}
],
"evidence": [
{
"repo": "pulse",
"path": "frontend-modern/src/components/Settings/__tests__/MonitoredSystemLedgerPanel.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Settings/__tests__/OrganizationBillingPanel.test.tsx",
@ -1020,11 +1015,6 @@
"path": "frontend-modern/src/components/Settings/__tests__/OrganizationBillingPanel.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/pages/__tests__/HostedSignup.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "internal/api/hosted_lifecycle_integration_test.go",
@ -1312,11 +1302,6 @@
"path": "frontend-modern/src/components/Settings/__tests__/OrganizationBillingPanel.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/pages/__tests__/CloudPricing.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "internal/cloudcp/account/handlers_test.go",
@ -1889,11 +1874,6 @@
"path": "docs/release-control/v6/internal/subsystems/unified-resources.md",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Storage/__tests__/DiskList.test.tsx",
@ -4175,11 +4155,6 @@
"path": "frontend-modern/src/components/Workloads/workloadSelectors.ts",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Workloads/WorkloadsStateCards.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Workloads/WorkloadsSurface.tsx",
@ -4579,16 +4554,6 @@
"path": "docs/STORAGE_ARCHITECTURE.md",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Recovery/Recovery.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Storage/__tests__/storagePageState.test.ts",
@ -4697,11 +4662,6 @@
"path": "frontend-modern/src/api/nodes.ts",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Infrastructure/deploy/ResultsStep.tsx",
"kind": "file"
},
{
"repo": "pulse",
"path": "frontend-modern/src/components/Settings/__tests__/connectionsTableModel.test.ts",

View file

@ -22,6 +22,7 @@ management, and fleet control surfaces.
1. `internal/api/agent_install_command_shared.go`
2. `internal/api/config_setup_handlers.go`
2a. `internal/api/setup_script_render.go`
3. `internal/api/unified_agent.go`
4. `internal/agentupdate/update.go`
5. `internal/hostagent/agent.go`
@ -51,49 +52,38 @@ management, and fleet control surfaces.
28. `frontend-modern/src/components/Settings/discoverySettingsModel.ts`
29. `frontend-modern/src/components/Settings/infrastructureWorkspaceModel.ts`
30. `frontend-modern/src/components/Settings/proxmoxSettingsModel.ts`
31. `frontend-modern/src/components/Settings/ConfiguredNodeTables.tsx`
32. `frontend-modern/src/components/Settings/SettingsSectionNav.tsx`
33. `frontend-modern/src/components/Settings/useInfrastructureOperationsState.tsx`
34. `frontend-modern/src/components/Settings/useInfrastructureSettingsState.ts`
35. `frontend-modern/src/components/Settings/NodeModalAuthenticationSection.tsx`
36. `frontend-modern/src/components/Settings/NodeModalBasicInfoSection.tsx`
37. `frontend-modern/src/components/Settings/nodeModalModel.ts`
38. `frontend-modern/src/components/Settings/NodeModalMonitoringSection.tsx`
39. `frontend-modern/src/components/Settings/NodeModalSetupGuideSection.tsx`
40. `frontend-modern/src/components/Settings/NodeModalStatusFooter.tsx`
41. `frontend-modern/src/components/Settings/useNodeModalState.ts`
42. `frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx`
43. `frontend-modern/src/components/Infrastructure/deploy/CandidatesStep.tsx`
44. `frontend-modern/src/components/Infrastructure/deploy/ConfirmStep.tsx`
45. `frontend-modern/src/components/Infrastructure/deploy/DeployingStep.tsx`
46. `frontend-modern/src/components/Infrastructure/deploy/PreflightStep.tsx`
47. `frontend-modern/src/components/Infrastructure/deploy/ResultsStep.tsx`
48. `frontend-modern/src/utils/agentProfilesPresentation.ts`
49. `frontend-modern/src/utils/agentInstallCommand.ts`
50. `frontend-modern/src/utils/infrastructureOnboardingPresentation.ts`
51. `frontend-modern/src/api/nodes.ts`
52. `frontend-modern/src/components/Settings/InfrastructureInstallerSection.tsx`
53. `frontend-modern/src/components/Settings/useInfrastructureInstallState.tsx`
54. `frontend-modern/src/components/Settings/infrastructureSettingsModel.ts`
55. `frontend-modern/src/components/Settings/useInfrastructureConfiguredNodesState.ts`
56. `frontend-modern/src/components/Settings/useInfrastructureDiscoveryRuntimeState.ts`
57. `frontend-modern/src/utils/infrastructureSettingsPresentation.ts`
54. `frontend-modern/src/utils/agentCapabilityPresentation.ts`
55. `frontend-modern/src/utils/agentProfileSuggestionPresentation.ts`
56. `frontend-modern/src/utils/configuredNodeCapabilityPresentation.ts`
57. `frontend-modern/src/utils/configuredNodeStatusPresentation.ts`
58. `frontend-modern/src/utils/unifiedAgentInventoryPresentation.ts`
59. `frontend-modern/src/utils/unifiedAgentStatusPresentation.ts`
60. `frontend-modern/src/utils/clusterEndpointPresentation.ts`
61. `frontend-modern/src/utils/nodeModalPresentation.ts`
62. `frontend-modern/src/utils/proxmoxSettingsPresentation.ts`
63. `frontend-modern/src/components/Settings/useTrueNASSettingsPanelState.ts`
64. `frontend-modern/src/components/Settings/useVMwareSettingsPanelState.ts`
65. `frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx`
66. `internal/hostagent/proxmox_setup.go`
67. `internal/remoteconfig/client.go`
68. `internal/agenttls/config.go`
69. `internal/api/agent_exec_token_binding.go`
31. `frontend-modern/src/components/Settings/useInfrastructureOperationsState.tsx`
32. `frontend-modern/src/components/Settings/useInfrastructureSettingsState.ts`
33. `frontend-modern/src/components/Settings/NodeModalAuthenticationSection.tsx`
34. `frontend-modern/src/components/Settings/NodeModalBasicInfoSection.tsx`
35. `frontend-modern/src/components/Settings/nodeModalModel.ts`
36. `frontend-modern/src/components/Settings/NodeModalMonitoringSection.tsx`
37. `frontend-modern/src/components/Settings/NodeModalSetupGuideSection.tsx`
38. `frontend-modern/src/components/Settings/NodeModalStatusFooter.tsx`
39. `frontend-modern/src/components/Settings/useNodeModalState.ts`
40. `frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx`
41. `frontend-modern/src/utils/agentProfilesPresentation.ts`
42. `frontend-modern/src/utils/agentInstallCommand.ts`
43. `frontend-modern/src/utils/infrastructureOnboardingPresentation.ts`
44. `frontend-modern/src/api/nodes.ts`
45. `frontend-modern/src/components/Settings/InfrastructureInstallerSection.tsx`
46. `frontend-modern/src/components/Settings/useInfrastructureInstallState.tsx`
47. `frontend-modern/src/components/Settings/infrastructureSettingsModel.ts`
48. `frontend-modern/src/components/Settings/useInfrastructureConfiguredNodesState.ts`
49. `frontend-modern/src/components/Settings/useInfrastructureDiscoveryRuntimeState.ts`
50. `frontend-modern/src/utils/infrastructureSettingsPresentation.ts`
51. `frontend-modern/src/utils/agentCapabilityPresentation.ts`
52. `frontend-modern/src/utils/agentProfileSuggestionPresentation.ts`
53. `frontend-modern/src/utils/unifiedAgentInventoryPresentation.ts`
54. `frontend-modern/src/utils/unifiedAgentStatusPresentation.ts`
55. `frontend-modern/src/utils/nodeModalPresentation.ts`
56. `frontend-modern/src/components/Settings/useTrueNASSettingsPanelState.ts`
57. `frontend-modern/src/components/Settings/useVMwareSettingsPanelState.ts`
58. `frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx`
59. `internal/hostagent/proxmox_setup.go`
60. `internal/remoteconfig/client.go`
61. `internal/agenttls/config.go`
62. `internal/api/agent_exec_token_binding.go`
## Shared Boundaries
@ -136,8 +126,9 @@ reverse.
18. `frontend-modern/src/utils/infrastructureSettingsPresentation.ts` shared with `api-contracts`: the infrastructure settings presentation helper is both an agent lifecycle control surface and an API-backed direct-node/discovery settings boundary.
19. `internal/api/agent_install_command_shared.go` shared with `api-contracts`: agent install command assembly is both an agent lifecycle control surface and a canonical API payload contract boundary.
20. `internal/api/config_setup_handlers.go` shared with `api-contracts`: auto-register and setup handlers are both an agent lifecycle control surface and a canonical API payload contract boundary.
21. `internal/api/unified_agent.go` shared with `api-contracts`: unified agent download and installer handlers are both an agent lifecycle control surface and a canonical API payload contract boundary.
22. `scripts/install.ps1` shared with `deployment-installability`: the Windows installer is both a deployment installability entry point and a canonical agent lifecycle runtime continuity boundary.
21. `internal/api/setup_script_render.go` shared with `api-contracts`, `storage-recovery`: the generated Proxmox setup-script is a shared boundary across agent lifecycle (forced-command keys, install/uninstall edits), API contracts (rendered token shape and encoded rerun URL), and storage/recovery (backup visibility grants, Pulse-managed temperature SSH keys, and SMART disk-temperature collection).
22. `internal/api/unified_agent.go` shared with `api-contracts`: unified agent download and installer handlers are both an agent lifecycle control surface and a canonical API payload contract boundary.
23. `scripts/install.ps1` shared with `deployment-installability`: the Windows installer is both a deployment installability entry point and a canonical agent lifecycle runtime continuity boundary.
The Windows installer must support a non-mutating download preflight that
can run before Administrator-only install work, must accept token-file
enrollment input, and must persist plain-HTTP/insecure runtime continuity
@ -146,7 +137,7 @@ reverse.
`pulse-agent` runs so installer "healthy" verification and post-install
smoke checks prove a live agent runtime, not merely a running service
wrapper.
23. `scripts/install.sh` shared with `deployment-installability`: the shell installer is both a deployment installability entry point and a canonical agent lifecycle runtime continuity boundary.
24. `scripts/install.sh` shared with `deployment-installability`: the shell installer is both a deployment installability entry point and a canonical agent lifecycle runtime continuity boundary.
Agent lifecycle and fleet-operation surfaces may consume
`POST /api/actions/plan` for resource capability planning, but the action plan
@ -668,61 +659,6 @@ profile and assignment columns, but embedded table framing must route through
Approval-gated command execution must expose stable rejection reasons for
invalid approval grants so fleet operators can distinguish missing, expired,
mismatched, and signature-invalid grants through agent metrics.
9. Add or change profile management, the extracted agent profiles runtime owner, the agent profile settings catalog, the infrastructure source-manager landing, the pure unified-agent inventory/install model, the connections-ledger workspace shell, the unified ConnectionEditor and its per-type credential slots, route model, shared install section owner, the shared direct-node/discovery infrastructure settings owners plus their model, shared frontend install-command assembly, Proxmox setup/install API transport, TrueNAS platform-connection management, VMware platform-connection management, the shared monitored-system impact preview shell for those platform connections, setup-completion install handoff transport, deploy-fallback manual install transport, and fleet-control presentation through `frontend-modern/src/api/agentProfiles.ts`, `frontend-modern/src/api/nodes.ts`, `frontend-modern/src/components/Settings/AgentProfilesPanel.tsx`, `frontend-modern/src/components/Settings/agentProfileSettings.ts`, `frontend-modern/src/components/Settings/useAgentProfilesPanelState.ts`, `frontend-modern/src/components/Settings/connectionsTableModel.ts`, `frontend-modern/src/components/Settings/useConnectionsLedger.ts`, `frontend-modern/src/components/Settings/useConnectionRowActions.ts`, `frontend-modern/src/components/Settings/ConnectionEditor/ConnectionEditor.tsx`, `frontend-modern/src/components/Settings/ConnectionEditor/AddressProbeStep.tsx`, `frontend-modern/src/components/Settings/ConnectionEditor/useConnectionEditor.ts`, `frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx`, `frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx`, `frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx`, `frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx`, `frontend-modern/src/components/Settings/InfrastructureInstallerSection.tsx`, `frontend-modern/src/components/Settings/InfrastructureWorkspace.tsx`, `frontend-modern/src/components/Settings/InfrastructureSourceManager.tsx`, `frontend-modern/src/components/Settings/infrastructureWorkspaceModel.ts`, `frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx`, `frontend-modern/src/components/Settings/useTrueNASSettingsPanelState.ts`, `frontend-modern/src/components/Settings/useVMwareSettingsPanelState.ts`, `frontend-modern/src/components/Settings/proxmoxSettingsModel.ts`, `frontend-modern/src/components/Settings/ConfiguredNodeTables.tsx`, `frontend-modern/src/components/Settings/SettingsSectionNav.tsx`, `frontend-modern/src/components/Settings/infrastructureSettingsModel.ts`, `frontend-modern/src/components/Settings/useInfrastructureConfiguredNodesState.ts`, `frontend-modern/src/components/Settings/useInfrastructureDiscoveryRuntimeState.ts`, `frontend-modern/src/components/Settings/useInfrastructureInstallState.tsx`, `frontend-modern/src/components/Settings/useInfrastructureOperationsState.tsx`, `frontend-modern/src/components/Settings/useInfrastructureSettingsState.ts`, `frontend-modern/src/components/Settings/nodeModalModel.ts`, `frontend-modern/src/components/Settings/useNodeModalState.ts`, `frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx`, and `frontend-modern/src/utils/agentInstallCommand.ts`. Phase 9 retired the legacy reporting/inventory surface (InfrastructureOperationsController, InfrastructureInventorySection, InfrastructureActiveRowDetails, InfrastructureIgnoredRowDetails, InfrastructureStopMonitoringDialog, useInfrastructureReportingState) and the per-type shells (PlatformConnectionsWorkspace, ProxmoxSettingsPanel, ProxmoxDirectWorkspace, ProxmoxConfiguredNodesTable, ProxmoxDirectConnectionsCard, ProxmoxDiscoveryResultsCard, ProxmoxDeleteNodeDialog, ProxmoxNodeModalStack, NodeModal shell, TrueNASSettingsPanel, VMwareSettingsPanel, useProxmoxDirectWorkspaceState); lifecycle extensions must route through the unified aggregator ledger, source-manager cards, and ConnectionEditor credential slots rather than reintroducing those retired surfaces.
Those lifecycle-owned settings hooks may consume websocket state only through `frontend-modern/src/contexts/appRuntime.ts`; they must not import `frontend-modern/src/App.tsx` or recreate root-shell providers.
Discovery configuration is part of that same lifecycle-owned workspace boundary. `InfrastructureSourceManager.tsx` must open one canonical discovery editor through `InfrastructureDiscoverySettingsDialog.tsx`, `DiscoverySettingsForm.tsx`, and `discoverySettingsModel.ts`, while the System/Network shell stays limited to network-boundary controls instead of reintroducing a second editable discovery surface. That same workspace boundary now owns the infrastructure source-management toolbar too: the landing page exposes `Add infrastructure`, `Run discovery`, and `Discovery settings` as first-viewport toolbar actions inside the source manager, while governed source rows expose per-source add actions such as `Install Pulse Agent` from the shared catalog. `Run discovery` must remain a visible text command for manual scans rather than an icon-only affordance. `Detect address` remains inside the single add-flow source picker/probe path rather than a duplicate toolbar action. The same landing boundary may surface setup confidence from the unified rows and discovered candidates, including connected-system count, API coverage, agent coverage, sources that still need an agent, and discovery review state, without creating a second inventory model or provider-specific summary fetch. Source groups must stay in the governed source catalog order instead of re-sorting by current row count, row-level lifecycle entry points must use `Manage` language, and locked agent-install states must show a compact command inventory without raw token placeholders or disabled copy commands until a token exists. Network discovery settings remain safety-critical: automatic scanning must surface the shared-network/subnet warning before operators save scan mode changes. The AI settings manual workload refresh at `/api/discovery/run` is a discovery/API refresh sweep, not an enrollment or source-admission path; lifecycle surfaces may observe the refreshed candidates but must not reinterpret it as agent install authority. Agent command-execution handoff copy belongs to that same install surface: `InfrastructureInstallerSection.tsx` may expose the Pulse command-execution toggle for Patrol, but the label must describe Patrol remediation rather than reviving `Patrol auto-fix` or implying a paid monitoring-volume gate.
Setup-completion handoff belongs to that same single add-flow boundary. The first-run completion screen must keep credentials as the first surfaced object, then present one compact next-step surface that sends operators to Add infrastructure or directly to the Agent handoff; source-choice explanation may live inside that surface, but lifecycle work must not reintroduce a separate setup-wizard tour, duplicate CTA section, or inline install-command owner before the canonical infrastructure workspace.
The same lifecycle-owned workspace boundary now also owns attached-agent composition. When a unified Pulse Agent augments a first-class platform source such as Proxmox VE, the source manager and edit dialog must present one primary platform row with explicit `API` plus `Pulse Agent` composition rather than duplicating that same machine as a second peer row under a generic Pulse Agent platform bucket. Standalone hosts with no owning platform source remain grouped under a standalone-host owner bucket, with `Pulse Agent` shown as the collection method rather than as the pseudo-platform label.
When that primary Proxmox source is cluster-backed, the same workspace boundary must render the row under the canonical cluster moniker carried by the backend grouping contract rather than under one sibling node's hostname. That same backend grouping contract must also carry the explicit member-node list for the cluster so the source manager can render child node composition such as `delly` and `minipc` beneath the owning cluster row with per-node coverage/status. Cluster-member node agents belong as augmentations on that owning Proxmox row and its child nodes, not as separate standalone-host peers.
That same lifecycle-owned workspace boundary also owns agent version
posture on infrastructure settings. The landing table stays compact and
should only raise row-level attention when an attached or standalone agent
has an update available, while the exact installed version belongs in the
add/edit detail surfaces rather than as a permanent top-level table column.
That compact fleet-control presentation is now backed by the canonical
`/api/connections` `fleet` projection rather than page-local inference:
`connectionsTableModel.ts`, `useConnectionsLedger.ts`, and
`InfrastructureSourceManager.tsx` may format enrollment, liveness, version
drift, adapter health, config rollout, credential status, update posture,
and remote-control posture. Desired-versus-applied config drift, staged
rollout, credential health/rotation, and command-policy enforcement must
come from the explicit nested `fleet` objects on `/api/connections`; Settings
surfaces may rank or compact those signals, but they must not reconstruct
them from table copy, status badge labels, or provider-local error strings.
The connections ledger is a retained-value settings query: polling or manual
reloads must preserve the last fulfilled connected-systems rows while the
next `/api/connections` request is in flight, and must not route the
Infrastructure settings table through app-level Suspense or a blank loading
replacement.
The lifecycle-owned command-policy projection must preserve desired server
policy and applied agent report truth as separate facts. Desired disabled
with applied enabled, and desired enabled with applied disabled, are both
attention states for lifecycle surfaces, never in-sync shorthand. If the
agent has not reported current command capability or applied policy,
lifecycle surfaces must leave the applied side pending or unknown with an
explicit reason and must not treat `remoteControl` or `commandsEnabled` as
proof that the server desired state is live on the agent.
Standalone host rows must still be recognizable at a glance, but that
identity belongs in the existing row cells rather than new diagnostics
columns: the landing table reuses the `System` and `Endpoint` cells for a
short host fact such as OS/platform plus a reported endpoint, while the
richer agent identity facts stay in the edit/detail surface.
Exact Proxmox host-agent attachments remain backend-authored through
`/api/connections` even when Proxmox workload inventory is blocked by
credentials or reachability; lifecycle surfaces must render that attached
agent as part of the API source row and must not re-offer host install or
standalone lifecycle actions for the same physical host.
Public demo and other read-only settings posture must stay reporting-first
on that same lifecycle-owned workspace boundary: infrastructure workspace
routes may retain install and platform setup surfaces for manageable
sessions, but read-only presentation must collapse back to reporting/control
instead of advertising first-system setup on a pre-populated demo estate.
Agent-profile suggestion affordances remain adjacent assistant UX, not a
second AI settings reader. `frontend-modern/src/components/Settings/useAgentProfilesPanelState.ts`
may gate the Ideas action only from the shared assistant-availability fact
seeded by `/api/security/status.sessionCapabilities.assistantEnabled`, and
must not probe `/api/settings/ai` just to decide whether that affordance
should render.
9. Preserve canonical token-lifecycle reads in shared `internal/api/` auth/security helpers so lifecycle-adjacent setup and install flows do not revoke a displayed relay pairing token after `lastUsedAt` proves that an already paired device is actively depending on that credential.
10. Preserve backend-owned Pulse Mobile relay runtime credential minting in those same shared `internal/api/` auth/security helpers so lifecycle-adjacent setup and install flows reuse the canonical mobile token route instead of reintroducing wildcard or browser-authored runtime token bundles.
11. Preserve the dedicated backend-owned `relay:mobile:access` capability and its governed backward-compatible route inventory plus the shared helper call sites around it, so lifecycle-adjacent setup and install flows do not widen the mobile device credential back into general AI chat/execute scope ownership.
@ -2759,6 +2695,12 @@ Proxmox-managed `/root/.ssh/authorized_keys` symlinks when lifecycle setup or
removal touches Pulse-managed temperature-monitoring SSH keys: scripts must
resolve the real authorized-keys target before filtering `# pulse-` entries and
must use that resolved path for both install and uninstall edits.
That same generated PVE setup-script boundary also owns the forced-command
shape for temperature-monitoring SSH keys: new keys must execute the
Pulse-owned `/usr/local/sbin/pulse-sensors` wrapper, not raw `sensors -j`, so
lifecycle setup can collect CPU sensor data and SMART disk temperatures through
one bounded JSON payload while preserving old forced-key compatibility in the
runtime collector.
That same generated setup-script boundary must also use exact token-name
matching when it decides whether to rotate an existing Pulse-managed token, so
reruns do not treat partial-name collisions as the canonical managed token.

View file

@ -85,6 +85,7 @@ product API routes free of maintainer commercial analytics.
51. `internal/api/recovery_handlers.go`
51a. `internal/api/pbs_backups.go`
52. `internal/api/config_setup_handlers.go`
52a. `internal/api/setup_script_render.go`
53. `internal/api/demo_mode_commercial.go`
54. `internal/api/demo_mode_operations.go`
55. `internal/api/security_status_capabilities.go`
@ -96,7 +97,6 @@ product API routes free of maintainer commercial analytics.
60. `internal/api/connections_probe.go`
61. `frontend-modern/src/api/connections.ts`
62. `frontend-modern/src/utils/connectionErrorPresentation.ts`
62. `frontend-modern/src/api/hostedSignup.ts`
63. `internal/api/availability_handlers.go`
64. `frontend-modern/src/api/availabilityTargets.ts`
65. `frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx`
@ -413,14 +413,15 @@ platform page needs source-native backup columns.
before minting a `relay:mobile:access` credential. Community installs may
receive the standard license-required response, but direct API calls must
not bypass Relay entitlement by creating mobile runtime tokens.
49. `internal/api/slo.go` shared with `performance-and-scalability`: the SLO endpoint is both an API contract surface and a protected performance hot-path boundary.
50. `internal/api/system_settings.go` shared with `security-privacy`: the system settings telemetry and auth controls are both a security/privacy control surface and a canonical API payload contract boundary.
51. `internal/api/unified_agent.go` shared with `agent-lifecycle`: unified agent download and installer handlers are both an agent lifecycle control surface and a canonical API payload contract boundary.
49. `internal/api/setup_script_render.go` shared with `agent-lifecycle`, `storage-recovery`: the generated Proxmox setup-script is a shared boundary across agent lifecycle (forced-command keys, install/uninstall edits), API contracts (rendered token shape and encoded rerun URL), and storage/recovery (backup visibility grants, Pulse-managed temperature SSH keys, and SMART disk-temperature collection).
50. `internal/api/slo.go` shared with `performance-and-scalability`: the SLO endpoint is both an API contract surface and a protected performance hot-path boundary.
51. `internal/api/system_settings.go` shared with `security-privacy`: the system settings telemetry and auth controls are both a security/privacy control surface and a canonical API payload contract boundary.
52. `internal/api/unified_agent.go` shared with `agent-lifecycle`: unified agent download and installer handlers are both an agent lifecycle control surface and a canonical API payload contract boundary.
Development-mode missing-binary responses must report the build command
for the requested normalized OS/architecture, not a hard-coded Linux
target, so installer preflight failures point operators at the artifact
they actually need.
52. `internal/api/updates.go` shared with `deployment-installability`: update handlers are both a deployment-installability control surface and a canonical API payload contract boundary.
53. `internal/api/updates.go` shared with `deployment-installability`: update handlers are both a deployment-installability control surface and a canonical API payload contract boundary.
The platform-connections API contract also owns inactive monitored-system
candidate semantics end to end. `enabled=false` on TrueNAS or VMware preview,
test, add, and update payloads must serialize through the shared ledger client
@ -3808,6 +3809,13 @@ temperature-monitoring SSH keys: the rendered shell must resolve the symlink
target before filtering Pulse-managed `# pulse-` entries, use the resolved path
for both install and uninstall edits, and keep this behavior pinned in
`internal/api/contract_test.go`.
That same rendered PVE setup-script payload must also bind the temperature SSH
key to the Pulse-owned `/usr/local/sbin/pulse-sensors` wrapper rather than to
raw `sensors -j`. The wrapper is the setup-script API contract for legacy SSH
temperature collection: it must emit a bounded JSON object with `sensors` and
`smart` members, install or verify `smartmontools` for SATA/SAS/HDD disk
temperatures, and keep `sensors -j` only as a compatibility fallback inside
the wrapper/runtime collector path.
That same generated-script payload must also preserve the canonical encoded
rerun URL contract: embedded `SETUP_SCRIPT_URL` values must carry the exact
selected `host`, `pulse_url`, and `backup_perms` query state instead of

View file

@ -59,47 +59,36 @@ cloud-specific enforcement rules.
36. `frontend-modern/src/App.tsx`
37. `frontend-modern/src/AppLayout.tsx`
38. `frontend-modern/src/useAppRuntimeState.ts`
39. `frontend-modern/src/components/Commercial/MonitoredSystemDefinitionDisclosure.tsx`
40. `frontend-modern/src/components/Settings/BillingAdminPanel.tsx`
41. `frontend-modern/src/components/Settings/BillingAdminOrganizationsTable.tsx`
42. `frontend-modern/src/components/Settings/OrganizationBillingPanel.tsx`
43. `frontend-modern/src/components/Settings/OrganizationBillingLoadingState.tsx`
44. `frontend-modern/src/components/Settings/MonitoredSystemLedgerPanel.tsx`
45. `frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx`
46. `frontend-modern/src/components/Settings/ProLicensePanel.tsx`
47. `frontend-modern/src/components/Settings/ProLicensePlanSection.tsx`
48. `frontend-modern/src/components/Settings/CommercialBillingSections.tsx`
49. `frontend-modern/src/components/Settings/SelfHostedCommercialRecoverySection.tsx`
50. `frontend-modern/src/components/Settings/RelaySettingsPanel.tsx`
51. `frontend-modern/src/components/Settings/RelayPairingSection.tsx`
52. `frontend-modern/src/components/Settings/useBillingAdminPanelState.ts`
53. `frontend-modern/src/components/Settings/useOrganizationBillingPanelState.ts`
54. `frontend-modern/src/components/Settings/useProLicensePanelState.ts`
55. `frontend-modern/src/components/Settings/useRelaySettingsPanelState.ts`
56. `frontend-modern/src/pages/CloudPricing.tsx`
57. `frontend-modern/src/pages/HostedSignup.tsx`
58. `frontend-modern/src/pages/PricingHandoff.tsx`
59. `frontend-modern/src/utils/apiClient.ts`
60. `frontend-modern/src/utils/cloudPlans.ts`
61. `frontend-modern/src/utils/commercialBillingModel.ts`
62. `frontend-modern/src/utils/licensePresentation.ts`
63. `frontend-modern/src/utils/monitoredSystemPresentation.ts`
64. `frontend-modern/src/utils/pricingHandoff.ts`
65. `frontend-modern/src/utils/selfHostedPlans.ts`
66. `frontend-modern/src/utils/upgradePresentation.ts`
67. `frontend-modern/src/components/Settings/selfHostedBillingPresentation.ts`
68. `pulse-pro:license-server/public_pricing.go`
69. `pulse-pro:license-server/v6_checkout.go`
70. `pulse-pro:landing-page/thanks.html`
71. `pulse-pro:scripts/grandfathered_recurring_cutover_preview.py`
72. `pulse-pro:scripts/validate_public_pricing_model.py`
73. `pulse-pro:V6_LAUNCH_CHECKLIST.md`
74. `pkg/licensing/trial_activation_public_key_override_dev.go`
75. `pkg/licensing/trial_activation_public_key_override_release.go`
76. `pkg/licensing/testing_helpers.go`
77. `pkg/licensing/self_hosted_feature_catalog.go`
78. `frontend-modern/src/utils/selfHostedFeatureCatalog.generated.ts`
79. `pulse-pro:license-server/self_hosted_feature_catalog.generated.go`
39. `frontend-modern/src/components/Settings/BillingAdminPanel.tsx`
40. `frontend-modern/src/components/Settings/BillingAdminOrganizationsTable.tsx`
41. `frontend-modern/src/components/Settings/OrganizationBillingPanel.tsx`
42. `frontend-modern/src/components/Settings/OrganizationBillingLoadingState.tsx`
43. `frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx`
44. `frontend-modern/src/components/Settings/ProLicensePanel.tsx`
45. `frontend-modern/src/components/Settings/ProLicensePlanSection.tsx`
46. `frontend-modern/src/components/Settings/CommercialBillingSections.tsx`
47. `frontend-modern/src/components/Settings/SelfHostedCommercialRecoverySection.tsx`
48. `frontend-modern/src/components/Settings/RelaySettingsPanel.tsx`
49. `frontend-modern/src/components/Settings/RelayPairingSection.tsx`
50. `frontend-modern/src/components/Settings/useBillingAdminPanelState.ts`
51. `frontend-modern/src/components/Settings/useOrganizationBillingPanelState.ts`
52. `frontend-modern/src/components/Settings/useProLicensePanelState.ts`
53. `frontend-modern/src/components/Settings/useRelaySettingsPanelState.ts`
54. `frontend-modern/src/pages/PricingHandoff.tsx`
55. `frontend-modern/src/utils/apiClient.ts`
56. `frontend-modern/src/utils/cloudPlans.ts`
57. `frontend-modern/src/utils/commercialBillingModel.ts`
58. `frontend-modern/src/utils/licensePresentation.ts`
59. `frontend-modern/src/utils/monitoredSystemPresentation.ts`
60. `frontend-modern/src/utils/pricingHandoff.ts`
61. `frontend-modern/src/utils/selfHostedPlans.ts`
62. `frontend-modern/src/utils/upgradePresentation.ts`
63. `frontend-modern/src/components/Settings/selfHostedBillingPresentation.ts`
64. `pkg/licensing/trial_activation_public_key_override_dev.go`
65. `pkg/licensing/trial_activation_public_key_override_release.go`
66. `pkg/licensing/testing_helpers.go`
67. `pkg/licensing/self_hosted_feature_catalog.go`
68. `frontend-modern/src/utils/selfHostedFeatureCatalog.generated.ts`
80. `internal/cloudcp/server.go`, `internal/cloudcp/authz.go`, `internal/cloudcp/commercial_identity.go`, `internal/cloudcp/security.go`
81. `internal/cloudcp/health_monitor.go`, `internal/cloudcp/health_stuck_provisioning.go`, `internal/cloudcp/tenant_state_metrics.go`, `internal/cloudcp/ratelimit.go`
82. `internal/cloudcp/hosted_entitlement_handlers.go`, `internal/cloudcp/url_helpers.go`
@ -109,7 +98,6 @@ cloud-specific enforcement rules.
86. `internal/cloudcp/handoff/handler.go`, `internal/cloudcp/handoff/handoff.go`
87. `internal/cloudcp/stripe/grace_enforcer.go`, `internal/cloudcp/stripe/helpers.go`, `internal/cloudcp/stripe/reconciler.go`, `internal/cloudcp/stripe/webhook.go`
88. `internal/hosted/hosted_metrics.go`, `internal/hosted/reaper.go`
89. `pulse-pro:ops/pulse-cloud/audit/`
## Shared Boundaries
@ -342,7 +330,6 @@ or other self-hosted uncapped continuity plans.
show active historical `subscription_state=trial` entitlement state, but
they must not turn `trial_eligible`, `trial_eligibility_reason`, or an
expired trial marker into a default Pro CTA or banner.
17. Add or change monitored-system ledger, disclosure, or impact-preview presentation through `frontend-modern/src/components/Settings/MonitoredSystemLedgerPanel.tsx`, `frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx`, `frontend-modern/src/components/Commercial/MonitoredSystemDefinitionDisclosure.tsx`, and `frontend-modern/src/utils/monitoredSystemPresentation.ts`
18. Add or change paid relay settings and pairing presentation through `frontend-modern/src/components/Settings/RelaySettingsPanel.tsx`, `frontend-modern/src/components/Settings/RelayPairingSection.tsx`, and `frontend-modern/src/components/Settings/useRelaySettingsPanelState.ts`. The retired Dashboard shell must not be restored to carry a Relay onboarding card or equivalent blanket upsell — relay discovery stays inside its owning settings surface.
Public demo and other read-only presentation policy states must suppress
relay setup and upsell onboarding instead of inviting pairing or commercial
@ -354,22 +341,6 @@ or other self-hosted uncapped continuity plans.
Relay settings and public commercial docs must also keep Remote Access
positioned as a Relay-and-higher capability, not a Pro-only feature, so the
Relay tier remains a tangible standalone paid product.
19. Add or change cloud plan presentation through `frontend-modern/src/pages/CloudPricing.tsx`
That same presentation boundary also owns truthful customer-entry copy for
hosted Cloud pricing and signup. Cloud CTA labels, setup steps, and
returning-account wording in `frontend-modern/src/pages/HostedSignup.tsx`,
`frontend-modern/src/utils/cloudPlans.ts`, and adjacent public Cloud entry
surfaces must describe the real commercial flow as secure checkout ->
Pulse Account -> open workspace, not as an immediate workspace creation or
trial-only shortcut.
Public hosted Cloud trial signup must state the trial duration and
checkout economics before Stripe handoff: Stripe may collect a payment
method, but the subscription starts with the configured trial period and no
upfront charge, then Pulse Account opens the provisioned workspace after
checkout completes. The app-facing Cloud pricing and hosted-signup pages
must use the same shared copy contract so product, portal, and public entry
points do not drift on trial duration, payment-method collection, or upfront
charge expectations.
20. Add contract tests where runtime and pricing need to stay aligned
21. Add or change hosted browser org-context bootstrap through `frontend-modern/src/App.tsx`, `frontend-modern/src/AppLayout.tsx`, `frontend-modern/src/useAppRuntimeState.ts`, and `frontend-modern/src/utils/apiClient.ts`
That same hosted bootstrap boundary also owns the runtime-capability JSON

View file

@ -28,82 +28,82 @@ server-side update execution surfaces.
6. `internal/cloudcp/docker/manager.go`
7. `internal/cloudcp/docker/labels.go`
8. `internal/cloudcp/tenant_runtime_rollout.go`
8. `.github/workflows/create-release.yml`
9. `.github/workflows/deploy-demo-server.yml`
10. `.github/workflows/helm-pages.yml`
11. `.github/workflows/promote-floating-tags.yml`
12. `.github/workflows/publish-docker.yml`
13. `.github/workflows/publish-helm-chart.yml`
14. `.github/workflows/release-dry-run.yml`
15. `.github/workflows/update-demo-server.yml`
16. `.github/workflows/validate-release-assets.yml`
17. `.github/workflows/install-sh-smoke.yml`
16. `.github/ISSUE_TEMPLATE/v6_rc_feedback.yml`
17. `docs/RELEASE_NOTES.md`
18. `docs/releases/`
19. `docs/UPGRADE_v6.md`
20. `docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md`
21. `docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md`
22. `docs/release-control/v6/internal/RELEASE_PROMOTION_POLICY.md`
23. `package.json`
24. `package-lock.json`
25. `frontend-modern/package.json`
26. `frontend-modern/package-lock.json`
27. `frontend-modern/vite.config.ts`
28. `go.mod`
29. `go.sum`
30. `scripts/build-release.sh`
31. `scripts/check-workflow-dispatch-inputs.py`
32. `scripts/clean-mock-alerts.sh`
33. `scripts/com.pulse.hot-dev.plist.template`
34. `scripts/dev-check.sh`
35. `scripts/dev-deploy-agent.sh`
36. `scripts/dev-launchd-setup.sh`
37. `scripts/dev-launchd-wrapper.sh`
38. `scripts/hot-dev-bg.sh`
39. `scripts/hot-dev.sh`
40. `scripts/lib/hot-dev-runtime.sh`
41. `scripts/lib/hot-dev-auth.sh`
42. `scripts/install-container-agent.sh`
43. `install.sh`
44. `scripts/install.ps1`
45. `scripts/install.sh`
46. `scripts/install-mcp.sh`
47. `scripts/install-mcp.ps1`
48. `cmd/pulse-mcp/`
49. `scripts/pulse-auto-update.sh`
50. `scripts/release_control/internal/record_rc_to_ga_rehearsal.py`
51. `scripts/release_control/record_rc_to_ga_rehearsal.py`
52. `scripts/release_control/release_promotion_policy_support.py`
53. `scripts/release_control/resolve_release_promotion.py`
54. `scripts/release_ldflags.sh`
55. `scripts/run_cloud_public_signup_smoke.sh`
56. `scripts/run_demo_public_browser_smoke.sh`
57. `scripts/demo_public_browser_smoke.cjs`
58. `scripts/run_hosted_staging_smoke.sh`
59. `scripts/trigger-release-dry-run.sh`
60. `scripts/trigger-release.sh`
61. `scripts/toggle-mock.sh`
62. `deploy/helm/pulse/`
63. `tests/integration/playwright.config.ts`
64. `tests/integration/QUICK_START.md`
65. `tests/integration/README.md`
66. `tests/integration/scripts/bootstrap-hosted-mobile-onboarding.mjs`
67. `tests/integration/scripts/hosted-mobile-token-runtime.mjs`
68. `tests/integration/scripts/hosted-tenant-approval-store.mjs`
69. `tests/integration/scripts/hosted-tenant-runtime.mjs`
70. `tests/integration/scripts/hosted-tenant-runtime-restart.mjs`
71. `tests/integration/scripts/managed-dev-runtime.mjs`
72. `tests/integration/scripts/relay-mobile-token-helper.go`
73. `tests/integration/tests/helpers.ts`
74. `tests/integration/tests/runtime-defaults.ts`
75. `docker-compose.yml`
76. `scripts/install-docker.sh`
77. `scripts/validate-published-release.sh`
78. `scripts/validate-release.sh`
79. `scripts/release_asset_common.sh`
80. `scripts/backfill-release-assets.sh`
81. `.github/workflows/backfill-release-assets.yml`
9. `.github/workflows/create-release.yml`
10. `.github/workflows/deploy-demo-server.yml`
11. `.github/workflows/helm-pages.yml`
12. `.github/workflows/promote-floating-tags.yml`
13. `.github/workflows/publish-docker.yml`
14. `.github/workflows/publish-helm-chart.yml`
15. `.github/workflows/release-dry-run.yml`
16. `.github/workflows/update-demo-server.yml`
17. `.github/workflows/validate-release-assets.yml`
18. `.github/workflows/install-sh-smoke.yml`
19. `.github/ISSUE_TEMPLATE/v6_rc_feedback.yml`
20. `docs/RELEASE_NOTES.md`
21. `docs/releases/`
22. `docs/UPGRADE_v6.md`
23. `docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md`
24. `docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md`
25. `docs/release-control/v6/internal/RELEASE_PROMOTION_POLICY.md`
26. `package.json`
27. `package-lock.json`
28. `frontend-modern/package.json`
29. `frontend-modern/package-lock.json`
30. `frontend-modern/vite.config.ts`
31. `go.mod`
32. `go.sum`
33. `scripts/build-release.sh`
34. `scripts/check-workflow-dispatch-inputs.py`
35. `scripts/clean-mock-alerts.sh`
36. `scripts/com.pulse.hot-dev.plist.template`
37. `scripts/dev-check.sh`
38. `scripts/dev-deploy-agent.sh`
39. `scripts/dev-launchd-setup.sh`
40. `scripts/dev-launchd-wrapper.sh`
41. `scripts/hot-dev-bg.sh`
42. `scripts/hot-dev.sh`
43. `scripts/lib/hot-dev-runtime.sh`
44. `scripts/lib/hot-dev-auth.sh`
45. `scripts/install-container-agent.sh`
46. `install.sh`
47. `scripts/install.ps1`
48. `scripts/install.sh`
49. `scripts/install-mcp.sh`
50. `scripts/install-mcp.ps1`
51. `cmd/pulse-mcp/`
52. `scripts/pulse-auto-update.sh`
53. `scripts/release_control/internal/record_rc_to_ga_rehearsal.py`
54. `scripts/release_control/record_rc_to_ga_rehearsal.py`
55. `scripts/release_control/release_promotion_policy_support.py`
56. `scripts/release_control/resolve_release_promotion.py`
57. `scripts/release_ldflags.sh`
58. `scripts/run_cloud_public_signup_smoke.sh`
59. `scripts/run_demo_public_browser_smoke.sh`
60. `scripts/demo_public_browser_smoke.cjs`
61. `scripts/run_hosted_staging_smoke.sh`
62. `scripts/trigger-release-dry-run.sh`
63. `scripts/trigger-release.sh`
64. `scripts/toggle-mock.sh`
65. `deploy/helm/pulse/`
66. `tests/integration/playwright.config.ts`
67. `tests/integration/QUICK_START.md`
68. `tests/integration/README.md`
69. `tests/integration/scripts/bootstrap-hosted-mobile-onboarding.mjs`
70. `tests/integration/scripts/hosted-mobile-token-runtime.mjs`
71. `tests/integration/scripts/hosted-tenant-approval-store.mjs`
72. `tests/integration/scripts/hosted-tenant-runtime.mjs`
73. `tests/integration/scripts/hosted-tenant-runtime-restart.mjs`
74. `tests/integration/scripts/managed-dev-runtime.mjs`
75. `tests/integration/scripts/relay-mobile-token-helper.go`
76. `tests/integration/tests/helpers.ts`
77. `tests/integration/tests/runtime-defaults.ts`
78. `docker-compose.yml`
79. `scripts/install-docker.sh`
80. `scripts/validate-published-release.sh`
81. `scripts/validate-release.sh`
82. `scripts/release_asset_common.sh`
83. `scripts/backfill-release-assets.sh`
84. `.github/workflows/backfill-release-assets.yml`
## Shared Boundaries
@ -255,7 +255,7 @@ server-side update execution surfaces.
operator support packet must describe that same candidate instead of leaving
the branch on a newer version string while the in-repo packet still speaks
for an older RC.
Later corrective RCs such as `rc.3`, `rc.4`, and `rc.5` must also carry the live stable
Later corrective RCs such as `rc.3`, `rc.4`, `rc.5`, and `rc.6` must also carry the live stable
rollback target and any prerelease trust-root continuity caveat in the
current release notes, changelog, operator support pack, upgrade guide, and
release-control evidence record before the release workflow is dispatched.

View file

@ -11,7 +11,6 @@
"registry_file": "docs/release-control/v6/internal/subsystems/registry.json",
"dependency_subsystem_ids": [
"agent-lifecycle",
"cloud-paid",
"storage-recovery"
]
}
@ -85,83 +84,80 @@ work extends shared components instead of creating new local variants.
59. `frontend-modern/src/components/shared/FilterBar/index.ts`
59a. `frontend-modern/src/components/shared/FilterBar/SavedViewsMenu.tsx`
59b. `frontend-modern/src/components/shared/FilterBar/useSavedViews.ts`
56. `frontend-modern/src/components/shared/TypeColumn.guardrails.test.ts`
57. `frontend-modern/src/features/`
58. `frontend-modern/src/components/SetupWizard/SetupWizard.tsx`
59. `frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts`
60. `frontend-modern/src/components/SetupWizard/SetupCompletionPreview.tsx`
61. `frontend-modern/src/components/SetupWizard/steps/WelcomeStep.tsx`
62. `frontend-modern/src/components/SetupWizard/__tests__/SetupWizard.test.tsx`
63. `frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPreview.test.tsx`
64. `frontend-modern/src/components/SetupWizard/__tests__/WelcomeStep.test.tsx`
66. `frontend-modern/src/components/Settings/SystemLogsPanel.tsx`
67. `frontend-modern/src/components/Settings/useSystemLogsPanelState.ts`
68. `frontend-modern/src/utils/systemLogsPresentation.ts`
69. `frontend-modern/src/components/Settings/__tests__/SystemLogsPanel.test.tsx`
70. `frontend-modern/src/components/Settings/ResourcePicker.tsx`
71. `frontend-modern/src/components/Settings/reportingResourceTypes.ts`
72. `frontend-modern/src/utils/reportableResourceTypes.ts`
73. `frontend-modern/src/utils/reportingResourceTypes.ts`
74. `frontend-modern/src/utils/problemResourcePresentation.ts`
75. `frontend-modern/src/utils/workloadEmptyStatePresentation.ts`
76. `frontend-modern/src/utils/workloadGuestPresentation.ts`
78. `frontend-modern/src/utils/emptyStatePresentation.ts`
79. `frontend-modern/src/utils/semanticTonePresentation.ts`
80. `frontend-modern/src/components/Toast/Toast.tsx`
81. `frontend-modern/src/utils/toast.ts`
82. `frontend-modern/src/utils/semanticTonePresentation.ts`
83. `frontend-modern/src/utils/emptyStatePresentation.ts`
84. `frontend-modern/src/utils/typeColumnPresentation.ts`
85. `frontend-modern/src/components/Settings/NetworkBoundarySettingsSection.tsx`
86. `frontend-modern/src/components/Settings/networkSettingsModel.ts`
87. `frontend-modern/src/components/Settings/useDiscoverySettingsState.ts`
88. `frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts`
89. `frontend-modern/src/components/Settings/AvailabilitySettingsPanel.tsx`
90. `frontend-modern/src/components/Settings/availabilitySettingsModel.ts`
91. `frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx`
92. `frontend-modern/src/components/Settings/settingsPanelRegistryLoaders.ts`
93. `frontend-modern/src/components/Settings/settingsNavigationModel.ts`
94. `frontend-modern/src/components/Settings/settingsNavCatalog.ts`
95. `frontend-modern/src/components/Settings/settingsNavVisibility.ts`
96. `frontend-modern/src/components/Settings/settingsRouting.ts`
97. `frontend-modern/src/components/Settings/settingsTabSaveBehavior.ts`
98. `frontend-modern/src/components/Settings/settingsTypes.ts`
99. `frontend-modern/src/components/Settings/useSettingsNavigation.ts`
100. `frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx`
101. `frontend-modern/src/components/Settings/useSettingsSystemPanels.tsx`
102. `frontend-modern/src/components/Settings/DockerRuntimeSettingsCard.tsx`
103. `frontend-modern/src/components/shared/EnvironmentLockBadge.tsx`
104. `frontend-modern/src/utils/environmentLockPresentation.ts`
105. `frontend-modern/src/utils/docsLinks.ts`
106. `tests/integration/tests/20-local-doc-links.spec.ts`
107. `frontend-modern/src/index.css`
108. `frontend-modern/src/components/shared/summaryInteractionA11y.ts`
109. `frontend-modern/src/components/shared/SummaryRowActionButton.tsx`
110. `frontend-modern/src/hooks/createNonSuspendingQuery.ts`
111. `frontend-modern/src/components/shared/TableCardHeader.tsx`
112. `frontend-modern/src/components/shared/SummaryTableCardHeader.tsx`
60. `frontend-modern/src/components/shared/TypeColumn.guardrails.test.ts`
61. `frontend-modern/src/features/`
62. `frontend-modern/src/components/SetupWizard/SetupWizard.tsx`
63. `frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts`
64. `frontend-modern/src/components/SetupWizard/SetupCompletionPreview.tsx`
65. `frontend-modern/src/components/SetupWizard/steps/WelcomeStep.tsx`
66. `frontend-modern/src/components/SetupWizard/__tests__/SetupWizard.test.tsx`
67. `frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPreview.test.tsx`
68. `frontend-modern/src/components/SetupWizard/__tests__/WelcomeStep.test.tsx`
69. `frontend-modern/src/components/Settings/SystemLogsPanel.tsx`
70. `frontend-modern/src/components/Settings/useSystemLogsPanelState.ts`
71. `frontend-modern/src/utils/systemLogsPresentation.ts`
72. `frontend-modern/src/components/Settings/__tests__/SystemLogsPanel.test.tsx`
73. `frontend-modern/src/components/Settings/ResourcePicker.tsx`
74. `frontend-modern/src/components/Settings/reportingResourceTypes.ts`
75. `frontend-modern/src/utils/reportableResourceTypes.ts`
76. `frontend-modern/src/utils/reportingResourceTypes.ts`
77. `frontend-modern/src/utils/workloadEmptyStatePresentation.ts`
78. `frontend-modern/src/utils/workloadGuestPresentation.ts`
79. `frontend-modern/src/utils/emptyStatePresentation.ts`
80. `frontend-modern/src/utils/semanticTonePresentation.ts`
81. `frontend-modern/src/components/Toast/Toast.tsx`
82. `frontend-modern/src/utils/toast.ts`
83. `frontend-modern/src/utils/semanticTonePresentation.ts`
84. `frontend-modern/src/utils/emptyStatePresentation.ts`
85. `frontend-modern/src/utils/typeColumnPresentation.ts`
86. `frontend-modern/src/components/Settings/NetworkBoundarySettingsSection.tsx`
87. `frontend-modern/src/components/Settings/networkSettingsModel.ts`
88. `frontend-modern/src/components/Settings/useDiscoverySettingsState.ts`
89. `frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts`
90. `frontend-modern/src/components/Settings/AvailabilitySettingsPanel.tsx`
91. `frontend-modern/src/components/Settings/availabilitySettingsModel.ts`
92. `frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx`
93. `frontend-modern/src/components/Settings/settingsPanelRegistryLoaders.ts`
94. `frontend-modern/src/components/Settings/settingsNavigationModel.ts`
95. `frontend-modern/src/components/Settings/settingsNavCatalog.ts`
96. `frontend-modern/src/components/Settings/settingsNavVisibility.ts`
97. `frontend-modern/src/components/Settings/settingsRouting.ts`
98. `frontend-modern/src/components/Settings/settingsTabSaveBehavior.ts`
99. `frontend-modern/src/components/Settings/settingsTypes.ts`
100. `frontend-modern/src/components/Settings/useSettingsNavigation.ts`
101. `frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx`
102. `frontend-modern/src/components/Settings/useSettingsSystemPanels.tsx`
103. `frontend-modern/src/components/Settings/DockerRuntimeSettingsCard.tsx`
104. `frontend-modern/src/components/shared/EnvironmentLockBadge.tsx`
105. `frontend-modern/src/utils/environmentLockPresentation.ts`
106. `frontend-modern/src/utils/docsLinks.ts`
107. `tests/integration/tests/20-local-doc-links.spec.ts`
108. `frontend-modern/src/index.css`
109. `frontend-modern/src/components/shared/summaryInteractionA11y.ts`
110. `frontend-modern/src/components/shared/SummaryRowActionButton.tsx`
111. `frontend-modern/src/hooks/createNonSuspendingQuery.ts`
112. `frontend-modern/src/components/shared/TableCardHeader.tsx`
113. `frontend-modern/src/components/shared/UpgradeLink.tsx`
114. `frontend-modern/src/components/shared/useUpgradeNavigation.ts`
115. `frontend-modern/src/utils/upgradeNavigation.ts`
116. `frontend-modern/src/components/DemoBanner.tsx`
117. `frontend-modern/src/components/Login.tsx`
118. `frontend-modern/src/stores/demoMode.ts`
119. `frontend-modern/src/stores/sessionCapabilities.ts`
120. `frontend-modern/src/stores/sessionPresentationPolicy.ts`
121. `frontend-modern/src/stores/licenseCommercial.ts`
122. `frontend-modern/src/useAppRuntimeState.ts`
123. `frontend-modern/src/routing/routePreload.ts`
124. `frontend-modern/src/stores/aiChat.ts`
125. `frontend-modern/scripts/header-audit.mjs`
126. `frontend-modern/src/components/Settings/DataHandlingPanel.tsx`
127. `frontend-modern/src/components/Settings/dataHandlingPanelModel.ts`
128. `frontend-modern/scripts/canonical-platform-audit.mjs`
129. `frontend-modern/scripts/settings-diagnostics-boundary-audit.mjs`
130. `frontend-modern/src/utils/platformSupportManifest.generated.ts`
131. `frontend-modern/src/utils/platformSupportManifest.ts`
132. `frontend-modern/src/utils/sourcePlatformOptions.ts`
133. `frontend-modern/src/utils/sourcePlatforms.ts`
134. `frontend-modern/src/utils/infrastructureOnboardingPresentation.ts`
118. `frontend-modern/src/stores/sessionCapabilities.ts`
119. `frontend-modern/src/stores/sessionPresentationPolicy.ts`
120. `frontend-modern/src/stores/licenseCommercial.ts`
121. `frontend-modern/src/useAppRuntimeState.ts`
122. `frontend-modern/src/routing/routePreload.ts`
123. `frontend-modern/src/stores/aiChat.ts`
124. `frontend-modern/scripts/header-audit.mjs`
125. `frontend-modern/src/components/Settings/DataHandlingPanel.tsx`
126. `frontend-modern/src/components/Settings/dataHandlingPanelModel.ts`
127. `frontend-modern/scripts/canonical-platform-audit.mjs`
128. `frontend-modern/scripts/settings-diagnostics-boundary-audit.mjs`
129. `frontend-modern/src/utils/platformSupportManifest.generated.ts`
130. `frontend-modern/src/utils/platformSupportManifest.ts`
131. `frontend-modern/src/utils/sourcePlatformOptions.ts`
132. `frontend-modern/src/utils/sourcePlatforms.ts`
133. `frontend-modern/src/utils/infrastructureOnboardingPresentation.ts`
## Shared Boundaries
@ -436,121 +432,6 @@ not a replacement status card, CTA band, or page-local nested card.
apply controlled `value` props after options are mounted so settings panels
such as workload discovery show the persisted option instead of falling back
to the first option while the collapsed summary shows a different value.
2. Route new top-level settings surfaces through the canonical settings shell
instead of introducing page-local framing.
When a new operator-facing concern is closely related to an
existing tab's intent, prefer adding it as a sibling
`SettingsPanel` inside that tab's container component over
minting a new top-level tab. The agent-integrations surface
added in slice 59 follows this pattern:
`frontend-modern/src/components/Settings/AgentIntegrationsPanel.tsx`
ships under the existing API Access tab via
`APIAccessPanel.tsx`'s composition, not as its own tab. This
keeps the tab inventory bounded, avoids touching
`settingsNavigationModel.ts`, the registry, the loaders, and
the routing tests for additive sub-surfaces, and presents
"tokens + what those tokens unlock" as one operator-facing
story.
Shared shells and primitives that need websocket or dark-mode context must
consume `frontend-modern/src/contexts/appRuntime.ts`; they must not import
`frontend-modern/src/App.tsx`, because `App.tsx` owns provider placement
while frontend primitives own reusable consumption.
That same shared shell boundary now also owns thin public-route handoff
presentation in `frontend-modern/src/App.tsx`: compatibility routes such as
`/pricing` may stay outside authenticated chrome, but they must remain
minimal handoff shells that defer destination truth to the owning subsystem
instead of embedding a second copy of public marketing or checkout UI inside
the product runtime.
Cloud acquisition follows that same app-shell rule: ordinary self-hosted
frontend primitives must not register `/cloud` or `/cloud/signup` as public
product-runtime routes, because Cloud signup belongs to Pulse Account and
the Cloud control plane rather than a local in-product trial page.
The former top-level `/infrastructure`, `/workloads`, `/storage`, and
`/recovery` operator routes never shipped as stable v6 surfaces and must
remain unregistered instead of being kept as compatibility redirects.
Workload, storage, and recovery workflows live inside the owning
platform/runtime pages and reusable component surfaces; they must not appear
in navigation, command palette, shortcut, route-preload, or active-tab
owners as standalone aggregate destinations. The old top-level `/ceph`
alias is also unregistered; Proxmox owns Ceph at `/proxmox/ceph`.
The same settings-shell boundary owns read-only landing posture: when the
session presentation policy says the operator cannot manage setup, `/settings`
and sidebar navigation must land on the canonical reporting/control surface
instead of setup-oriented install routes.
Route normalization must wait until that presentation policy has resolved
before stripping infrastructure onboarding queries such as `?add=pick`,
`?add=agent`, or `?add=detect`, so first-session and explicit add-flow
handoffs do not lose their modal target during session bootstrap.
When those infrastructure modal targets are preserved, the settings shell
must keep the owning add-flow copy intact: the detect target is an API
platform probe, not a generic infrastructure-source detector, and its outer
dialog frame should match the management-API endpoint language owned by the
agent-lifecycle onboarding contract.
That same settings-shell boundary also owns explicit organization-route
stability. Deep links such as `/settings/organization`,
`/settings/organization/access`, and adjacent organization shells must keep
their canonical header and page frame when the route itself is allowed,
even while runtime capabilities or presentation policy are still settling.
Shared shell filtering may hide the sidebar item until the governing state
resolves, but `settingsHeaderMeta.ts`, `useSettingsAccess.ts`, and the
canonical settings-shell tests must not bounce an allowed organization
route back to `Infrastructure` just because nav filtering has not yet
surfaced that tab.
That same settings-shell boundary also owns authenticated operator identity
propagation into organization panels.
`frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx`
must derive the effective username from the resolved security status
(`proxyAuthUsername`, then `ssoSessionUsername`, then `authUsername`) and
pass that identity into the organization overview, access, and sharing
panels so deep-linked organization routes do not collapse into anonymous
read-only shells under proxy-auth, SSO, or local-auth sessions.
That same shared session-presentation boundary also owns alerts read-only
posture: `/alerts` may continue exposing reporting tabs such as overview and
history, but activation controls plus configuration routes must collapse out
of the public-demo shell instead of advertising blocked management actions.
That same public-demo presentation boundary also owns Settings support
posture: the authenticated demo shell must not advertise `Diagnostics &
Health`, `Data & Reports`, or `System Logs` in the Settings navigation, and
the retired top-level `/operations/*` browser path must stay unregistered
instead of reviving a standalone Operations utility tab or route-local
support shell.
Because `Data & Reports` is the `advanced_reporting` capability surface
rather than a general diagnostics page, `settingsNavCatalog.ts` must hide it
when that feature is unavailable; ordinary self-hosted users should see
support diagnostics and logs without being shown a Pro-locked reporting tab,
while paid instances keep the canonical `/settings/support/reporting` route
and panel.
Resource Privacy/Data Handling is a route-backed trust surface, not a
commercial surface or default settings destination. The Settings shell must
keep it governed by the Security registry/header/navigation model without
advertising it in the normal sidebar while it remains informational only,
and it must avoid trial, upgrade, paid-plan, or monitoring-limit copy when
commercial presentation is hidden.
General settings runtime cards that present source-platform actions must
consume `frontend-modern/src/utils/systemSettingsPresentation.ts` and the
shared source-platform vocabulary rather than card-local product names.
`frontend-modern/src/components/Settings/DockerRuntimeSettingsCard.tsx`
owns the local toggle layout, but Docker and Podman update-action titles,
descriptions, environment variable display, and failure copy belong to
`systemSettingsPresentation.ts` so settings surfaces do not drift back to
generic container wording.
Shared sparkline primitives must also stay CSP-safe by construction:
`frontend-modern/src/components/shared/InteractiveSparkline.tsx` may use SVG
attributes and shared state/model helpers for cursor, axis-label, and
tooltip positioning, but it must not write inline `style=` attributes for
tick labels, tooltip placement, or per-series transitions on the public
shell. Axis labels must render in fixed-size SVG shells or another
non-scaled primitive boundary; the shared sparkline must not put axis glyphs
inside `preserveAspectRatio="none"` label viewBoxes that stretch text as the
chart resizes.
The same shared presentation boundary also owns reusable scroll containers:
`frontend-modern/src/components/shared/Table.tsx` must keep touch-scroll
behavior on classes and shared CSS in `frontend-modern/src/index.css`
instead of reintroducing inline `style=` attributes for overflow or mobile
scroll behavior. `frontend-modern/src/components/shared/PulseDataGrid.tsx`
inherits that same boundary: shared data-grid shells must route scrollbar
hiding and table-width sizing through shared classes plus HTML attributes,
not inline overflow or min-width styles.
3. Add feature-specific presentation only when no shared primitive should own it.
Feature surfaces under `frontend-modern/src/features/` that display
product labels must consume the owning subsystem's presentation utilities
@ -880,36 +761,9 @@ not a replacement status card, CTA band, or page-local nested card.
system badge names a platform with its version, source/method context may
still add collection labels such as Pulse Agent, but it must not repeat the
same platform again as an unversioned source badge.
9. Keep top-of-page summary interaction on shared primitives. Infrastructure, workloads, and storage summary cards must route sticky-shell behavior through `frontend-modern/src/components/shared/StickySummarySection.tsx` and route row-hover or focused-series rendering through shared chart primitives such as `frontend-modern/src/components/shared/InteractiveSparkline.tsx` and `frontend-modern/src/components/shared/DensityMap.tsx`, rather than page-local sticky wrappers or metric-card-specific hover logic. When a page keeps summary charts visible below the desktop breakpoint, it must use the shared `stickyDesktopOnly` mode instead of adding page-local media queries, so wrapped two-column summaries scroll as normal content and only become sticky once the large-screen layout is active. The shared summary-card contract must also own stable summary-card geometry for chart-backed cards so row hover, focus, synchronized readouts, or idle header metadata cannot ratchet the sticky summary taller across rerenders. Shared chart slot geometry belongs in `frontend-modern/src/components/shared/summaryChartLayout.ts` so `SummaryMetricCard` and governed non-summary chart sections can consume the same normal and compact chart heights instead of re-declaring page-local `h-*` sizing.
Storage's top-of-page summary scope is limited to the stable chart grid and
shared row/group focus affordances. A rolling-history capacity planner must
not be bolted onto the sticky summary shell as an extra frontend primitive;
if capacity planning returns, it needs an owner-level model that can keep
the summary geometry and visible card set stable across refreshes.
10. Keep summary chart interaction identity on one shared helper. Summary surfaces that expose row-hover, group-hover, chart-hover, or route-focus-driven chart emphasis must derive page/group/entity scope through `frontend-modern/src/components/shared/summaryCardInteraction.ts` and pass that same resolved scope into card-state, sparkline, and density-map primitives, rather than letting cards read `hovered || focused` while charts listen to a different page-local ID source. Hovering one summary chart must promote that series into the shared active entity so sibling cards highlight the same object instead of keeping chart-local hover islands, and hovering or pinning a workload group header, infrastructure cluster header, or storage pool-group header must scope the matching summary cards through that same shared contract instead of forking a page-local summary filter path. Sibling cards should surface that synchronized hover as one compact header readout through the shared summary-card contract, while the chart under the pointer keeps the only floating tooltip. Recovery is explicitly outside this interaction dialect: its retired posture-card strip must not return with row/group/chart hover behavior without a separate governed product decision.
11. Keep page summaries page-scoped when table rows enter contextual focus. Route-backed row selection may add a focused label and shared series emphasis, but infrastructure, workloads, and storage summary cards must continue to render the page-level series set instead of collapsing the summary down to the selected row or replacing the global trend view with row-local empty states.
12. Keep contextual row focus on the shared summary primitive. Summary surfaces and same-route table drill-ins must reuse `frontend-modern/src/components/shared/contextualFocus.ts` for interactive-series filtering, focused-name lookup, active-series derivation, local scroll preservation, and deliberate inline-detail reveal instead of rebuilding page-local `Set` filters, focused-label scans, drawer-aware scroll math, or ad hoc scroll restoration in each surface.
13. Keep summary-to-table coordination deliberate, explicit, and reversible. Shared summary hover may highlight the matching table row when it is already visible, but transient chart hover must not auto-filter tables, auto-scroll the page, or reshuffle table ordering. Pinned page/group/entity scope on workloads, infrastructure, or storage must stay row-first: the pinned row or group header is the visible scoped state, not a second strip or search-row widget. Page shells therefore must not reintroduce always-on scope banners, preview bars, page-local chips, breadcrumbs, or search/filter-row scope accessories just to explain pinned state. When the active row is off-screen, page owners must still route through `frontend-modern/src/components/shared/summaryTableFocus.ts` and surface a lightweight `Jump to row` affordance that reveals and scrolls only on explicit user action. That same shared table-focus owner now also owns reversible clearing: pinned scope may clear only from governed neutral interaction-surface space or the shared `Escape` path, with page owners binding a broader clear-surface root separately from the row-lookup table root when needed and supplying one page-level reset callback for filters plus summary-linked selections. Row cells, group headers, inline detail, summary cards, and explicit controls must not accidentally clear pinned scope, while governed table/card clear surfaces must still allow real user clicks on neutral whitespace to clear it. Deliberate row focus may reveal inline detail automatically, but that reveal must be drawer-aware: infrastructure and workload row toggles that already have the row in view must hand the current `.app-scroll-shell` position through `frontend-modern/src/utils/appShellScrollRestoration.ts` so the remounted shell in `frontend-modern/src/App.tsx` can reopen the inline detail without looking like a page refresh, and then still route through the shared reveal helper whenever the opened drawer would otherwise land below the fold. Same-route drawers must therefore scroll only enough to keep the row header plus the top of the inline detail visible, never hard-center the row just because the route state changed. The same-route state scheduler owns the lifecycle for its deferred scroll-restore timers and animation frames; page owners must clean it up on unmount instead of leaving replay work attached to a torn-down route.
Shared summary-linked rows and group headers must also route their preview
semantics through
`frontend-modern/src/components/shared/summaryInteractionA11y.ts`.
Leaf rows and any explicit row-level control chrome must route deliberate
pin/open ownership through
`frontend-modern/src/components/shared/SummaryRowActionButton.tsx`, so
`aria-expanded`, `aria-controls`, `aria-pressed`, focus treatment, and
`Escape` preview clearing stay on the shared control instead of focusable-
table-row shims. Group headers are different: they may use the header row
itself as the deliberate pin target when that keeps the table chrome
native, but they must not grow separate scope/pinned pill buttons or
off-screen fallback strips. Workloads, infrastructure,
and storage must not rebuild row-as-button keyboard handling or trailing
one-off expand columns once the shared action primitive exists.
When pinned page, group, or entity scope needs a local explicit reset,
the only shared table-chrome owner is
`frontend-modern/src/components/shared/SummaryTableCardHeader.tsx`: the
reset action stays as one compact header-level `Clear` control with an
accessible `Clear selection` label, not a second page-level scope strip,
search-row accessory, or filter-bar badge.
13. Keep summary-linked table row emphasis on the shared primitive contract. Workloads, infrastructure, and storage rows that mirror the active summary entity must expose that state through `data-summary-row-active` and let the shared presentation in `frontend-modern/src/index.css` render the row emphasis, rather than carrying page-local sky or blue fill classes inside each row renderer. Group-scoped preview and pin must use that same shared presentation boundary: child rows that belong to a hovered or pinned summary group should expose `data-summary-group-member-active="preview|pinned"` so the block-level emphasis stays subtle, consistent, and reversible instead of each table inventing its own outline, badge, or full-strength fill treatment. Static grouped row headers on workloads, infrastructure, storage, recovery, and future grouped tables must use `frontend-modern/src/components/shared/groupedTableRowPresentation.ts` plus the `.grouped-table-row` CSS contract in `frontend-modern/src/index.css`, rather than rebuilding local `bg-surface-alt` variants with subtly different light/dark behavior or page-local left-accent markers. That shared grouped-table primitive owns the subgroup cell padding, typography, small metadata, and badge treatment as well as the row background token, so a future adjustment to the subgroup visual language changes every grouped product table from one owner. Storage-backed reusable row presenters under `frontend-modern/src/features/storageBackups/` must also keep row height and alert accents on class/data-attribute presentation instead of runtime inline style maps, so the shared table contract stays CSP-safe on both steady-state and alert-highlighted routes.
14. Keep retained-value data loading honest at the ownership boundary. Helpers
that prevent a feature surface from falling through the app-level Suspense
@ -957,12 +811,6 @@ not a replacement status card, CTA band, or page-local nested card.
must stay on the app-shell assistant-availability fact instead of
re-reading raw AI settings just to decide whether assistant affordances
should render.
17. Keep optional shared selectors honest about data ownership. Reusable
shells such as `frontend-modern/src/components/shared/InfrastructureSelector.tsx`
must gate their runtime data hooks on actual surface visibility, passing
explicit disabled/null inputs to shared data owners when the selector is
hidden instead of hydrating background summary data for chrome the page is
not rendering.
18. Keep Patrol shell composition and product-first provider vocabulary on the
shared feature-presentation boundary.
`frontend-modern/src/features/patrol/PatrolIntelligenceSummary.tsx`,

View file

@ -66,6 +66,7 @@ truth for live infrastructure data.
42. `internal/mock/demo_scenarios.go`
43. `internal/kubernetesagent/agent.go`
44. `pkg/agents/kubernetes/report.go`
45. `internal/monitoring/temperature.go`
## Shared Boundaries
@ -854,6 +855,12 @@ temperatures. `internal/monitoring/monitor_polling_node_helpers.go` may skip
SSH only once the host-agent temperature payload already has SMART disk data,
so nodes keep their disk-temperature and SMART augmentation when the host agent
is present but lacks SMART support.
Legacy SSH temperature collection must also use the Pulse sensor-wrapper
contract before falling back to raw lm-sensors output. `internal/monitoring/temperature.go`
must request `/usr/local/sbin/pulse-sensors` when it exists, parse the wrapper
payload as `{sensors, smart}`, preserve backward compatibility with old forced
`sensors -j` keys, and expose SMART disk temperatures through the same
`models.Temperature.SMART` path used by the physical-disk merge.
That same Proxmox monitoring boundary also owns checked response parsing for
polymorphic numeric fields. Shared client parsers such as
`pkg/proxmox/replication.go` must use the package's checked integer conversion

View file

@ -24,131 +24,120 @@ regression protection.
2. `pkg/metrics/store_query_plan_test.go`
3. `pkg/metrics/store_slo_test.go`
4. `pkg/metrics/store_additional_test.go`
4. `internal/api/slo.go`
5. `internal/api/slo_bench_test.go`
6. `frontend-modern/src/components/Workloads/WorkloadsSurface.tsx`
7. `frontend-modern/src/components/Workloads/WorkloadsStateCards.tsx`
5. `internal/api/slo.go`
6. `internal/api/slo_bench_test.go`
7. `frontend-modern/src/components/Workloads/WorkloadsSurface.tsx`
8. `frontend-modern/src/components/Workloads/workloadInventorySourceIssues.ts`
9. `frontend-modern/src/components/Workloads/__tests__/workloadInventorySourceIssues.test.ts`
10. `frontend-modern/src/components/Workloads/WorkloadsStatsStrip.tsx`
9. `frontend-modern/src/components/Workloads/WorkloadsTable.tsx`
10. `frontend-modern/src/components/Workloads/WorkloadPanel.tsx`
11. `frontend-modern/src/components/Workloads/WorkloadTableHeader.tsx`
12. `frontend-modern/src/components/Workloads/useWorkloadsState.ts`
13. `frontend-modern/src/hooks/useWorkloads.ts`
13. `frontend-modern/src/components/Workloads/useWorkloadsControlsState.ts`
14. `frontend-modern/src/components/Workloads/useWorkloadsDerivedState.ts`
15. `frontend-modern/src/components/Workloads/useWorkloadGuestMetadataState.ts`
16. `frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts`
17. `frontend-modern/src/components/Workloads/useWorkloadRouteState.ts`
18. `frontend-modern/src/components/Workloads/useWorkloadUrlSync.ts`
19. `frontend-modern/src/components/Workloads/WorkloadsFilter.tsx`
20. `frontend-modern/src/components/Workloads/workloadsFilterModel.ts`
22. `frontend-modern/src/components/Workloads/ThresholdSlider.tsx`
23. `frontend-modern/src/components/Workloads/thresholdSliderModel.ts`
24. `frontend-modern/src/components/Workloads/useThresholdSliderState.ts`
25. `frontend-modern/src/components/Workloads/StackedDiskBar.tsx`
26. `frontend-modern/src/components/Workloads/stackedDiskBarModel.ts`
27. `frontend-modern/src/components/Workloads/useStackedDiskBarState.ts`
28. `frontend-modern/src/components/Workloads/StackedMemoryBar.tsx`
29. `frontend-modern/src/components/Workloads/stackedMemoryBarModel.ts`
30. `frontend-modern/src/components/Workloads/useStackedMemoryBarState.ts`
31. `frontend-modern/src/components/Workloads/MetricBar.tsx`
32. `frontend-modern/src/components/Workloads/metricBarModel.ts`
32. `frontend-modern/src/components/Workloads/MetricMiniSparkline.tsx`
33. `frontend-modern/src/components/Workloads/useMetricBarState.ts`
34. `frontend-modern/src/components/Workloads/EnhancedCPUBar.tsx`
35. `frontend-modern/src/components/Workloads/enhancedCpuBarModel.ts`
36. `frontend-modern/src/components/Workloads/useEnhancedCPUBarState.ts`
37. `frontend-modern/src/components/Workloads/DiskList.tsx`
38. `frontend-modern/src/components/Workloads/diskListModel.ts`
39. `frontend-modern/src/components/Workloads/useDiskListState.ts`
40. `frontend-modern/src/components/Workloads/GuestRow.tsx`
41. `frontend-modern/src/components/Workloads/GuestRowCells.tsx`
42. `frontend-modern/src/components/Workloads/guestRowModel.tsx`
43. `frontend-modern/src/components/Workloads/useGuestRowState.ts`
44. `frontend-modern/src/components/Workloads/GuestDrawer.tsx`
45. `frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx`
45. `frontend-modern/src/components/Workloads/GuestDrawerHistory.tsx`
46. `frontend-modern/src/components/Workloads/guestDrawerModel.ts`
47. `frontend-modern/src/components/Workloads/useGuestDrawerState.ts`
48. `frontend-modern/src/components/Workloads/useGroupedTableWindowing.ts`
49. `frontend-modern/src/components/Workloads/workloadSelectors.ts`
50. `frontend-modern/src/components/Workloads/workloadTopology.ts`
51. `frontend-modern/src/components/Workloads/workloadSelectionModel.ts`
52. `frontend-modern/src/components/Workloads/workloadRouteModel.ts`
53. `frontend-modern/src/components/Workloads/workloadFilterConfigModel.ts`
53. `frontend-modern/src/components/Workloads/workloadMetricHistoryModel.ts`
54. `frontend-modern/src/components/Workloads/workloadRouteStateModel.ts`
55. `frontend-modern/src/components/Workloads/workloadUrlSyncModel.ts`
56. `frontend-modern/src/components/Workloads/useWorkloadFilterOptions.ts`
57. `frontend-modern/src/components/Workloads/__tests__/workloadSelectionModel.test.ts`
58. `frontend-modern/src/components/Workloads/__tests__/workloadFilterConfigModel.test.ts`
59. `frontend-modern/src/components/Workloads/__tests__/workloadRouteModel.test.ts`
60. `frontend-modern/src/components/Workloads/__tests__/workloadRouteStateModel.test.ts`
61. `frontend-modern/src/components/Workloads/__tests__/workloadUrlSyncModel.test.ts`
62. `frontend-modern/src/components/Workloads/__tests__/workloadTopology.test.ts`
63. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx`
64. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
65. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
66. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts`
67. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx`
68. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx`
69. `frontend-modern/src/components/Workloads/WorkloadsSummary.tsx`
70. `frontend-modern/src/utils/throughputPresentation.ts`
71. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx`
72. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx`
73. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts`
74. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts`
75. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`
76. `frontend-modern/src/components/Workloads/__tests__/WorkloadsSurface.performance.contract.test.tsx`
77. `frontend-modern/src/components/Workloads/__tests__/WorkloadsFilter.test.tsx`
79. `frontend-modern/src/components/Workloads/__tests__/useWorkloadSelectionState.test.ts`
80. `frontend-modern/src/components/Workloads/MetricBar.test.tsx`
81. `frontend-modern/src/components/Workloads/__tests__/useMetricBarState.test.tsx`
82. `frontend-modern/src/components/Workloads/__tests__/EnhancedCPUBar.test.tsx`
83. `frontend-modern/src/components/Workloads/__tests__/useEnhancedCPUBarState.test.tsx`
84. `frontend-modern/src/components/Workloads/ThresholdSlider.test.tsx`
85. `frontend-modern/src/components/Workloads/__tests__/useThresholdSliderState.test.ts`
86. `frontend-modern/src/components/Workloads/__tests__/StackedDiskBar.test.tsx`
87. `frontend-modern/src/components/Workloads/__tests__/useStackedDiskBarState.test.tsx`
88. `frontend-modern/src/components/Workloads/StackedMemoryBar.test.tsx`
89. `frontend-modern/src/components/Workloads/__tests__/useStackedMemoryBarState.test.tsx`
90. `frontend-modern/src/components/Workloads/__tests__/DiskList.test.tsx`
91. `frontend-modern/src/components/Workloads/__tests__/GuestRow.test.tsx`
91. `frontend-modern/src/components/Workloads/__tests__/MetricMiniSparkline.test.tsx`
91. `frontend-modern/src/components/Workloads/__tests__/workloadMetricHistoryModel.test.ts`
92. `frontend-modern/src/components/Workloads/GuestDrawer.test.tsx`
93. `frontend-modern/src/components/Workloads/__tests__/useGroupedTableWindowing.test.ts`
94. `frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx`
95. `frontend-modern/src/components/Workloads/useWorkloadViewportSync.ts`
96. `frontend-modern/src/components/Workloads/__tests__/useWorkloadViewportSync.test.tsx`
97. `frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx`
98. `frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts`
99. `frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts`
10. `frontend-modern/src/components/Workloads/WorkloadsTable.tsx`
11. `frontend-modern/src/components/Workloads/WorkloadPanel.tsx`
12. `frontend-modern/src/components/Workloads/WorkloadTableHeader.tsx`
13. `frontend-modern/src/components/Workloads/useWorkloadsState.ts`
14. `frontend-modern/src/hooks/useWorkloads.ts`
15. `frontend-modern/src/components/Workloads/useWorkloadsControlsState.ts`
16. `frontend-modern/src/components/Workloads/useWorkloadsDerivedState.ts`
17. `frontend-modern/src/components/Workloads/useWorkloadGuestMetadataState.ts`
18. `frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts`
19. `frontend-modern/src/components/Workloads/useWorkloadRouteState.ts`
20. `frontend-modern/src/components/Workloads/useWorkloadUrlSync.ts`
21. `frontend-modern/src/components/Workloads/WorkloadsFilter.tsx`
22. `frontend-modern/src/components/Workloads/workloadsFilterModel.ts`
23. `frontend-modern/src/components/Workloads/ThresholdSlider.tsx`
24. `frontend-modern/src/components/Workloads/thresholdSliderModel.ts`
25. `frontend-modern/src/components/Workloads/useThresholdSliderState.ts`
26. `frontend-modern/src/components/Workloads/StackedDiskBar.tsx`
27. `frontend-modern/src/components/Workloads/stackedDiskBarModel.ts`
28. `frontend-modern/src/components/Workloads/useStackedDiskBarState.ts`
29. `frontend-modern/src/components/Workloads/StackedMemoryBar.tsx`
30. `frontend-modern/src/components/Workloads/stackedMemoryBarModel.ts`
31. `frontend-modern/src/components/Workloads/useStackedMemoryBarState.ts`
32. `frontend-modern/src/components/Workloads/MetricBar.tsx`
33. `frontend-modern/src/components/Workloads/metricBarModel.ts`
34. `frontend-modern/src/components/Workloads/MetricMiniSparkline.tsx`
35. `frontend-modern/src/components/Workloads/useMetricBarState.ts`
36. `frontend-modern/src/components/Workloads/EnhancedCPUBar.tsx`
37. `frontend-modern/src/components/Workloads/enhancedCpuBarModel.ts`
38. `frontend-modern/src/components/Workloads/useEnhancedCPUBarState.ts`
39. `frontend-modern/src/components/Workloads/DiskList.tsx`
40. `frontend-modern/src/components/Workloads/diskListModel.ts`
41. `frontend-modern/src/components/Workloads/useDiskListState.ts`
42. `frontend-modern/src/components/Workloads/GuestRow.tsx`
43. `frontend-modern/src/components/Workloads/GuestRowCells.tsx`
44. `frontend-modern/src/components/Workloads/guestRowModel.tsx`
45. `frontend-modern/src/components/Workloads/useGuestRowState.ts`
46. `frontend-modern/src/components/Workloads/GuestDrawer.tsx`
47. `frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx`
48. `frontend-modern/src/components/Workloads/GuestDrawerHistory.tsx`
49. `frontend-modern/src/components/Workloads/guestDrawerModel.ts`
50. `frontend-modern/src/components/Workloads/useGuestDrawerState.ts`
51. `frontend-modern/src/components/Workloads/useGroupedTableWindowing.ts`
52. `frontend-modern/src/components/Workloads/workloadSelectors.ts`
53. `frontend-modern/src/components/Workloads/workloadTopology.ts`
54. `frontend-modern/src/components/Workloads/workloadSelectionModel.ts`
55. `frontend-modern/src/components/Workloads/workloadRouteModel.ts`
56. `frontend-modern/src/components/Workloads/workloadFilterConfigModel.ts`
57. `frontend-modern/src/components/Workloads/workloadMetricHistoryModel.ts`
58. `frontend-modern/src/components/Workloads/workloadRouteStateModel.ts`
59. `frontend-modern/src/components/Workloads/workloadUrlSyncModel.ts`
60. `frontend-modern/src/components/Workloads/useWorkloadFilterOptions.ts`
61. `frontend-modern/src/components/Workloads/__tests__/workloadSelectionModel.test.ts`
62. `frontend-modern/src/components/Workloads/__tests__/workloadFilterConfigModel.test.ts`
63. `frontend-modern/src/components/Workloads/__tests__/workloadRouteModel.test.ts`
64. `frontend-modern/src/components/Workloads/__tests__/workloadRouteStateModel.test.ts`
65. `frontend-modern/src/components/Workloads/__tests__/workloadUrlSyncModel.test.ts`
66. `frontend-modern/src/components/Workloads/__tests__/workloadTopology.test.ts`
67. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx`
68. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
69. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
70. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts`
71. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx`
72. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx`
73. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx`
74. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx`
75. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts`
76. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts`
77. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`
78. `frontend-modern/src/components/Workloads/__tests__/WorkloadsSurface.performance.contract.test.tsx`
79. `frontend-modern/src/components/Workloads/__tests__/WorkloadsFilter.test.tsx`
80. `frontend-modern/src/components/Workloads/__tests__/useWorkloadSelectionState.test.ts`
81. `frontend-modern/src/components/Workloads/MetricBar.test.tsx`
82. `frontend-modern/src/components/Workloads/__tests__/useMetricBarState.test.tsx`
83. `frontend-modern/src/components/Workloads/__tests__/EnhancedCPUBar.test.tsx`
84. `frontend-modern/src/components/Workloads/__tests__/useEnhancedCPUBarState.test.tsx`
85. `frontend-modern/src/components/Workloads/ThresholdSlider.test.tsx`
86. `frontend-modern/src/components/Workloads/__tests__/useThresholdSliderState.test.ts`
87. `frontend-modern/src/components/Workloads/__tests__/StackedDiskBar.test.tsx`
88. `frontend-modern/src/components/Workloads/__tests__/useStackedDiskBarState.test.tsx`
89. `frontend-modern/src/components/Workloads/StackedMemoryBar.test.tsx`
90. `frontend-modern/src/components/Workloads/__tests__/useStackedMemoryBarState.test.tsx`
91. `frontend-modern/src/components/Workloads/__tests__/DiskList.test.tsx`
92. `frontend-modern/src/components/Workloads/__tests__/GuestRow.test.tsx`
93. `frontend-modern/src/components/Workloads/__tests__/MetricMiniSparkline.test.tsx`
94. `frontend-modern/src/components/Workloads/__tests__/workloadMetricHistoryModel.test.ts`
95. `frontend-modern/src/components/Workloads/GuestDrawer.test.tsx`
96. `frontend-modern/src/components/Workloads/__tests__/useGroupedTableWindowing.test.ts`
97. `frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx`
98. `frontend-modern/src/components/Workloads/useWorkloadViewportSync.ts`
99. `frontend-modern/src/components/Workloads/__tests__/useWorkloadViewportSync.test.tsx`
100. `frontend-modern/src/utils/workloadsSummaryCache.ts`
101. `frontend-modern/src/routing/routePreload.ts`
102. `frontend-modern/src/useAppRuntimeState.ts`
103. `frontend-modern/src/components/Storage/StorageSummary.tsx`
104. `frontend-modern/src/utils/storageSummaryCache.ts`
105. `frontend-modern/src/components/Workloads/WorkloadsSurface.tsx`
103. `frontend-modern/src/utils/storageSummaryCache.ts`
104. `frontend-modern/src/components/Workloads/WorkloadsSurface.tsx`
## Shared Boundaries
1. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts` shared with `unified-resources`: the infrastructure selector pipeline is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
2. `frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx` shared with `unified-resources`: the infrastructure summary surface is both a canonical unified-resource consumer and a fleet-scale summary chart hot-path boundary.
3. `frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts` shared with `unified-resources`: infrastructure summary chart matching, focused-summary view derivation, and metric-series shaping are both a canonical unified-resource consumer surface and a fleet-scale summary chart hot-path boundary.
4. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts` shared with `unified-resources`: resource detail mappers are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
5. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx` shared with `unified-resources`: the unified resource host table card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
6. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx` shared with `unified-resources`: the unified resource PBS section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
7. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx` shared with `unified-resources`: the unified resource PMG section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
8. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx` shared with `unified-resources`: the unified resource service infrastructure card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
9. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx` shared with `unified-resources`: the unified resource table is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
10. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts` shared with `unified-resources`: unified resource service row shaping and I/O emphasis are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
11. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts` shared with `unified-resources`: unified resource table state derivation, sort-cycle policy, service sorting, and responsive column layout are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
12. `frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts` shared with `unified-resources`: infrastructure summary chart polling, cache hydration, and summary-state orchestration are both a canonical unified-resource consumer surface and a fleet-scale summary chart hot-path boundary.
13. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts` shared with `unified-resources`: unified resource table state, grouping, and windowing are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
14. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts` shared with `unified-resources`: unified resource table viewport sync and selected-row reveal are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
2. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts` shared with `unified-resources`: resource detail mappers are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
3. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx` shared with `unified-resources`: the unified resource host table card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
4. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx` shared with `unified-resources`: the unified resource PBS section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
5. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx` shared with `unified-resources`: the unified resource PMG section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
6. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx` shared with `unified-resources`: the unified resource service infrastructure card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
7. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx` shared with `unified-resources`: the unified resource table is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
8. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts` shared with `unified-resources`: unified resource service row shaping and I/O emphasis are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
9. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts` shared with `unified-resources`: unified resource table state derivation, sort-cycle policy, service sorting, and responsive column layout are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
10. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts` shared with `unified-resources`: unified resource table state, grouping, and windowing are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
11. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts` shared with `unified-resources`: unified resource table viewport sync and selected-row reveal are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
15. `frontend-modern/src/routing/routePreload.ts` shared with `frontend-primitives`: the app-shell route preload registry is both a canonical frontend shell boundary and an authenticated hot-path performance boundary.
16. `frontend-modern/src/useAppRuntimeState.ts` shared with `cloud-paid`: the authenticated app runtime bootstrap is both a hosted commercial org-context boundary and a protected app-shell performance boundary.
17. `internal/api/slo.go` shared with `api-contracts`: the SLO endpoint is both an API contract surface and a protected performance hot-path boundary.
@ -377,7 +366,6 @@ regression protection.
`prefers-reduced-motion`; hot-path Workloads components must not re-create
independent number animation loops.
23. Extend grouped workload row windowing, reveal-index clamping, overscan math, and per-group visible-slice derivation through `frontend-modern/src/components/Workloads/useGroupedTableWindowing.ts`, and extend viewport event wiring through `frontend-modern/src/components/Workloads/useWorkloadViewportSync.ts` rather than rebuilding scroll handlers, mounted-row budgets, viewport listeners, or group-slice math inside `frontend-modern/src/components/Workloads/useWorkloadsDerivedState.ts`
24. Extend Workloads shell rendering through `frontend-modern/src/components/Workloads/WorkloadsStateCards.tsx`, `frontend-modern/src/components/Workloads/WorkloadsTable.tsx`, and `frontend-modern/src/components/Workloads/WorkloadsStatsStrip.tsx` rather than accreting loading cards, workload table markup, or stats-strip presentation back into `frontend-modern/src/components/Workloads/WorkloadsSurface.tsx`
25. Extend workload table shell ownership through `frontend-modern/src/components/Workloads/WorkloadTableHeader.tsx` and `frontend-modern/src/components/Workloads/WorkloadPanel.tsx` rather than rebuilding sortable header markup, grouped node rows, row expansion, or guest-drawer rendering inside `frontend-modern/src/components/Workloads/WorkloadsTable.tsx`
`WorkloadPanel` owns the mutually exclusive host/guest drawer handoff:
clicking a grouped host row while a guest drawer is open must clear the
@ -398,77 +386,6 @@ regression protection.
default filter-option labels must flow through the shared all-option
presentation helper via the workload filter config model instead of the
hot-path shell hard-coding local `All ...` strings.
26. Keep long-range workload chart capping time-proportional across `frontend-modern/src/components/Workloads/WorkloadsSummary.tsx`, `frontend-modern/src/api/charts.ts`, and `internal/api/router.go`: when the workload hot path caps mixed-cadence history for top cards, it must bucket by time window rather than raw point index so 7-day and 30-day workload cards stay visually even without relaxing the protected payload budget.
Workload summary chart caching and app-shell prewarming belong to
`frontend-modern/src/utils/workloadsSummaryCache.ts`, not to page-local
component state. `frontend-modern/src/components/Workloads/WorkloadsSummary.tsx`
may hydrate from that shared in-memory/local cache and refresh it, while
`frontend-modern/src/useAppRuntimeState.ts` may warm the same cache after
authenticated bootstrap without mounting the Workloads route or starting a
second polling loop.
27. Keep summary hover/focus and sticky-card behavior on shared hot paths: infrastructure, workloads, and storage summary shells must reuse one page/group/entity scope model plus `frontend-modern/src/components/shared/StickySummarySection.tsx` inside the app scroll shell instead of per-page scroll listeners or per-card hover derivations, so row scrubbing highlights all cards, workload group headers, infrastructure cluster headers, and storage pool-group headers scope the summary coherently, pinned group focus remains route-backed and reversible, and the hot path does not multiply render or scroll work. Pages that keep chart summaries visible below the desktop breakpoint must use the shared desktop-sticky mode on that primitive rather than page-local viewport listeners, so wrapped chart grids do not pin most of the scroll container on narrow screens. That hot path stays row-first rather than adding fallback chrome: the on-screen row or group header is the scoped state, and any explicit reset belongs to one compact shared table-header action plus the shared `Escape` path, not to page-level scope strips, search-row widgets, or filter-bar badges. Background whitespace clearing may exist as a convenience, but the hot path must not depend on brittle dead-space hit testing as the only reversible control. The same hot path must therefore keep one page-level reset owner for filters plus pinned summary selections, and it must keep chart-backed summary-card geometry explicit and stable so hover rerenders, synchronized readouts, or idle header metadata cannot feed layout loops that grow or shrink the top cards over time. Recoverys summary rail is not part of this interactive hot path; it may share summary-card framing, but it must remain non-interactive until a separately governed model says otherwise.
The input path for that hot summary contract must stay shared too:
`frontend-modern/src/components/shared/summaryInteractionA11y.ts` owns
fine-pointer preview and focus-preview continuity, while
`frontend-modern/src/components/shared/SummaryRowActionButton.tsx` owns
deliberate open/pin controls for summary-linked leaf rows and explicit row
chrome. Group headers may pin through the header row itself when that
keeps the hot path visually native, but they must not reintroduce local
scope/pinned pill buttons that compete with the summary shell. Workloads,
infrastructure, and storage do not rebuild mouse-only hover branches,
focusable-row toggles, or touch-hostile synthetic hover behavior inside
individual row renderers. The same hot path must also carry block-level
group feedback through one shared row-state contract: when a summary group
is previewed or pinned, member rows should take a restrained shared
preview/pinned wash via `data-summary-group-member-active` rather than
per-surface outlines, secondary buttons, or full-strength row fills.
Static grouped-row header emphasis on the same workload and infrastructure
tables must route through
`frontend-modern/src/components/shared/groupedTableRowPresentation.ts` and
the shared `.grouped-table-row` CSS contract in `frontend-modern/src/index.css`,
rather than local `bg-surface-alt` or hover-fill variants that drift between
pages.
Framed product-table cards on the same hot-path surfaces must consume the
frontend-primitives-owned `TableCard` wrapper and `TableCardHeader`
title/action band so Workloads, Infrastructure, Storage, and Recovery keep
one border/background/overflow frame and table-section header treatment
instead of introducing page-local table shells with different light/dark
contrast. The shared `Table` primitive owns the horizontal scroll container
for those hot tables; page-local nested `overflow-x-auto` wrappers are not
allowed because they add extra scroll and paint surfaces on the row-heavy
path.
Infrastructure table responsive behavior belongs to that same hot-path
owner. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
must derive column visibility from the measured table surface width, with
`frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
owning the column-priority breakpoints. It must not rely on global viewport
width alone, because app chrome can leave the table with tablet-sized space
while the window still reports a desktop breakpoint. Live resize proof must
show the host and service tables dropping lower-priority columns without
introducing horizontal overflow.
Dashboard workload table responsive behavior belongs to the same hot-path
owner. `frontend-modern/src/components/Workloads/useWorkloadsControlsState.ts`
must derive workload table layout stages from live viewport width, and
`frontend-modern/src/components/Workloads/guestRowModel.tsx` must own the
responsive workload column priority and width model. `WorkloadsTable`,
`WorkloadTableHeader`, and `WorkloadPanel` must consume one shared
layout-visible column set so headers, colgroups, and rows stay aligned during
live resize. Workload rows keep navigation inside owned identity cells
rather than trailing action columns: guest custom web-interface URLs render
on the workload name, and
grouped node web-interface navigation stays on the shared node header name,
so responsive colgroups and rows do not carry an otherwise empty link
column. View-mode-specific column sets in
`frontend-modern/src/components/Workloads/guestRowModel.tsx` must retain
core monitoring metrics when narrowing scope: app-container mode keeps CPU,
memory, disk, and the responsive Net I/O and Disk I/O columns available,
while the layout priority model decides when detail-heavy I/O columns are
rendered. Filtering to a workload type must not silently remove capacity or
I/O observability that remains available in the all-workloads table. Tablet
and compact workload stages must normalize active column widths against the
currently visible column IDs, show higher-priority workload information
before exposing detail-heavy Net I/O and Disk I/O columns, and avoid
horizontal overflow at mobile, tablet, compact, and full desktop widths.
28. Keep summary-card hover emphasis on one bounded rendering budget: when a summary row is active, shared sparkline and density-map primitives must promote the selected series and demote background series through the same active-series ID rather than layering a second page-local highlight pass, so zoom-range and hover scrubbing stay visually coherent without reintroducing multi-series overdraw on the hot summary cards. Density maps on that hot path must stay overview-first under focus: preserve the multi-entity heatmap rows, layer focused-entity detail inside the card, and avoid swapping transient hover into a separate single-series chart path.
29. Keep public self-hosted checkout handoff endpoints on the adjacent
commercial/router boundary, not the summary-chart hot path. When
@ -586,12 +503,6 @@ shell clickable behind another overlay.
canonical dock threshold, the assistant must switch to an overlay drawer so
table filters, grouped rows, and other hot-path controls keep their
existing layout budget instead of paying a second collapse cost.
33. Keep hidden workload selector shells off the hot path. When an owning
platform page keeps `frontend-modern/src/components/shared/InfrastructureSelector.tsx`
mounted only for layout parity, `frontend-modern/src/components/shared/useInfrastructureSelectorState.ts`
must not hydrate `all-resources` or recovery rollups behind a hidden node
summary; selector-owned data hooks must be explicitly visibility-gated so
workload surfaces only pay for workload-owned transports.
34. Keep the retired dashboard page-header path absent from the compact hot
path. New page headers must stay pure presentation on their owning route
and must not introduce a second data load, widen suspense ownership, or

View file

@ -81,22 +81,6 @@
"unified-resources"
]
},
{
"path": "frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
"rationale": "the infrastructure summary surface is both a canonical unified-resource consumer and a fleet-scale summary chart hot-path boundary",
"subsystems": [
"performance-and-scalability",
"unified-resources"
]
},
{
"path": "frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
"rationale": "infrastructure summary chart matching, focused-summary view derivation, and metric-series shaping are both a canonical unified-resource consumer surface and a fleet-scale summary chart hot-path boundary",
"subsystems": [
"performance-and-scalability",
"unified-resources"
]
},
{
"path": "frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts",
"rationale": "resource detail mappers are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary",
@ -161,14 +145,6 @@
"unified-resources"
]
},
{
"path": "frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts",
"rationale": "infrastructure summary chart polling, cache hydration, and summary-state orchestration are both a canonical unified-resource consumer surface and a fleet-scale summary chart hot-path boundary",
"subsystems": [
"performance-and-scalability",
"unified-resources"
]
},
{
"path": "frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts",
"rationale": "unified resource table state, grouping, and windowing are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary",
@ -585,6 +561,15 @@
"security-privacy"
]
},
{
"path": "internal/api/setup_script_render.go",
"rationale": "the generated Proxmox setup-script is a shared boundary across agent lifecycle (forced-command keys, install/uninstall edits), API contracts (rendered token shape and encoded rerun URL), and storage/recovery (backup visibility grants, Pulse-managed temperature SSH keys, and SMART disk-temperature collection)",
"subsystems": [
"agent-lifecycle",
"api-contracts",
"storage-recovery"
]
},
{
"path": "internal/api/slo.go",
"rationale": "the SLO endpoint is both an API contract surface and a protected performance hot-path boundary",
@ -687,14 +672,8 @@
"cmd/pulse-agent/main.go",
"frontend-modern/src/api/agentProfiles.ts",
"frontend-modern/src/api/nodes.ts",
"frontend-modern/src/components/Infrastructure/deploy/CandidatesStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/ConfirmStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/DeployingStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/PreflightStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/ResultsStep.tsx",
"frontend-modern/src/components/Settings/agentProfileSettings.ts",
"frontend-modern/src/components/Settings/AgentProfilesPanel.tsx",
"frontend-modern/src/components/Settings/ConfiguredNodeTables.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/AddressProbeStep.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/ConnectionEditor.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx",
@ -719,7 +698,6 @@
"frontend-modern/src/components/Settings/NodeModalSetupGuideSection.tsx",
"frontend-modern/src/components/Settings/NodeModalStatusFooter.tsx",
"frontend-modern/src/components/Settings/proxmoxSettingsModel.ts",
"frontend-modern/src/components/Settings/SettingsSectionNav.tsx",
"frontend-modern/src/components/Settings/useAgentProfilesPanelState.ts",
"frontend-modern/src/components/Settings/useConnectionRowActions.ts",
"frontend-modern/src/components/Settings/useConnectionsLedger.ts",
@ -736,18 +714,15 @@
"frontend-modern/src/utils/agentInstallCommand.ts",
"frontend-modern/src/utils/agentProfilesPresentation.ts",
"frontend-modern/src/utils/agentProfileSuggestionPresentation.ts",
"frontend-modern/src/utils/clusterEndpointPresentation.ts",
"frontend-modern/src/utils/configuredNodeCapabilityPresentation.ts",
"frontend-modern/src/utils/configuredNodeStatusPresentation.ts",
"frontend-modern/src/utils/infrastructureOnboardingPresentation.ts",
"frontend-modern/src/utils/infrastructureSettingsPresentation.ts",
"frontend-modern/src/utils/nodeModalPresentation.ts",
"frontend-modern/src/utils/proxmoxSettingsPresentation.ts",
"frontend-modern/src/utils/unifiedAgentInventoryPresentation.ts",
"frontend-modern/src/utils/unifiedAgentStatusPresentation.ts",
"internal/agenttls/config.go",
"internal/api/agent_install_command_shared.go",
"internal/api/config_setup_handlers.go",
"internal/api/setup_script_render.go",
"internal/api/unified_agent.go",
"internal/remoteconfig/client.go",
"scripts/install.ps1",
@ -779,6 +754,7 @@
"match_files": [
"internal/api/agent_install_command_shared.go",
"internal/api/config_setup_handlers.go",
"internal/api/setup_script_render.go",
"internal/api/unified_agent.go"
],
"allow_same_subsystem_tests": false,
@ -975,30 +951,6 @@
"frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.guardrails.test.ts"
]
},
{
"id": "deploy-fallback-install-surface",
"label": "deploy wizard surface proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/Infrastructure/deploy/CandidatesStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/ConfirmStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/DeployingStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/PreflightStep.tsx",
"frontend-modern/src/components/Infrastructure/deploy/ResultsStep.tsx"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Infrastructure/__tests__/DeployStepComponents.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/CandidatesStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/ConfirmStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/DeployingStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/PreflightStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/ResultsStep.test.tsx",
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts"
]
},
{
"id": "unified-agent-settings-surface",
"label": "unified agent settings lifecycle proof",
@ -1038,7 +990,6 @@
"label": "platform connections workspace lifecycle proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/Settings/ConfiguredNodeTables.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/AddressProbeStep.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/ConnectionEditor.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx",
@ -1054,16 +1005,13 @@
"frontend-modern/src/components/Settings/infrastructureWorkspaceModel.ts",
"frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx",
"frontend-modern/src/components/Settings/proxmoxSettingsModel.ts",
"frontend-modern/src/components/Settings/SettingsSectionNav.tsx",
"frontend-modern/src/components/Settings/useConnectionRowActions.ts",
"frontend-modern/src/components/Settings/useConnectionsLedger.ts",
"frontend-modern/src/components/Settings/useInfrastructureConfiguredNodesState.ts",
"frontend-modern/src/components/Settings/useInfrastructureDiscoveryRuntimeState.ts",
"frontend-modern/src/components/Settings/useInfrastructureSettingsState.ts",
"frontend-modern/src/components/Settings/useTrueNASSettingsPanelState.ts",
"frontend-modern/src/components/Settings/useVMwareSettingsPanelState.ts",
"frontend-modern/src/utils/clusterEndpointPresentation.ts",
"frontend-modern/src/utils/proxmoxSettingsPresentation.ts"
"frontend-modern/src/components/Settings/useVMwareSettingsPanelState.ts"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
@ -1073,9 +1021,7 @@
"frontend-modern/src/components/Settings/__tests__/useTrueNASSettingsPanelState.test.tsx",
"frontend-modern/src/components/Settings/__tests__/useVMwareSettingsPanelState.test.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/__tests__/ConnectionEditor.test.tsx",
"frontend-modern/src/utils/__tests__/clusterEndpointPresentation.test.ts",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
"frontend-modern/src/utils/__tests__/proxmoxSettingsPresentation.test.ts",
"tests/integration/tests/21-truenas-settings-platform-connections.spec.ts",
"tests/integration/tests/22-vmware-settings-platform-connections.spec.ts"
]
@ -1087,8 +1033,6 @@
"match_files": [
"frontend-modern/src/utils/agentCapabilityPresentation.ts",
"frontend-modern/src/utils/agentProfileSuggestionPresentation.ts",
"frontend-modern/src/utils/configuredNodeCapabilityPresentation.ts",
"frontend-modern/src/utils/configuredNodeStatusPresentation.ts",
"frontend-modern/src/utils/unifiedAgentInventoryPresentation.ts",
"frontend-modern/src/utils/unifiedAgentStatusPresentation.ts"
],
@ -1097,8 +1041,6 @@
"exact_files": [
"frontend-modern/src/utils/__tests__/agentCapabilityPresentation.test.ts",
"frontend-modern/src/utils/__tests__/agentProfileSuggestionPresentation.test.ts",
"frontend-modern/src/utils/__tests__/configuredNodeCapabilityPresentation.test.ts",
"frontend-modern/src/utils/__tests__/configuredNodeStatusPresentation.test.ts",
"frontend-modern/src/utils/__tests__/unifiedAgentInventoryPresentation.test.ts",
"frontend-modern/src/utils/__tests__/unifiedAgentStatusPresentation.test.ts"
]
@ -1116,7 +1058,8 @@
"frontend-modern/src/utils/__tests__/infrastructureSettingsPresentation.test.ts"
]
}
]
],
"match_files": null
}
},
{
@ -1269,7 +1212,8 @@
"scripts/release_control/ai_runtime_docs_policy_test.py"
]
}
]
],
"match_files": null
}
},
{
@ -1521,7 +1465,8 @@
"internal/alerts/update_alerts_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -1555,6 +1500,7 @@
"frontend-modern/src/utils/agentInstallCommand.ts",
"frontend-modern/src/utils/apiTokenPresentation.ts",
"frontend-modern/src/utils/infrastructureSettingsPresentation.ts",
"internal/api/setup_script_render.go",
"internal/websocket/hub.go",
"pkg/pulsecli/actions.go",
"pkg/pulsecli/api_client.go",
@ -1869,7 +1815,8 @@
"frontend-modern/src/utils/__tests__/infrastructureSettingsPresentation.test.ts"
]
}
]
],
"match_files": null
}
},
{
@ -1888,12 +1835,10 @@
"owned_files": [
"frontend-modern/src/App.tsx",
"frontend-modern/src/AppLayout.tsx",
"frontend-modern/src/components/Commercial/MonitoredSystemDefinitionDisclosure.tsx",
"frontend-modern/src/components/Settings/BillingAdminOrganizationsTable.tsx",
"frontend-modern/src/components/Settings/BillingAdminPanel.tsx",
"frontend-modern/src/components/Settings/CommercialBillingSections.tsx",
"frontend-modern/src/components/Settings/MonitoredSystemImpactPreview.tsx",
"frontend-modern/src/components/Settings/MonitoredSystemLedgerPanel.tsx",
"frontend-modern/src/components/Settings/OrganizationBillingLoadingState.tsx",
"frontend-modern/src/components/Settings/OrganizationBillingPanel.tsx",
"frontend-modern/src/components/Settings/ProLicensePanel.tsx",
@ -1905,8 +1850,6 @@
"frontend-modern/src/components/Settings/useOrganizationBillingPanelState.ts",
"frontend-modern/src/components/Settings/useProLicensePanelState.ts",
"frontend-modern/src/components/Settings/useRelaySettingsPanelState.ts",
"frontend-modern/src/pages/CloudPricing.tsx",
"frontend-modern/src/pages/HostedSignup.tsx",
"frontend-modern/src/pages/PricingHandoff.tsx",
"frontend-modern/src/pages/RuntimeHome.tsx",
"frontend-modern/src/useAppRuntimeState.ts",
@ -2415,8 +2358,6 @@
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/api/__tests__/hostedSignup.test.ts",
"frontend-modern/src/pages/__tests__/HostedSignup.test.tsx",
"internal/api/billing_state_handlers_test.go",
"internal/api/contract_test.go",
"internal/api/licensing_handlers_auto_migrate_test.go",
@ -2460,7 +2401,6 @@
"label": "pro license surface proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/Settings/MonitoredSystemLedgerPanel.tsx",
"frontend-modern/src/components/Settings/ProLicensePanel.tsx",
"frontend-modern/src/components/Settings/ProLicensePlanSection.tsx",
"frontend-modern/src/components/Settings/selfHostedBillingPresentation.ts",
@ -2470,7 +2410,6 @@
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Settings/__tests__/MonitoredSystemLedgerPanel.test.tsx",
"frontend-modern/src/components/Settings/__tests__/ProLicensePanel.test.tsx"
]
},
@ -2510,7 +2449,6 @@
"label": "commercial plan model proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/Commercial/MonitoredSystemDefinitionDisclosure.tsx",
"frontend-modern/src/components/Settings/CommercialBillingSections.tsx",
"frontend-modern/src/utils/cloudPlans.ts",
"frontend-modern/src/utils/commercialBillingModel.ts",
@ -2519,11 +2457,8 @@
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Commercial/__tests__/MonitoredSystemDefinitionDisclosure.test.tsx",
"frontend-modern/src/components/Settings/__tests__/OrganizationBillingPanel.test.tsx",
"frontend-modern/src/components/Settings/__tests__/ProLicensePanel.test.tsx",
"frontend-modern/src/pages/__tests__/CloudPricing.test.tsx",
"frontend-modern/src/pages/__tests__/HostedSignup.test.tsx",
"frontend-modern/src/utils/__tests__/selfHostedPlans.test.ts"
]
},
@ -2582,17 +2517,12 @@
"label": "pricing route and page proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/pages/CloudPricing.tsx",
"frontend-modern/src/pages/HostedSignup.tsx",
"frontend-modern/src/pages/PricingHandoff.tsx",
"frontend-modern/src/utils/pricingHandoff.ts"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/api/__tests__/hostedSignup.test.ts",
"frontend-modern/src/pages/__tests__/CloudPricing.test.tsx",
"frontend-modern/src/pages/__tests__/HostedSignup.test.tsx",
"frontend-modern/src/pages/__tests__/PricingHandoff.test.tsx",
"frontend-modern/src/utils/__tests__/pricingHandoff.test.ts"
]
@ -2645,7 +2575,8 @@
"tests/integration/tests/53-demo-mode-commercial-boundary.spec.ts"
]
}
]
],
"match_files": null
}
},
{
@ -2999,7 +2930,8 @@
"scripts/installtests/install_sh_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -3089,7 +3021,6 @@
"frontend-modern/src/utils/environmentLockPresentation.ts",
"frontend-modern/src/utils/platformSupportManifest.generated.ts",
"frontend-modern/src/utils/platformSupportManifest.ts",
"frontend-modern/src/utils/problemResourcePresentation.ts",
"frontend-modern/src/utils/reportableResourceTypes.ts",
"frontend-modern/src/utils/reportingPresentation.ts",
"frontend-modern/src/utils/reportingResourceTypes.ts",
@ -3112,7 +3043,6 @@
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/shared/PageControls.guardrails.test.ts",
"frontend-modern/src/components/shared/TypeColumn.guardrails.test.ts"
],
"require_explicit_path_policy_coverage": true,
@ -3137,7 +3067,6 @@
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/shared/FilterToolbar.tsx",
"frontend-modern/src/components/shared/PageControls.tsx",
"frontend-modern/src/components/shared/SettingsPanel.tsx",
"frontend-modern/src/components/shared/Subtabs.tsx",
"frontend-modern/src/components/shared/Toggle.tsx"
@ -3146,9 +3075,7 @@
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts",
"frontend-modern/src/components/shared/FilterToolbar.test.tsx",
"frontend-modern/src/components/shared/PageControls.guardrails.test.ts",
"frontend-modern/src/components/shared/PageControls.test.tsx"
"frontend-modern/src/components/shared/FilterToolbar.test.tsx"
]
},
{
@ -3322,7 +3249,6 @@
"match_files": [
"frontend-modern/src/components/Toast/Toast.tsx",
"frontend-modern/src/utils/emptyStatePresentation.ts",
"frontend-modern/src/utils/problemResourcePresentation.ts",
"frontend-modern/src/utils/semanticTonePresentation.ts",
"frontend-modern/src/utils/toast.ts",
"frontend-modern/src/utils/typeColumnPresentation.ts"
@ -3333,7 +3259,6 @@
"frontend-modern/src/components/shared/TypeColumn.guardrails.test.ts",
"frontend-modern/src/components/Toast/__tests__/Toast.test.tsx",
"frontend-modern/src/utils/__tests__/emptyStatePresentation.test.ts",
"frontend-modern/src/utils/__tests__/problemResourcePresentation.test.ts",
"frontend-modern/src/utils/__tests__/semanticTonePresentation.test.ts",
"frontend-modern/src/utils/__tests__/typeColumnPresentation.test.ts"
]
@ -3499,7 +3424,6 @@
"label": "search input proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/shared/CollapsibleSearchInput.tsx",
"frontend-modern/src/components/shared/SearchField.tsx",
"frontend-modern/src/components/shared/SearchInput.tsx",
"frontend-modern/src/components/shared/SearchInputEnhancements.tsx",
@ -3517,17 +3441,12 @@
"label": "data grid and summary proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/shared/InfrastructureSummaryTable.tsx",
"frontend-modern/src/components/shared/infrastructureSummaryTableModel.ts",
"frontend-modern/src/components/shared/InfrastructureSummaryTableRow.tsx",
"frontend-modern/src/components/shared/PulseDataGrid.tsx",
"frontend-modern/src/components/shared/Table.tsx",
"frontend-modern/src/components/shared/useInfrastructureSummaryTableState.ts"
"frontend-modern/src/components/shared/Table.tsx"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/shared/__tests__/InfrastructureSummaryTable.test.tsx",
"frontend-modern/src/components/shared/__tests__/PulseDataGrid.test.tsx"
]
},
@ -3543,12 +3462,9 @@
"exact_files": [
"frontend-modern/src/components/shared/__tests__/CommandPaletteModal.test.tsx",
"frontend-modern/src/components/shared/__tests__/ContainerUpdateBadge.test.tsx",
"frontend-modern/src/components/shared/__tests__/DensityMap.test.tsx",
"frontend-modern/src/components/shared/__tests__/Dialog.test.tsx",
"frontend-modern/src/components/shared/__tests__/HelpIcon.test.tsx",
"frontend-modern/src/components/shared/__tests__/HistoryChart.test.tsx",
"frontend-modern/src/components/shared/__tests__/InfrastructureSelector.test.tsx",
"frontend-modern/src/components/shared/__tests__/InfrastructureSummaryTable.test.tsx",
"frontend-modern/src/components/shared/__tests__/MobileNavBar.test.tsx",
"frontend-modern/src/components/shared/__tests__/PulseDataGrid.test.tsx",
"frontend-modern/src/components/shared/__tests__/SearchField.test.tsx",
@ -3558,13 +3474,12 @@
"frontend-modern/src/components/shared/__tests__/WebInterfaceUrlField.test.tsx",
"frontend-modern/src/components/shared/ColumnPicker.test.tsx",
"frontend-modern/src/components/shared/FilterToolbar.test.tsx",
"frontend-modern/src/components/shared/PageControls.guardrails.test.ts",
"frontend-modern/src/components/shared/PageControls.test.tsx",
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts",
"frontend-modern/src/components/shared/TypeColumn.guardrails.test.ts"
]
}
]
],
"match_files": null
}
},
{
@ -3873,7 +3788,8 @@
"internal/unifiedresources/code_standards_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -3922,7 +3838,8 @@
],
"exact_files": []
}
]
],
"match_files": null
}
},
{
@ -4091,7 +4008,8 @@
"internal/api/org_validation_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -4272,7 +4190,8 @@
"frontend-modern/src/utils/__tests__/patrolSummaryPresentation.test.ts"
]
}
]
],
"match_files": null
}
},
{
@ -4284,8 +4203,6 @@
],
"owned_files": [
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
"frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
"frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
"frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts",
"frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx",
"frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx",
@ -4294,7 +4211,6 @@
"frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts",
"frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts",
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts",
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts",
"frontend-modern/src/components/Workloads/DiskList.tsx",
@ -4343,9 +4259,6 @@
"frontend-modern/src/components/Workloads/workloadSelectors.ts",
"frontend-modern/src/components/Workloads/WorkloadsFilter.tsx",
"frontend-modern/src/components/Workloads/workloadsFilterModel.ts",
"frontend-modern/src/components/Workloads/WorkloadsStateCards.tsx",
"frontend-modern/src/components/Workloads/WorkloadsStatsStrip.tsx",
"frontend-modern/src/components/Workloads/WorkloadsSummary.tsx",
"frontend-modern/src/components/Workloads/WorkloadsSurface.tsx",
"frontend-modern/src/components/Workloads/WorkloadsTable.tsx",
"frontend-modern/src/components/Workloads/WorkloadTableHeader.tsx",
@ -4355,7 +4268,6 @@
"frontend-modern/src/routing/routePreload.ts",
"frontend-modern/src/useAppRuntimeState.ts",
"frontend-modern/src/utils/thresholdSliderPresentation.ts",
"frontend-modern/src/utils/throughputPresentation.ts",
"frontend-modern/src/utils/workloadsSummaryCache.ts",
"internal/api/router_bench_test.go",
"internal/api/slo.go",
@ -4386,7 +4298,6 @@
"frontend-modern/src/components/Workloads/MetricBar.test.tsx",
"frontend-modern/src/components/Workloads/StackedMemoryBar.test.tsx",
"frontend-modern/src/components/Workloads/ThresholdSlider.test.tsx",
"frontend-modern/src/components/Workloads/WorkloadsSummary.test.tsx",
"frontend-modern/src/hooks/__tests__/useWorkloads.test.ts",
"internal/api/router_bench_test.go",
"internal/api/slo_bench_test.go",
@ -4434,8 +4345,6 @@
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
"frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
"frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
"frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts",
"frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx",
"frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx",
@ -4444,7 +4353,6 @@
"frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts",
"frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts",
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts",
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts",
"frontend-modern/src/components/Workloads/DiskList.tsx",
@ -4493,9 +4401,6 @@
"frontend-modern/src/components/Workloads/workloadSelectors.ts",
"frontend-modern/src/components/Workloads/WorkloadsFilter.tsx",
"frontend-modern/src/components/Workloads/workloadsFilterModel.ts",
"frontend-modern/src/components/Workloads/WorkloadsStateCards.tsx",
"frontend-modern/src/components/Workloads/WorkloadsStatsStrip.tsx",
"frontend-modern/src/components/Workloads/WorkloadsSummary.tsx",
"frontend-modern/src/components/Workloads/WorkloadsSurface.tsx",
"frontend-modern/src/components/Workloads/WorkloadsTable.tsx",
"frontend-modern/src/components/Workloads/WorkloadTableHeader.tsx",
@ -4508,7 +4413,6 @@
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSummaryModel.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/unifiedResourceTableStateModel.test.ts",
"frontend-modern/src/components/Workloads/__tests__/DiskList.test.tsx",
@ -4538,25 +4442,11 @@
"frontend-modern/src/components/Workloads/MetricBar.test.tsx",
"frontend-modern/src/components/Workloads/StackedMemoryBar.test.tsx",
"frontend-modern/src/components/Workloads/ThresholdSlider.test.tsx",
"frontend-modern/src/components/Workloads/WorkloadsSummary.test.tsx",
"frontend-modern/src/hooks/__tests__/useWorkloads.test.ts",
"frontend-modern/src/utils/__tests__/thresholdSliderPresentation.test.ts",
"frontend-modern/src/utils/__tests__/workloadsSummaryCache.test.ts"
]
},
{
"id": "throughput-presentation-helper",
"label": "throughput presentation helper proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/utils/throughputPresentation.ts"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/utils/__tests__/throughputPresentation.test.ts"
]
},
{
"id": "api-history-slo",
"label": "API history SLO proof",
@ -4570,7 +4460,8 @@
"internal/api/slo_bench_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -4665,7 +4556,8 @@
"pulse-mobile:src/relay/__tests__/proxy.test.ts"
]
}
]
],
"match_files": null
}
},
{
@ -4875,7 +4767,8 @@
"internal/api/system_settings_telemetry_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -4883,9 +4776,7 @@
"lane": "L15",
"contract": "docs/release-control/v6/internal/subsystems/storage-recovery.md",
"owned_prefixes": [
"frontend-modern/src/components/Recovery/",
"frontend-modern/src/components/Storage/",
"frontend-modern/src/features/recovery/",
"frontend-modern/src/features/storageBackups/",
"internal/recovery/"
],
@ -4897,31 +4788,20 @@
"frontend-modern/src/features/proxmox/ProxmoxBackupsTable.tsx",
"frontend-modern/src/features/proxmox/proxmoxBackupSummaryPresentation.ts",
"frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx",
"frontend-modern/src/features/recovery/useRecoverySurfaceState.ts",
"frontend-modern/src/hooks/useRecoveryPoints.ts",
"frontend-modern/src/hooks/useRecoveryPointsFacets.ts",
"frontend-modern/src/hooks/useRecoveryPointsSeries.ts",
"frontend-modern/src/hooks/useRecoveryRollups.ts",
"frontend-modern/src/types/recovery.ts",
"frontend-modern/src/utils/recoveryActionPresentation.ts",
"frontend-modern/src/utils/recoveryArtifactModePresentation.ts",
"frontend-modern/src/utils/recoveryDatePresentation.ts",
"frontend-modern/src/utils/recoveryEmptyStatePresentation.ts",
"frontend-modern/src/utils/recoveryFilterChipPresentation.ts",
"frontend-modern/src/utils/recoveryIssuePresentation.ts",
"frontend-modern/src/utils/recoveryItemTypePresentation.ts",
"frontend-modern/src/utils/recoveryOutcomePresentation.ts",
"frontend-modern/src/utils/recoveryRecordPresentation.ts",
"frontend-modern/src/utils/recoveryStatusPresentation.ts",
"frontend-modern/src/utils/recoveryTablePresentation.ts",
"frontend-modern/src/utils/recoveryTimelineChartPresentation.ts",
"frontend-modern/src/utils/recoveryTimelinePresentation.ts"
"frontend-modern/src/utils/recoveryTimelinePresentation.ts",
"internal/api/setup_script_render.go"
],
"verification": {
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx",
"frontend-modern/src/components/Storage/code_standards.test.ts",
"internal/recovery/recovery_test.go"
],
@ -4949,34 +4829,20 @@
{
"id": "recovery-product-surface",
"label": "recovery product surface proof",
"match_prefixes": [
"frontend-modern/src/components/Recovery/"
],
"match_prefixes": [],
"match_files": [
"frontend-modern/src/features/recovery/useRecoverySurfaceState.ts",
"frontend-modern/src/hooks/useRecoveryPoints.ts",
"frontend-modern/src/hooks/useRecoveryPointsFacets.ts",
"frontend-modern/src/hooks/useRecoveryPointsSeries.ts",
"frontend-modern/src/hooks/useRecoveryRollups.ts",
"frontend-modern/src/types/recovery.ts",
"frontend-modern/src/utils/recoveryActionPresentation.ts",
"frontend-modern/src/utils/recoveryArtifactModePresentation.ts",
"frontend-modern/src/utils/recoveryDatePresentation.ts",
"frontend-modern/src/utils/recoveryEmptyStatePresentation.ts",
"frontend-modern/src/utils/recoveryFilterChipPresentation.ts",
"frontend-modern/src/utils/recoveryIssuePresentation.ts",
"frontend-modern/src/utils/recoveryItemTypePresentation.ts",
"frontend-modern/src/utils/recoveryOutcomePresentation.ts",
"frontend-modern/src/utils/recoveryRecordPresentation.ts",
"frontend-modern/src/utils/recoveryStatusPresentation.ts",
"frontend-modern/src/utils/recoveryTablePresentation.ts",
"frontend-modern/src/utils/recoveryTimelineChartPresentation.ts",
"frontend-modern/src/utils/recoveryTimelinePresentation.ts"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
"tests/integration/tests/17-recovery-layout.spec.ts"
]
@ -4997,7 +4863,6 @@
"frontend-modern/src/components/Storage/__tests__/Storage.test.tsx",
"frontend-modern/src/components/Storage/__tests__/StorageGroupRow.test.tsx",
"frontend-modern/src/components/Storage/__tests__/StoragePoolDetail.test.tsx",
"frontend-modern/src/components/Storage/__tests__/useStoragePageSummary.test.ts",
"frontend-modern/src/components/Storage/code_standards.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/resourceStorageMapping.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storageAdapters.test.ts",
@ -5030,8 +4895,22 @@
"frontend-modern/src/features/proxmox/__tests__/ProxmoxBackupsTable.test.tsx",
"frontend-modern/src/features/proxmox/__tests__/proxmoxBackupSummaryPresentation.test.ts"
]
},
{
"id": "shared-setup-script-boundary",
"label": "shared Proxmox setup-script storage/recovery boundary proof",
"match_prefixes": [],
"match_files": [
"internal/api/setup_script_render.go"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"internal/api/contract_test.go"
]
}
]
],
"match_files": null
}
},
{
@ -5046,10 +4925,7 @@
"frontend-modern/src/components/Discovery/useDiscoveryTabState.ts",
"frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx",
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
"frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
"frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
"frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx",
"frontend-modern/src/components/Infrastructure/resourceBadges.ts",
"frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx",
"frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx",
"frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts",
@ -5070,7 +4946,6 @@
"frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts",
"frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts",
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts",
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts",
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts",
@ -5079,10 +4954,6 @@
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts",
"frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx",
"frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx",
"frontend-modern/src/components/PMG/MailGateway.tsx",
"frontend-modern/src/components/PMG/PMGInstanceDrawer.tsx",
"frontend-modern/src/components/PMG/PMGInstancePanel.tsx",
"frontend-modern/src/components/PMG/ServiceHealthBadge.tsx",
"frontend-modern/src/features/docker/DockerConfigsTable.tsx",
"frontend-modern/src/features/docker/DockerContainersTable.tsx",
"frontend-modern/src/features/docker/DockerImagesTable.tsx",
@ -5193,8 +5064,6 @@
"frontend-modern/src/components/Discovery/useDiscoveryTabState.ts",
"frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx",
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
"frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
"frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
"frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx",
"frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx",
"frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx",
@ -5216,7 +5085,6 @@
"frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts",
"frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts",
"frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts",
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts",
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts",
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts",
@ -5225,9 +5093,6 @@
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts",
"frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx",
"frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx",
"frontend-modern/src/components/PMG/MailGateway.tsx",
"frontend-modern/src/components/PMG/PMGInstanceDrawer.tsx",
"frontend-modern/src/components/PMG/PMGInstancePanel.tsx",
"frontend-modern/src/features/docker/DockerConfigsTable.tsx",
"frontend-modern/src/features/docker/DockerContainersTable.tsx",
"frontend-modern/src/features/docker/DockerImagesTable.tsx",
@ -5263,7 +5128,6 @@
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSelectors.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSummaryModel.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/ResourceChangeSummary.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/ResourceCorrelationSummary.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.discovery.test.ts",
@ -5375,8 +5239,6 @@
"label": "resource type, badge, and service-health presentation helper proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/components/Infrastructure/resourceBadges.ts",
"frontend-modern/src/components/PMG/ServiceHealthBadge.tsx",
"frontend-modern/src/utils/canonicalResourceTypes.ts",
"frontend-modern/src/utils/resourceBadgePresentation.ts",
"frontend-modern/src/utils/resourceTypeCompat.ts",
@ -5388,7 +5250,6 @@
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/components/Infrastructure/__tests__/resourceBadges.test.ts",
"frontend-modern/src/utils/__tests__/canonicalResourceTypes.test.ts",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
"frontend-modern/src/utils/__tests__/resourceBadgePresentation.test.ts",
@ -5489,7 +5350,8 @@
"internal/unifiedresources/store_test.go"
]
}
]
],
"match_files": null
}
}
]

View file

@ -26,10 +26,6 @@ for Pulse instance bridging.
2. `internal/relay/protocol.go`
3. `internal/config/persistence_relay.go`
4. `internal/api/relay_mobile_capability.go`
5. `pulse-mobile:src/relay/client.ts`
6. `pulse-mobile:src/relay/protocol.ts`
7. `pulse-mobile:src/relay/proxy.ts`
8. `pulse-mobile:src/relay/encryption.ts`
## Shared Boundaries
@ -51,7 +47,6 @@ for Pulse instance bridging.
from the UI is allowed to persist the env-effective state to disk;
the override is not stripped before save.
4. Add or change the backend-owned mobile relay capability inventory and compatibility scope mapping through `internal/api/relay_mobile_capability.go`
5. Add or change mobile relay reconnect, drain, channel, encryption, proxy, or identity behavior through `pulse-mobile:src/relay/`
6. Keep desktop and mobile relay changes aligned with the governed server relay surfaces represented by the L7 lane evidence
## Forbidden Paths

View file

@ -30,45 +30,30 @@ state.
1. `internal/recovery/index.go`
2. `internal/recovery/manager/manager.go`
3. `internal/recovery/store/store.go`
4. `frontend-modern/src/components/Recovery/Recovery.tsx`
5. `frontend-modern/src/features/recovery/useRecoverySurfaceState.ts`
6. `frontend-modern/src/components/Recovery/RecoveryProtectedInventorySection.tsx`
7. `frontend-modern/src/components/Recovery/RecoveryActivitySection.tsx`
8. `frontend-modern/src/components/Recovery/RecoveryHistorySection.tsx`
9. `frontend-modern/src/components/Recovery/RecoveryHistoryTable.tsx`
10. `frontend-modern/src/components/Recovery/RecoveryHistoryItemFilter.tsx`
11. `frontend-modern/src/components/Recovery/RecoveryPointDetails.tsx`
12. `frontend-modern/src/components/Recovery/useRecoveryHistorySectionState.ts`
13. `frontend-modern/src/components/Storage/Storage.tsx`
14. `frontend-modern/src/features/storageBackups/storageModelCore.ts`
15. `frontend-modern/src/utils/storageSources.ts`
16. `frontend-modern/src/hooks/useRecoveryPoints.ts`
17. `frontend-modern/src/hooks/useRecoveryRollups.ts`
18. `frontend-modern/src/hooks/useRecoveryPointsFacets.ts`
19. `frontend-modern/src/hooks/useRecoveryPointsSeries.ts`
20. `frontend-modern/src/routing/resourceLinks.ts`
21. `frontend-modern/src/types/recovery.ts`
22. `frontend-modern/src/utils/recoveryDatePresentation.ts`
23. `frontend-modern/src/utils/recoveryEmptyStatePresentation.ts`
24. `frontend-modern/src/utils/recoveryTablePresentation.ts`
25. `frontend-modern/src/utils/recoveryTimelinePresentation.ts`
26. `frontend-modern/src/utils/recoveryItemTypePresentation.ts`
27. `frontend-modern/src/utils/textPresentation.ts`
28. `frontend-modern/src/components/Storage/StorageSummary.tsx`
29. `frontend-modern/src/utils/storageSummaryCache.ts`
30. `frontend-modern/src/components/Storage/useStorageSummaryCharts.ts`
31. `frontend-modern/src/features/storageBackups/storageCapacityDeltaPresentation.ts`
32. `frontend-modern/src/features/proxmox/BackupActivityChart.tsx`
33. `frontend-modern/src/features/proxmox/ProxmoxBackupsCoverageStrip.tsx`
34. `frontend-modern/src/features/proxmox/ProxmoxBackupsTable.tsx`
35. `frontend-modern/src/features/proxmox/proxmoxBackupActivityPresentation.ts`
36. `frontend-modern/src/features/proxmox/proxmoxBackupRecoveryModel.ts`
37. `frontend-modern/src/features/proxmox/proxmoxBackupSummaryPresentation.ts`
38. `frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx`
4. `frontend-modern/src/components/Storage/Storage.tsx`
5. `frontend-modern/src/features/storageBackups/storageModelCore.ts`
6. `frontend-modern/src/utils/storageSources.ts`
7. `frontend-modern/src/hooks/useRecoveryPoints.ts`
8. `frontend-modern/src/routing/resourceLinks.ts`
9. `frontend-modern/src/types/recovery.ts`
10. `frontend-modern/src/utils/recoveryDatePresentation.ts`
11. `frontend-modern/src/utils/recoveryTimelinePresentation.ts`
12. `frontend-modern/src/utils/recoveryItemTypePresentation.ts`
13. `frontend-modern/src/utils/textPresentation.ts`
14. `frontend-modern/src/utils/storageSummaryCache.ts`
15. `frontend-modern/src/components/Storage/useStorageSummaryCharts.ts`
16. `frontend-modern/src/features/storageBackups/storageCapacityDeltaPresentation.ts`
17. `frontend-modern/src/features/proxmox/BackupActivityChart.tsx`
18. `frontend-modern/src/features/proxmox/ProxmoxBackupsCoverageStrip.tsx`
19. `frontend-modern/src/features/proxmox/ProxmoxBackupsTable.tsx`
20. `frontend-modern/src/features/proxmox/proxmoxBackupActivityPresentation.ts`
21. `frontend-modern/src/features/proxmox/proxmoxBackupRecoveryModel.ts`
22. `frontend-modern/src/features/proxmox/proxmoxBackupSummaryPresentation.ts`
23. `frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx`
## Shared Boundaries
1. None.
1. `internal/api/setup_script_render.go` shared with `agent-lifecycle`, `api-contracts`: the generated Proxmox setup-script is a shared boundary across agent lifecycle (forced-command keys, install/uninstall edits), API contracts (rendered token shape and encoded rerun URL), and storage/recovery (backup visibility grants, Pulse-managed temperature SSH keys, and SMART disk-temperature collection).
## Extension Points
@ -216,176 +201,6 @@ outside the storage/recovery contract and must not become restore material,
recovery scope, or a storage/recovery-owned secret source.
1. Add or change recovery-point persistence, rollups, or series derivation through `internal/recovery/`
2. Add or change recovery page UX through `frontend-modern/src/components/Recovery/` and keep canonical route/query/filter state ownership in `frontend-modern/src/features/recovery/useRecoverySurfaceState.ts`
`/recovery` is no longer an authenticated top-level aggregate workspace.
`frontend-modern/src/App.tsx`, `frontend-modern/src/AppLayout.tsx`, the command
palette, keyboard shortcuts, route preloading, and active-tab routing must
keep Recovery unregistered as standalone product navigation rather than as
hidden compatibility. `/storage` has the same retired top-level status for
the Storage owner surface. Storage/recovery state remains owned by the
reusable surfaces that platform pages embed; the app shell must not
duplicate that state or expose it as a separate aggregate route.
Recovery table surfaces must consume the frontend-primitives-owned
`TableCard` frame and `TableCardHeader` header band for protected-item,
recovery-event, and adjacent table-fallback chrome. Storage/recovery may own
table content, filters, columns, and rows, but it must not fork
border/background/overflow table shells, page-local table-title bands, or
lighter open-sided Recovery table frames. Recovery and storage tables must
use the shared `Table` scroll shell directly; if a table needs the card body
surface fill it may pass `wrapperClass`, but it must not nest a local
`overflow-x-auto` div around the shared table. Storage pools and physical
disks inherit the surrounding `StorageContentCard` frame and must not add
a nested `Card` or second scroll wrapper.
Recovery is event-first: recovery surfaces open on recovery events history
by default, and protected-item rollups are a secondary Protection coverage
review reached from the explicit header action or route state owned by the
embedding surface. Recovery must not present
protected rollups and recovery events as equal default sub-tabs because
workload backup posture already belongs on Workloads, while Recovery owns
concrete backup/snapshot/replication evidence. Both surfaces still opt into
shared saved views (`savedViewsKey="recovery-protected"` and
`"recovery-events"`) so operators can save and recall named filter combos
through the shared `SavedViewsMenu`.
Recovery must not restore the retired top-level RecoverySummary metric card
strip. Aggregate recovery counts, success/failure mix, and selected-day
context belong inside the Recovery activity and event-history surfaces that
already own the chart and table filters, rather than as a separate summary
helper that competes with the event-first page hierarchy.
Recovery event filters now compose the shared chip-based `FilterBar`
(`frontend-modern/src/components/shared/FilterBar/FilterBar.tsx`) with a
`FilterDef[]` catalog. The legacy "advanced filter popover" retired: scope,
method, verification, cluster, node, and namespace fold into the same chip
catalog as Item Type, Platform, and Status. Page-level search, column
visibility, and the rollup item filter live in the shared search row; the
chip row appears below it only when filters are active. Recovery must not
reintroduce a parallel `PageControls` toolbar or local no-wrap overrides
that bypass the FilterBar shell.
Compact stable recovery-event filters, such as event outcome/status, ride
the same chip popover affordance the rest of the catalog uses, with
type-ahead in the "+ Filter" menu and chip popovers, but Recovery must keep
route-backed event filter state and query semantics in its recovery owner
rather than introducing a page-local presentation state path.
Recovery inventory protection posture and recovery-event outcome filtering
must stay separate in that owner: protection coverage uses the route-backed
`state` query for health, stale, failed, warning, running, unknown, and
never-succeeded states, while recovery events use `status` for event
outcomes. Legacy `stale=1` input may hydrate the inventory `state=stale`
compatibility path, but event views and event drill-ins must drop
protected-inventory-only state so hidden filters cannot make valid history
look empty.
Recovery activity timeline state is also storage/recovery-owned. The
route-backed range, selected day, and viewport-derived mobile cadence must
stay in `frontend-modern/src/features/recovery/useRecoverySurfaceState.ts`
and the Recovery page owner, with one shared transport-query builder feeding
the chart window to rollups/series and the table window to points/facets.
The series query may append timezone offset for bucket alignment, but
presentation components must not rebuild a second query contract or conflate
the chart and table windows. The activity chart must preserve readable
bucket density across the governed 7, 30, 90, and 365 day ranges: seven-day
timelines fit the available viewport without forced horizontal overflow,
thirty-day timelines may widen on mobile only, and longer ranges scroll in a
bounded chart viewport with sparser labels so first, last, and selected-day
context remain visible without overlapping labels. Selecting a timeline
column remains route-backed and must not become component-local ephemeral
state. The Recovery activity bar chart must use the frontend-primitives
shared chart slot height and matching plot-area height so it keeps visual
parity with the Workloads, Storage, and Infrastructure summary charts
without copying page-local chart sizing.
A selected timeline day is an active event-history filter: table totals,
footer ranges, empty states, and search/date matching must describe the
visible filtered recovery points, not the unfiltered API page metadata.
3. Add or change storage UX through `frontend-modern/src/components/Storage/Storage.tsx` (the canonical `StorageSurface`, embedded inside platform pages), `frontend-modern/src/components/Storage/`, `frontend-modern/src/features/storageBackups/`, the shared storage-source contract in `frontend-modern/src/utils/storageSources.ts`, and the Proxmox-native Ceph table at `frontend-modern/src/features/proxmox/ProxmoxCephTable.tsx`. The old standalone page wrappers under the legacy frontend-modern pages directory were retired with the platform-first primary nav (2026-05-16), and the top-level aggregate Workloads / Storage / Recovery routes were unregistered on 2026-05-25; the canonical component surfaces remain alive as embedded views inside matching platform page sub-tabs. The old top-level Ceph alias is unregistered; Proxmox owns the Ceph tab.
The retired dashboard route must not reintroduce storage or recovery
widgets as compatibility panels. Storage capacity, storage health,
protected-item, and recovery-outcome readiness claims belong on the Storage
and Recovery surfaces or their shared summary components, not in a restored
dashboard panel cluster or Assistant brief.
Storage filters now compose the shared `FilterBar` shell
through `frontend-modern/src/components/Storage/StoragePageControls.tsx`.
The legacy three-layer indirection
(`StoragePageControls``StorageControls``StorageFilter`) and the
bare unlabelled Node `<select>` retired with the migration; Node, Source,
Status, Group by, and the disk-role / disk-group filters all enter the
`FilterDef[]` catalog. Low-cardinality Group by and Status filters render
as unlabeled inline primary controls from that catalog in the same compact
second-row rail as sort, charts, and reset, while Node, Source, Role, and
Group remain menu/chip filters because their option sets are scope- or
data-driven. Embedded platform storage surfaces must provide page-owned
filter aria labels, search placeholders, and empty-search copy for the
visible storage model (for example Proxmox storage, TrueNAS pools and
datasets, or vSphere datastores) while preserving the canonical
`FilterDef[]` route-state contract in `StoragePageControls`. Forced source
storage surfaces must derive Node facet options from nodes that match the
same canonical source scope, and empty filtered storage record sets must
render the storage empty state instead of a synthetic empty "All" group.
Subtabs (Pools / Physical Disks) sit above the bar as navigation, not
filters.
Storage pool table header sorting is storage/recovery-owned and must bind
visible pool columns to the canonical storage sort state instead of
introducing platform-local or table-local sort state. Header clicks must
update the same `sort` / `order` model as the toolbar select, expose
`aria-sort` on the active column, and cover displayed pool columns
including state, source, type, host, protection, usage, and growth. Growth
sorting may consume summary delta data, but non-growth sorts must not track
chart delta maps or re-render row order when chart data refreshes.
Platform-page storage and recovery section tabs
are still capability-gated navigation: Proxmox Storage,
Backups, Ceph, Replication, and adjacent recovery sections may appear only
when the platform page model has matching storage, backup, Ceph,
replication, or provider evidence. Empty capability tabs must not remain as
disabled placeholders on platform pages.
Storage summary chart visibility remains a
page-level display preference, but it now rides the shared
`FilterBar.viewOptionsTrailing` slot together with the sort key/direction
controls. The charts toggle must read as an explicit `Show charts` /
`Hide charts` pressed display action, and the off state must remove the
summary section fully instead of leaving a collapsed summary shell in the
interface. When the storage summary remains visible below the desktop
breakpoint, it must use the frontend-primitives-owned
`StickySummarySection` desktop-sticky mode so the wrapped 2x2 chart grid
scrolls with the page instead of pinning most of a narrow viewport.
Storage must not reintroduce a sticky, frontend-only growth planner or
runway card derived directly from rolling summary chart deltas. Capacity
pressure planning may return as a governed storage/recovery model only when
the signal is stable, backend-owned, and action-oriented enough not to churn
the primary monitoring surface during ordinary refreshes.
Ceph table shells on the storage route share the same frontend-primitives
table contract: `frontend-modern/src/pages/Ceph.tsx` may own Ceph-specific
columns and rows, but horizontal overflow and scrollbar hiding must route
through the shared `Table` wrapper rather than page-local scroll divs or
inline overflow styles.
Storage capacity bars may consume alert-backed disk thresholds, but storage
surfaces do not own threshold selection. `EnhancedStorageBar` and its model
must pass resolved thresholds from the alerts activation boundary into the
shared metric-color helper instead of carrying storage-local usage color
bands.
Storage presentation is topology-first and factual. The Storage page should
show each platform's main storage shape in recognizable platform terms
(arrays, cache pools, pools, datastores, repositories, and physical media)
with capacity and health facts attached to that shape; it must not collapse
non-Proxmox platforms into Proxmox-shaped pool rows or turn factual topology
into prescriptive remediation copy.
Storage state presentation must stay incident/risk-owned. Composite
posture summaries from unified resources may include dependency or protected
workload impact, so `frontend-modern/src/features/storageBackups/` must not
use `storage.postureSummary` or `pbs.postureSummary` as primary issue copy
for healthy resources. Dependency impact belongs to impact summaries and
detail context, while visible state must derive from explicit incidents,
storage risk summaries, or storage-risk reasons.
Unraid storage posture follows that same rule even when it reaches
infrastructure through the agent facet instead of the dedicated Storage
page: no-parity, parity-unavailable, disabled/missing disk, and sync/check
explanations must come from canonical `storage` or `agent.unraid`
storage-risk fields. Storage/recovery-adjacent consumers may render those
summaries, but they must not infer Unraid array health from generic
frontend status labels or from empty no-present slots.
Unraid storage presentation must also consume the canonical Unraid
topology published by unified resources. Array disks, cache/pool storage,
native used/free capacity, device model/transport, temperature, spin state,
and error counters belong in `storage`, `physical_disk`, and `agent.unraid`
payloads before they reach Storage. The Storage page must not attempt to
reconstruct Unraid pools from generic SMART-only rows or treat missing SMART
scans on spun-down disks as missing storage topology.
4. Route transport changes for storage and recovery endpoints through `internal/api/` and the owning `api-contracts` proof routes
Shared API-token transport helpers may be consumed by storage/recovery-
adjacent flows, but `owner_user_id` remains server-authored token identity
@ -855,13 +670,6 @@ recovery scope, or a storage/recovery-owned secret source.
In mock mode, that same compact route must stay aggregate-only and
sampler-prewarmed; storage and recovery must not trigger per-pool chart
reconstruction on the first dashboard request after each mock refresh.
35. Keep storage and recovery websocket reads on the neutral app-runtime boundary. `frontend-modern/src/components/Recovery/RecoveryPointDetails.tsx`, `frontend-modern/src/components/Storage/useStoragePageResources.ts`, and adjacent summary/detail composition may consume live websocket state only through `frontend-modern/src/contexts/appRuntime.ts`, not by importing `frontend-modern/src/App.tsx` or rebuilding shell-local providers.
That same dashboard composition boundary is now a retired negative space.
Storage and recovery-adjacent summaries may reuse
`/api/charts/storage-summary` through their owning summary components, but
they must not restore `/api/resources/dashboard-summary`, paginated
`useUnifiedResources()` transport, or per-pool `/api/metrics-store/history`
fan-out under a dashboard hot path.
36. Keep shared `frontend-modern/src/App.tsx` public-route ownership explicit by
surface. Storage/recovery preview entrypoints such as
`/preview/setup-complete` may remain public app-shell routes, but unrelated
@ -964,14 +772,6 @@ recovery scope, or a storage/recovery-owned secret source.
runtime as populated mock inventory, but they must not expose
`demo_fixtures`, billing identity, or alternate entitlement semantics as
recovery-local transport or operator-facing storage metadata.
38. Keep storage summary fetches scope-owned on the shared summary caches.
`frontend-modern/src/components/Storage/StorageSummary.tsx`,
`frontend-modern/src/utils/storageSummaryCache.ts`,
and `frontend-modern/src/utils/storageSummaryTrendCache.ts` may reuse
cached org/range storage summaries for first paint, but they must not
refetch the full `/api/storage-charts` payload once per adjacent summary
card or invent a dashboard-only storage summary transport path outside the
canonical cache owners.
39. Keep storage and recovery route framing additive and owner-neutral.
`frontend-modern/src/components/Storage/Storage.tsx` and storage/recovery-
adjacent route composition may use the shared `PageHeader` shell for
@ -2737,6 +2537,12 @@ setup and removal preserve Proxmox-managed `/root/.ssh/authorized_keys`
symlinks: adjacent storage and recovery setup flows may depend on the shared
script renderer, but they must not replace the symlink path with a local file
when filtering Pulse-managed `# pulse-` SSH key entries.
That same dependency also assumes the shared PVE setup script binds
temperature-monitoring SSH keys to `/usr/local/sbin/pulse-sensors` and emits
SMART disk temperatures in the wrapper payload. Storage and recovery disk
temperature surfaces may depend on that monitoring-owned SMART merge path, but
they must not reintroduce raw `sensors -j` as the setup contract or build a
storage-local disk-temperature collector.
That same shared discovery dependency also assumes runtime discovery state owns
only structured errors, while adjacent API and WebSocket payloads may derive
the deprecated string `errors` list only as a compatibility field from those

View file

@ -9,9 +9,7 @@
"contract_file": "docs/release-control/v6/internal/subsystems/unified-resources.md",
"status_file": "docs/release-control/v6/internal/status.json",
"registry_file": "docs/release-control/v6/internal/subsystems/registry.json",
"dependency_subsystem_ids": [
"api-contracts"
]
"dependency_subsystem_ids": []
}
```
@ -67,81 +65,73 @@ cross-source deduplication.
43. `frontend-modern/src/features/docker/dockerPageModel.ts`
44. `frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx`
45. `frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx`
46. `frontend-modern/src/components/PMG/PMGInstanceDrawer.tsx`
47. `frontend-modern/src/components/PMG/MailGateway.tsx`
48. `frontend-modern/src/components/PMG/PMGInstancePanel.tsx`
49. `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`
50. `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`
51. `frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx`
52. `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx`
53. `frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx`
54. `frontend-modern/src/components/Infrastructure/resourceBadges.ts`
55. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx`
56. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx`
57. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx`
58. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx`
59. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts`
60. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
61. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts`
62. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerServiceModel.ts`
63. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts`
64. `frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts`
65. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerOperationalModel.ts`
66. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts`
67. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts`
68. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`
69. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
70. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts`
71. `frontend-modern/src/components/Discovery/DiscoveryTab.tsx`
72. `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts`
73. `frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx`
74. `frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts`
75. `frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts`
76. `frontend-modern/src/utils/agentResources.ts`
77. `frontend-modern/src/utils/canonicalResourceTypes.ts`
78. `frontend-modern/src/utils/resourceBadgePresentation.ts`
79. `frontend-modern/src/utils/resourceChangePresentation.ts`
80. `frontend-modern/src/utils/actionAuditPresentation.ts`
81. `frontend-modern/src/utils/resourceCorrelationPresentation.ts`
82. `frontend-modern/src/utils/resourcePlatformData.ts`
83. `frontend-modern/src/utils/resourcePolicyPresentation.ts`
84. `frontend-modern/src/utils/resourceStateAdapters.ts`
85. `frontend-modern/src/utils/resourceTypeCompat.ts`
86. `frontend-modern/src/utils/resourceTypePresentation.ts`
87. `frontend-modern/src/utils/serviceHealthPresentation.ts`
88. `frontend-modern/src/utils/sourceTypePresentation.ts`
89. `frontend-modern/src/utils/workloadTypePresentation.ts`
90. `frontend-modern/src/components/PMG/ServiceHealthBadge.tsx`
91. `frontend-modern/src/utils/resourceIdentity.ts`
92. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerIdentityModel.ts`
93. `frontend-modern/src/hooks/useUnifiedResources.ts`
94. `frontend-modern/src/types/resource.ts`
95. `frontend-modern/src/utils/sourcePlatforms.ts`
96. `frontend-modern/src/utils/platformSupportManifest.generated.ts`
97. `internal/unifiedresources/kubernetes_metric_ids.go`
98. `internal/unifiedresources/policy_posture.go`
99. `internal/unifiedresources/clone.go`
100. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerPresentation.ts`
101. `internal/unifiedresources/storage_consumers.go`
102. `frontend-modern/src/features/standalone/standalonePageModel.ts`
103. `frontend-modern/src/features/standalone/StandalonePageSurface.tsx`
104. `frontend-modern/src/features/standalone/AgentsMachinesTable.tsx`
105. `frontend-modern/src/features/standalone/AvailabilityChecksTable.tsx`
106. `internal/platformsupport/manifest_generated.go`
107. `frontend-modern/src/features/kubernetes/KubernetesControllersTable.tsx`
108. `frontend-modern/src/features/kubernetes/KubernetesPageSurface.tsx`
109. `frontend-modern/src/features/kubernetes/kubernetesPageModel.ts`
110. `frontend-modern/src/features/kubernetes/KubernetesClustersTable.tsx`
111. `frontend-modern/src/features/kubernetes/KubernetesDeploymentsTable.tsx`
112. `frontend-modern/src/features/kubernetes/KubernetesNodesTable.tsx`
113. `frontend-modern/src/features/kubernetes/KubernetesPodsTable.tsx`
114. `frontend-modern/src/features/kubernetes/KubernetesStorageTable.tsx`
115. `frontend-modern/src/features/kubernetes/KubernetesNetworkingTable.tsx`
116. `frontend-modern/src/features/kubernetes/KubernetesServicesTable.tsx`
117. `frontend-modern/src/features/kubernetes/KubernetesConfigTable.tsx`
118. `frontend-modern/src/features/kubernetes/KubernetesPolicyTable.tsx`
119. `frontend-modern/src/features/kubernetes/KubernetesAutoscalingTable.tsx`
120. `frontend-modern/src/features/kubernetes/KubernetesEventsTable.tsx`
46. `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`
47. `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`
48. `frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx`
49. `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx`
50. `frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx`
51. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx`
52. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx`
53. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx`
54. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx`
55. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts`
56. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
57. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts`
58. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerServiceModel.ts`
59. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts`
60. `frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts`
61. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerOperationalModel.ts`
62. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts`
63. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts`
64. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`
65. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
66. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts`
67. `frontend-modern/src/components/Discovery/DiscoveryTab.tsx`
68. `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts`
69. `frontend-modern/src/utils/agentResources.ts`
70. `frontend-modern/src/utils/canonicalResourceTypes.ts`
71. `frontend-modern/src/utils/resourceBadgePresentation.ts`
72. `frontend-modern/src/utils/resourceChangePresentation.ts`
73. `frontend-modern/src/utils/actionAuditPresentation.ts`
74. `frontend-modern/src/utils/resourceCorrelationPresentation.ts`
75. `frontend-modern/src/utils/resourcePlatformData.ts`
76. `frontend-modern/src/utils/resourcePolicyPresentation.ts`
77. `frontend-modern/src/utils/resourceStateAdapters.ts`
78. `frontend-modern/src/utils/resourceTypeCompat.ts`
79. `frontend-modern/src/utils/resourceTypePresentation.ts`
80. `frontend-modern/src/utils/serviceHealthPresentation.ts`
81. `frontend-modern/src/utils/sourceTypePresentation.ts`
82. `frontend-modern/src/utils/workloadTypePresentation.ts`
83. `frontend-modern/src/utils/resourceIdentity.ts`
84. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerIdentityModel.ts`
85. `frontend-modern/src/hooks/useUnifiedResources.ts`
86. `frontend-modern/src/types/resource.ts`
87. `frontend-modern/src/utils/sourcePlatforms.ts`
88. `frontend-modern/src/utils/platformSupportManifest.generated.ts`
89. `internal/unifiedresources/kubernetes_metric_ids.go`
90. `internal/unifiedresources/policy_posture.go`
91. `internal/unifiedresources/clone.go`
92. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerPresentation.ts`
93. `internal/unifiedresources/storage_consumers.go`
94. `frontend-modern/src/features/standalone/standalonePageModel.ts`
95. `frontend-modern/src/features/standalone/StandalonePageSurface.tsx`
96. `frontend-modern/src/features/standalone/AgentsMachinesTable.tsx`
97. `frontend-modern/src/features/standalone/AvailabilityChecksTable.tsx`
98. `internal/platformsupport/manifest_generated.go`
99. `frontend-modern/src/features/kubernetes/KubernetesControllersTable.tsx`
100. `frontend-modern/src/features/kubernetes/KubernetesPageSurface.tsx`
101. `frontend-modern/src/features/kubernetes/kubernetesPageModel.ts`
102. `frontend-modern/src/features/kubernetes/KubernetesClustersTable.tsx`
103. `frontend-modern/src/features/kubernetes/KubernetesDeploymentsTable.tsx`
104. `frontend-modern/src/features/kubernetes/KubernetesNodesTable.tsx`
105. `frontend-modern/src/features/kubernetes/KubernetesPodsTable.tsx`
106. `frontend-modern/src/features/kubernetes/KubernetesStorageTable.tsx`
107. `frontend-modern/src/features/kubernetes/KubernetesNetworkingTable.tsx`
108. `frontend-modern/src/features/kubernetes/KubernetesServicesTable.tsx`
109. `frontend-modern/src/features/kubernetes/KubernetesConfigTable.tsx`
110. `frontend-modern/src/features/kubernetes/KubernetesPolicyTable.tsx`
111. `frontend-modern/src/features/kubernetes/KubernetesAutoscalingTable.tsx`
112. `frontend-modern/src/features/kubernetes/KubernetesEventsTable.tsx`
## Shared Boundaries
@ -218,19 +208,16 @@ engine storage rows must stay host-scoped with table proof hooks so browser
proof can distinguish a populated disk-usage tab from an empty fixture.
1. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts` shared with `performance-and-scalability`: the infrastructure selector pipeline is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
2. `frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx` shared with `performance-and-scalability`: the infrastructure summary surface is both a canonical unified-resource consumer and a fleet-scale summary chart hot-path boundary.
3. `frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts` shared with `performance-and-scalability`: infrastructure summary chart matching, focused-summary view derivation, and metric-series shaping are both a canonical unified-resource consumer surface and a fleet-scale summary chart hot-path boundary.
4. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts` shared with `performance-and-scalability`: resource detail mappers are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
5. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx` shared with `performance-and-scalability`: the unified resource host table card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
6. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx` shared with `performance-and-scalability`: the unified resource PBS section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
7. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx` shared with `performance-and-scalability`: the unified resource PMG section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
8. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx` shared with `performance-and-scalability`: the unified resource service infrastructure card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
9. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx` shared with `performance-and-scalability`: the unified resource table is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
10. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts` shared with `performance-and-scalability`: unified resource service row shaping and I/O emphasis are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
11. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts` shared with `performance-and-scalability`: unified resource table state derivation, sort-cycle policy, service sorting, and responsive column layout are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
12. `frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts` shared with `performance-and-scalability`: infrastructure summary chart polling, cache hydration, and summary-state orchestration are both a canonical unified-resource consumer surface and a fleet-scale summary chart hot-path boundary.
13. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts` shared with `performance-and-scalability`: unified resource table state, grouping, and windowing are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
14. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts` shared with `performance-and-scalability`: unified resource table viewport sync and selected-row reveal are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
2. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts` shared with `performance-and-scalability`: resource detail mappers are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
3. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx` shared with `performance-and-scalability`: the unified resource host table card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
4. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx` shared with `performance-and-scalability`: the unified resource PBS section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
5. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx` shared with `performance-and-scalability`: the unified resource PMG section is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
6. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx` shared with `performance-and-scalability`: the unified resource service infrastructure card is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
7. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx` shared with `performance-and-scalability`: the unified resource table is both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
8. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts` shared with `performance-and-scalability`: unified resource service row shaping and I/O emphasis are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
9. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts` shared with `performance-and-scalability`: unified resource table state derivation, sort-cycle policy, service sorting, and responsive column layout are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
10. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts` shared with `performance-and-scalability`: unified resource table state, grouping, and windowing are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
11. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts` shared with `performance-and-scalability`: unified resource table viewport sync and selected-row reveal are both a canonical unified-resource consumer surface and a fleet-scale performance hot-path boundary.
15. `frontend-modern/src/utils/platformSupportManifest.generated.ts` shared with `frontend-primitives`: the generated platform support projection is both a canonical unified-resource platform union boundary and a shared frontend source/platform vocabulary boundary.
It must carry the manifest `surface_kind` distinction so `docker` remains
machine-readable as a `runtime-lens` while owning infrastructure sources
@ -388,112 +375,6 @@ proof can distinguish a populated disk-usage tab from an empty fixture.
tables must not render ConfigMap or Secret payload values, and metadata-only
rows must not expose key names as if payload fields had been read.
2. Add typed accessors and views in `internal/unifiedresources/views.go`
3. Add source ingestion/adaptation in the adapter layer only
Frontend resource platform contracts in
`frontend-modern/src/types/resource.ts` must include platform ids that the
canonical source/platform adapters can emit. Agent-native storage sources
such as Unraid are first-class platform ids for typing, filtering, tests,
and storage/recovery presentation; consumers must not hide them behind
Proxmox-only or generic agent-only frontend unions when unified resources
has already published explicit platform evidence.
Infrastructure table platform presentation extends through
`frontend-modern/src/utils/resourceBadgePresentation.ts`,
`frontend-modern/src/components/Infrastructure/resourceBadges.ts`, and the
unified resource table sections. Visible infrastructure table headers,
filters, sort keys, and compact row badges must present the owning
infrastructure platform first, while full merged-source detail remains
available for tooltips, accessibility metadata, and routing. Agent telemetry
is collection-method detail when a provider/API platform is also present.
When the primary system identity includes a platform version, table metadata
must suppress an otherwise duplicate unversioned platform source badge while
preserving non-duplicate collection context such as Pulse Agent.
Infrastructure table presentation controls must describe the table mode
rather than a platform-specific resource concept: the grouped/flat toggle
uses operator-facing `Grouped` and `List` wording with a `Group by`
accessible group label, while Proxmox, Kubernetes, and other platform
clusters stay reserved for actual resource identity, filters, and detail
surfaces.
Infrastructure row drawer expansion and summary group focus are local table
interaction state. Inbound infrastructure deep links may hydrate `resource`
and `summaryGroup` into the focused resource or group, but local expansion,
closing, and clearing must not rewrite those params or navigate. Route sync
for this surface is limited to filter-owned `source` and `q` state so
unified-resource rows open inline without refreshing the page shell.
Resource detail drawers must declare their presentation context explicitly.
The default full presentation owns the canonical resource-change,
operator-override, maintenance-verification, and action-audit surfaces. The
`table-row` presentation is reserved for inline platform and resource table
expansion: it presents current-state and identity details from the local
resource payload, may show preloaded change data when present, and must not
start remote history, intelligence, or action-audit reads only to render
empty advanced governance panels inside a table row.
Cluster group headers may use a compact `Cluster` type chip when the group
name itself is only an estate label, but `unifiedResourceTableStateModel.ts`
must suppress that chip when the visible group name already ends in
`Cluster`, so rows do not read as duplicated labels such as `Production
Cluster Cluster`.
Infrastructure host and service table cards must consume the
frontend-primitives-owned `TableCard` frame and `TableCardHeader` header
band; unified resources owns the resource identity, rows, grouping, and
detail presentation, not page-local table-shell chrome. The nested
infrastructure tables must use the shared `Table` primitive's scroll frame
directly instead of reintroducing page-local `overflow-x-auto` wrappers
inside the canonical card shell.
Unified resource host metric cells may consume alert-backed agent
thresholds, but resource consumers must not resolve alert defaults or
overrides locally. Host table state exposes the alerts-owned threshold
resolver and row renderers pass the resolved values into metric bars while
preserving unified-resource ownership over identity, rows, grouping, and
detail presentation.
Docker Swarm service, Kubernetes namespace/deployment, and PMG detail
tables follow that same shell boundary: their platform-specific rows and
actions are unified-resource-owned, while horizontal overflow is owned by
the shared `Table` wrapper rather than drawer-local scroll divs. This
includes both current resource drawers and legacy PMG resource panels that
still consume unified-resource PMG state.
Realtime resource payloads are part of the same consumer contract as REST:
websocket broadcasts must carry canonical identity, discovery targets,
metrics targets, incident rollups, facet counts, and source-specific raw
facets such as `agent`, `storage`, `docker`, and provider API facets. Host
rows and drawers may present compact issue labels such as Unraid
no-parity posture, but those labels must derive from canonical incident or
storage-risk summaries already present on the resource rather than local
platform heuristics or generic status-only wording.
Agent-backed Unraid topology belongs in the canonical adapter layer, not in
storage-page inference. Assigned Unraid array members must produce physical
disk resources even when SMART telemetry is absent, Unraid cache/pool members
must produce agent-backed storage resources with native capacity metrics, and
SMART rows may enrich or merge with those resources only through normalized
device or serial identity. Generic SMART status must not overwrite stronger
Unraid health/risk, and physical-disk metric IDs must prefer stable serials
while normalizing device tokens such as `/dev/sdd` and `sdd [sat]` to the
same fallback identity.
Frontend canonicalization must treat top-level source facets as source
evidence when `platformData.sources` is absent, but it must not invent
provider facets from generic agent telemetry: flat agent disk payloads stay
under the agent source and do not become Proxmox facets without explicit
Proxmox source, node, or workload-shape evidence.
Realtime resource adapters must also apply the canonical host source-bridge
rule to incoming websocket snapshots: agent plus platform/API evidence for
the same host coalesces into one hybrid infrastructure resource, while
same-name agent-only records remain separate until stronger identity or
platform evidence exists.
Proxmox child resources must derive their canonical parent from the owning
node identity across all supported source-key shapes. Guests, storage pools,
and physical disks may arrive with instance-node, cluster-node, or bare-node
parent evidence, and both snapshot ingest and already-unified registry seed
paths must attach them to the same merged host resource before REST,
websocket, Workloads, or Infrastructure consumers render the estate.
This derivation must tolerate node source IDs that retain a previous cluster
alias while the current Proxmox metadata exposes a renamed instance or
cluster label; current node metadata is the fallback source of truth when
source-key lookup misses.
Platform pages that bucket Docker / Podman or Kubernetes resources must use
these canonical resource types and source facets directly. Page-local
scraping, generic type coercion, or "single table for every native object"
fallbacks are not a substitute for adapter-owned image, volume, network,
service, controller, storage, ingress, and event resource projections.
Resource detail mappers now reuse the shared
`frontend-modern/src/utils/textPresentation.ts` title-case helper for sensor
labels so the canonical unified-resource presentation layer owns the wording.
@ -595,7 +476,6 @@ AI-only summary payloads, or page-local heuristics.
generic disk-count aggregate, so resource drawers and incidents do not hide
the actual protection boundary behind a broader count phrase.
6. Add canonical governed name-resolution or policy-aware resource lookup behavior through `internal/unifiedresources/resolve.go` and `internal/unifiedresources/resolve_context.go`
7. Add or change resource drawer timeline/facet/action-history presentation through `frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx`, `frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx`, `frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx`, `frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx`, `frontend-modern/src/components/PMG/PMGInstanceDrawer.tsx`, `frontend-modern/src/components/PMG/MailGateway.tsx`, `frontend-modern/src/components/PMG/PMGInstancePanel.tsx`, `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`, `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`, `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`, `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`, and the governed `internal/api/resources.go` facet/timeline plus `internal/api/activity_audit_handlers.go` action-audit contracts together
8. Add or change discovery-support runtime under the resource drawer through `frontend-modern/src/components/Discovery/DiscoveryTab.tsx` for shell/presentation ownership and `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts` for fetch, websocket-progress, manual-run triggering, and notes-mutation ownership. Embedded drawers may expose the top-level run action through this shared Discovery tab, but they must still call the canonical discovery trigger state path instead of introducing drawer-local API mutations.
9. Keep dashboard and infrastructure freshness on the canonical unified-resource
ownership path. `frontend-modern/src/stores/websocket.ts`,
@ -638,24 +518,6 @@ AI-only summary payloads, or page-local heuristics.
explicit cluster identity such as Kubernetes context or platform cluster
labels; standalone resource names must never be repurposed as synthetic
`clusterId` values.
10. Keep the retired dashboard overview shell absent from unified-resource
consumers. `frontend-modern/src/pages/Dashboard.tsx`,
`frontend-modern/src/hooks/useDashboardOverview.ts`, and
`/api/resources/dashboard-summary` must not be restored as compatibility
readers for KPI cards, problem-resource rows, or top-resource identity.
Infrastructure and Workloads must consume their owning unified-resource
projections directly.
11. Keep summary consumers on the payload they were already given.
`frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts`
may derive chart identity, storage presence, and infrastructure rollups
from the resource snapshot it already owns, but it must not reopen
`useResources()` or start a second unfiltered unified-resource fetch path
as a replacement dashboard summary surface.
12. Keep shared selector hydration visibility-bound. Reusable shells such as
`frontend-modern/src/components/shared/useInfrastructureSelectorState.ts`
may consume `frontend-modern/src/hooks/useUnifiedResources.ts`, but hidden
selectors must pass an explicit `enabled` gate instead of booting the
`all-resources` transport behind a collapsed or non-rendered summary shell.
13. Keep operator-facing resource analysis vocabulary task-first on unified-resource
surfaces. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`,
`frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts`,

View file

@ -91,9 +91,14 @@ Canonical short reply:
### Will old bookmarks and familiar v5 pages still work?
Not necessarily. v6 reorganizes the product around Infrastructure, Workloads,
Storage, and Recovery, with Infrastructure as the default landing surface. Old
platform-era aliases and runbook links should be updated to canonical v6 routes.
Yes for the platform-shaped top-level pages (`/proxmox`, `/docker`,
`/kubernetes`, `/truenas`, `/vmware`, `/standalone`). v6 keeps the
platform-shaped top-level navigation existing v5 operators already
know, on a unified resource model backend (`/api/resources`). The v6
line briefly shipped a unified `Infrastructure` / `Workloads` /
`Storage` / `Recovery` layout across `rc.1`-`rc.5` that was reverted
in `rc.6`; bookmarks into those unified routes should move to the
platform-shaped equivalents.
## Recommended Evaluation Path

View file

@ -92,7 +92,8 @@ docker exec pulse rm -rf /home/pulse/.ssh/id_ed25519*
If you fully understand the risk and are **not** containerized (VM/bare-metal
install), the legacy SSH flow still works. Use a dedicated monitoring user,
restrict the key with `command="sensors -j"` and `from="<pulse-ip>"`, and
restrict the key to the Pulse sensor wrapper with
`command="/usr/local/sbin/pulse-sensors"` and `from="<pulse-ip>"`, and
rotate keys regularly.
#### Auditing Your Deployment

View file

@ -150,7 +150,7 @@ func TestHandleSetupScriptUsesCanonicalShellDownloadHeaders(t *testing.T) {
}
}
func TestPVESetupScriptQuotesTemperatureMonitoringAuthorizedKeyEntry(t *testing.T) {
func TestPVESetupScriptRestrictsTemperatureMonitoringToPulseSensorWrapper(t *testing.T) {
tempDir := t.TempDir()
cfg := &config.Config{
DataPath: tempDir,
@ -173,14 +173,20 @@ func TestPVESetupScriptQuotesTemperatureMonitoringAuthorizedKeyEntry(t *testing.
}
script := rr.Body.String()
if !containsString(
script,
`SSH_SENSORS_KEY_ENTRY="command=\"sensors -j\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_SENSORS_PUBLIC_KEY # pulse-sensors"`,
) {
t.Fatalf("expected temperature monitoring key entry to escape forced-command quotes, got:\n%s", truncate(script, 900))
required := []string{
`PULSE_SENSORS_WRAPPER="/usr/local/sbin/pulse-sensors"`,
`install_pulse_sensors_wrapper() {`,
`"smart": collect_smart(),`,
`SSH_SENSORS_KEY_ENTRY="command=\"$PULSE_SENSORS_WRAPPER\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_SENSORS_PUBLIC_KEY # pulse-sensors"`,
}
if containsString(script, `SSH_SENSORS_KEY_ENTRY="command="sensors -j",`) {
t.Fatalf("unexpected unescaped temperature monitoring key entry in generated script:\n%s", truncate(script, 900))
for _, want := range required {
if !containsString(script, want) {
t.Fatalf("expected setup script to contain %q, got:\n%s", want, truncate(script, 1800))
}
}
if containsString(script, `SSH_SENSORS_KEY_ENTRY="command=\"sensors -j\"`) ||
containsString(script, `SSH_SENSORS_KEY_ENTRY="command="sensors -j",`) {
t.Fatalf("unexpected raw sensors forced-command key entry in generated script:\n%s", truncate(script, 1800))
}
}

View file

@ -5529,6 +5529,18 @@ func TestContract_SetupScriptEmbedsFailFastGuidance(t *testing.T) {
if strings.Contains(script, `/root/.ssh/authorized_keys.tmp`) || strings.Contains(script, `echo "$SSH_SENSORS_KEY_ENTRY" >> /root/.ssh/authorized_keys`) {
t.Fatalf("setup script preserved symlink-breaking authorized_keys rewrite: %s", script)
}
if !strings.Contains(script, `PULSE_SENSORS_WRAPPER="/usr/local/sbin/pulse-sensors"`) {
t.Fatalf("setup script missing canonical Pulse sensor wrapper path: %s", script)
}
if !strings.Contains(script, `SSH_SENSORS_KEY_ENTRY="command=\"$PULSE_SENSORS_WRAPPER\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_SENSORS_PUBLIC_KEY # pulse-sensors"`) {
t.Fatalf("setup script must force temperature SSH keys to the Pulse sensor wrapper: %s", script)
}
if !strings.Contains(script, `"smart": collect_smart(),`) || !strings.Contains(script, `apt-get install -y smartmontools`) {
t.Fatalf("setup script missing SMART temperature wrapper contract: %s", script)
}
if strings.Contains(script, `SSH_SENSORS_KEY_ENTRY="command=\"sensors -j\"`) {
t.Fatalf("setup script preserved stale raw sensors forced command: %s", script)
}
if strings.Contains(script, `echo "Please run this script as root"`) {
t.Fatalf("setup script preserved stale root-only guidance: %s", script)
}

View file

@ -606,16 +606,263 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
echo ""
SSH_SENSORS_PUBLIC_KEY="%s"
SSH_SENSORS_KEY_ENTRY="command=\"sensors -j\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_SENSORS_PUBLIC_KEY # pulse-sensors"
PULSE_SENSORS_WRAPPER="/usr/local/sbin/pulse-sensors"
SSH_SENSORS_KEY_ENTRY="command=\"$PULSE_SENSORS_WRAPPER\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_SENSORS_PUBLIC_KEY # pulse-sensors"
TEMPERATURE_ENABLED=false
install_pulse_sensors_wrapper() {
mkdir -p "$(dirname "$PULSE_SENSORS_WRAPPER")"
cat > "$PULSE_SENSORS_WRAPPER" <<'PULSE_SENSORS_WRAPPER_EOF'
#!/bin/sh
set -eu
if command -v python3 >/dev/null 2>&1; then
python3 - <<'PY'
import datetime
import json
import os
import re
import shlex
import shutil
import subprocess
import sys
def run_command(args, timeout=8):
try:
return subprocess.run(
args,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
timeout=timeout,
check=False,
)
except Exception:
return None
def load_sensors():
if shutil.which("sensors"):
result = run_command(["sensors", "-j"])
if result and result.stdout.strip():
try:
return json.loads(result.stdout)
except Exception:
pass
thermal_path = "/sys/class/thermal/thermal_zone0/temp"
try:
with open(thermal_path, "r", encoding="utf-8") as handle:
raw = handle.read().strip()
value = float(raw)
if value > 1000:
value = value / 1000
return {"rpitemp": {"temp1": {"temp1_input": value}}}
except Exception:
return {}
def smartctl_path():
return shutil.which("smartctl")
def smart_targets(path):
result = run_command([path, "--scan-open"])
if not result or not result.stdout.strip():
return []
targets = []
seen = set()
typed_by_path = set()
for raw_line in result.stdout.splitlines():
line = raw_line.split("#", 1)[0].strip()
if not line:
continue
try:
fields = shlex.split(line)
except ValueError:
fields = line.split()
if not fields:
continue
device = fields[0].strip()
if not device.startswith("/"):
continue
device_type = ""
for idx, field in enumerate(fields[:-1]):
if field == "-d":
device_type = fields[idx + 1].strip()
break
key = (device, device_type)
if key in seen:
continue
seen.add(key)
if device_type:
typed_by_path.add(device)
targets.append(key)
return [(device, dtype) for device, dtype in targets if dtype or device not in typed_by_path]
def disk_type(data):
device = data.get("device") if isinstance(data.get("device"), dict) else {}
protocol = str(device.get("protocol") or "").lower()
dtype = str(device.get("type") or "").lower()
if "nvme" in protocol or "nvme" in dtype:
return "nvme"
if "sas" in protocol or "scsi" in protocol or "sas" in dtype:
return "sas"
if "ata" in protocol or "sata" in protocol or "sat" in dtype:
return "sata"
return dtype
def smart_health(data):
status = data.get("smart_status")
if isinstance(status, dict) and "passed" in status:
return "PASSED" if status.get("passed") else "FAILED"
return "UNKNOWN"
def format_wwn(data):
wwn = data.get("wwn")
if not isinstance(wwn, dict) or not wwn.get("naa"):
return ""
try:
naa = int(wwn.get("naa") or 0)
oui = int(wwn.get("oui") or 0)
ident = int(wwn.get("id") or 0)
except Exception:
return ""
return f"{naa:x}-{oui:x}-{ident:x}"
def first_valid_temperature(candidates):
for value in candidates:
try:
temp = int(float(value))
except Exception:
continue
if 0 < temp < 150:
return temp
return 0
def raw_attribute_temperature(data):
attrs = data.get("ata_smart_attributes")
if not isinstance(attrs, dict):
return 0
table = attrs.get("table")
if not isinstance(table, list):
return 0
for attr in table:
if not isinstance(attr, dict) or attr.get("id") not in (190, 194):
continue
raw = attr.get("raw") if isinstance(attr.get("raw"), dict) else {}
candidates = []
raw_string = raw.get("string")
if isinstance(raw_string, str):
match = re.search(r"(-?\d{1,3})", raw_string)
if match:
candidates.append(match.group(1))
if "value" in raw:
candidates.append(raw.get("value"))
temperature = first_valid_temperature(candidates)
if temperature > 0:
return temperature
return 0
def smart_temperature(data):
temperatures = []
temp = data.get("temperature")
if isinstance(temp, dict):
temperatures.append(temp.get("current"))
nvme = data.get("nvme_smart_health_information_log")
if isinstance(nvme, dict):
temperatures.append(nvme.get("temperature"))
sct = data.get("ata_sct_status")
if isinstance(sct, dict):
current = sct.get("current")
if isinstance(current, dict):
temperatures.append(current.get("value"))
temperature = first_valid_temperature(temperatures)
if temperature > 0:
return temperature
return raw_attribute_temperature(data)
def collect_smart():
path = smartctl_path()
if not path:
return []
entries = []
observed_at = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
for device, device_type in smart_targets(path):
args = [path]
if device_type:
args.extend(["-d", device_type])
args.extend(["-n", "standby,3", "-i", "-A", "-H", "--json=o", device])
result = run_command(args)
if not result or not result.stdout.strip():
continue
try:
data = json.loads(result.stdout)
except Exception:
continue
power_mode = str(data.get("power_mode") or "").lower()
entries.append({
"device": device,
"model": str(data.get("model_name") or data.get("model_family") or "").strip(),
"serial": str(data.get("serial_number") or "").strip(),
"wwn": format_wwn(data),
"type": disk_type(data),
"temperature": smart_temperature(data),
"health": smart_health(data),
"standbySkipped": "standby" in power_mode or "sleep" in power_mode,
"lastUpdated": observed_at,
})
return entries
payload = {
"sensors": load_sensors(),
"smart": collect_smart(),
}
json.dump(payload, sys.stdout, separators=(",", ":"))
sys.stdout.write("\n")
PY
exit 0
fi
sensors_json="{}"
if command -v sensors >/dev/null 2>&1; then
sensors_json="$(sensors -j 2>/dev/null || printf '{}')"
fi
if [ -z "$sensors_json" ]; then
sensors_json="{}"
fi
printf '{"sensors":%%s,"smart":[]}\n' "$sensors_json"
PULSE_SENSORS_WRAPPER_EOF
chown root:root "$PULSE_SENSORS_WRAPPER" 2>/dev/null || true
chmod 755 "$PULSE_SENSORS_WRAPPER"
}
if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
echo "📊 Enable Temperature Monitoring?"
echo ""
echo "Collect CPU and drive temperatures via secure SSH connection."
echo ""
echo "Security:"
echo " • SSH key authentication with forced command (sensors -j only)"
echo " • SSH key authentication with forced command (Pulse sensor wrapper only)"
echo " • No shell access, port forwarding, or other SSH features"
echo " • Keys stored in Pulse service user's home directory"
echo ""
@ -641,6 +888,14 @@ if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
if [[ $SSH_REPLY =~ ^[Yy]$ ]]; then
echo "Configuring temperature monitoring..."
SENSORS_WRAPPER_OK=false
if install_pulse_sensors_wrapper; then
SENSORS_WRAPPER_OK=true
echo " ✓ Pulse sensor wrapper installed"
else
echo " ⚠️ Failed to install Pulse sensor wrapper"
fi
# Add key to root's authorized_keys
AUTH_KEYS="$(resolve_authorized_keys_path)"
mkdir -p "$(dirname "$AUTH_KEYS")"
@ -650,7 +905,7 @@ if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
# Remove any old pulse keys
SENSORS_KEY_OK=false
TMP_AUTH_KEYS="$(mktemp /tmp/.pulse-authorized-keys.XXXXXX 2>/dev/null)" || TMP_AUTH_KEYS=""
if [ -n "$TMP_AUTH_KEYS" ] && [ -f "$TMP_AUTH_KEYS" ]; then
if [ "$SENSORS_WRAPPER_OK" = true ] && [ -n "$TMP_AUTH_KEYS" ] && [ -f "$TMP_AUTH_KEYS" ]; then
if [ -f "$AUTH_KEYS" ]; then
grep -vF "# pulse-" "$AUTH_KEYS" > "$TMP_AUTH_KEYS" 2>/dev/null
GREP_EXIT=$?
@ -671,10 +926,16 @@ if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
fi
fi
else
echo " ⚠️ Failed to create temp file - cannot update authorized_keys"
if [ "$SENSORS_WRAPPER_OK" = true ]; then
echo " ⚠️ Failed to create temp file - cannot update authorized_keys"
elif [ -n "$TMP_AUTH_KEYS" ]; then
rm -f "$TMP_AUTH_KEYS" 2>/dev/null || true
fi
fi
if [ "$SENSORS_KEY_OK" = true ]; then
echo " ✓ Sensors key configured (restricted to sensors -j)"
echo " ✓ Sensors key configured (restricted to Pulse sensor wrapper)"
else
echo " ⚠️ Sensors key was not configured"
fi
# Check if this is a Raspberry Pi
@ -733,8 +994,21 @@ if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
TEMPERATURE_SETUP_SUCCESS=true
fi
if ! command -v smartctl &> /dev/null; then
echo " ✓ Installing smartmontools for disk SMART temperatures..."
apt-get update -qq > /dev/null 2>&1 || true
if apt-get install -y smartmontools > /dev/null 2>&1; then
echo " ✓ smartmontools installed successfully"
else
echo " ⚠️ Could not install smartmontools"
echo " HDD/SATA/SAS disk temperatures require smartctl or the unified agent."
fi
else
echo " ✓ smartmontools package verified"
fi
echo ""
if [ "$TEMPERATURE_SETUP_SUCCESS" = true ]; then
if [ "$TEMPERATURE_SETUP_SUCCESS" = true ] && [ "$SENSORS_KEY_OK" = true ]; then
echo "✓ Temperature monitoring enabled"
if [ "$IS_RPI" = true ]; then
echo " Using Raspberry Pi native temperature interface"
@ -743,7 +1017,7 @@ if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
TEMPERATURE_ENABLED=true
else
echo "⚠️ Temperature monitoring setup incomplete"
echo " You can re-run this script after installing lm-sensors"
echo " You can re-run this script after installing lm-sensors and smartmontools"
fi
else
echo "Skipping temperature monitoring."

View file

@ -160,6 +160,18 @@ func TestPVEBackupPermissionWarningsPreserveTokenACLRepair(t *testing.T) {
}
}
func TestLegacySSHTemperatureUsesPulseSensorWrapperContract(t *testing.T) {
if !strings.Contains(pulseSensorsSSHCommand, "/usr/local/sbin/pulse-sensors") {
t.Fatalf("legacy SSH temperature command must prefer Pulse sensor wrapper, got %q", pulseSensorsSSHCommand)
}
if !strings.Contains(pulseSensorsSSHCommand, "sensors -j") {
t.Fatalf("legacy SSH temperature command must preserve sensors -j fallback for old forced keys, got %q", pulseSensorsSSHCommand)
}
if strings.Contains(pulseSensorsSSHCommand, "smartctl") {
t.Fatalf("SMART collection belongs inside the remote wrapper, not the local SSH command: %q", pulseSensorsSSHCommand)
}
}
func TestPBSBackupsSnapshotPreservesSourceArtifactFields(t *testing.T) {
state := models.NewState()
backupTime := time.Date(2026, 5, 25, 1, 34, 25, 0, time.UTC)

View file

@ -22,6 +22,7 @@ import (
const maxTemperatureCommandOutputSize = 1 << 20 // 1 MiB
const defaultSSHCommandTimeout = 15 * time.Second
const pulseSensorsSSHCommand = "if command -v /usr/local/sbin/pulse-sensors >/dev/null 2>&1; then /usr/local/sbin/pulse-sensors; else sensors -j 2>/dev/null || true; fi"
var errTemperatureCommandOutputTooLarge = errors.New("temperature command output exceeded limit")
@ -150,10 +151,11 @@ func (tc *TemperatureCollector) CollectTemperature(ctx context.Context, nodeHost
return &models.Temperature{Available: false}, nil
}
// Direct SSH (legacy method for non-containerized deployments)
// Try sensors first, fall back to Raspberry Pi method if that fails
// sensors exits non-zero when optional subfeatures fail; "|| true" keeps the JSON for parsing (#600)
output, err := tc.runSSHCommand(ctx, host, "sensors -j 2>/dev/null || true")
// Direct SSH (legacy method for non-containerized deployments).
// New setup scripts restrict the key to /usr/local/sbin/pulse-sensors, which emits
// the canonical {sensors, smart} payload. Older keys ignore the requested command
// and still force sensors -j, so keep the parser backward-compatible.
output, err := tc.runSSHCommand(ctx, host, pulseSensorsSSHCommand)
if err != nil || strings.TrimSpace(output) == "" {
if tc.disableLegacySSHOnAuthFailure(err, nodeName, host) {
return &models.Temperature{Available: false}, nil

View file

@ -170,6 +170,39 @@ func TestTemperatureCollector_ParseSensorsJSON_StringTemps(t *testing.T) {
assert.InDelta(t, 42.0, temp.NVMe[0].Temp, 0.0001)
}
func TestTemperatureCollector_ParseSensorsJSON_WrapperSMARTTemps(t *testing.T) {
tc := &TemperatureCollector{}
jsonStr := `{
"sensors": {
"coretemp-isa-0000": {
"Package id 0": { "temp1_input": 46.0 }
}
},
"smart": [
{
"device": "/dev/sda",
"model": "Samsung SSD 860 EVO",
"serial": "SATA-SERIAL-1",
"wwn": "5-c50-1234",
"type": "sata",
"temperature": 34,
"lastUpdated": "2026-05-26T18:00:00Z"
}
]
}`
temp, err := tc.parseSensorsJSON(jsonStr)
require.NoError(t, err)
require.NotNil(t, temp)
assert.True(t, temp.Available)
assert.True(t, temp.HasSMART)
require.Len(t, temp.SMART, 1)
assert.Equal(t, "/dev/sda", temp.SMART[0].Device)
assert.Equal(t, "SATA-SERIAL-1", temp.SMART[0].Serial)
assert.Equal(t, 34, temp.SMART[0].Temperature)
assert.Equal(t, "sata", temp.SMART[0].Type)
}
func TestTemperatureCollector_HelperMethods(t *testing.T) {
// extractCoreNumber
// Private methods are hard to test directly from separate package if using _test,

File diff suppressed because it is too large Load diff

View file

@ -206,7 +206,14 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertNotIn("guest-capacity continuity", support_pack)
self.assertNotIn("core monitoring unlimited", normalize_ws(support_pack))
def test_stable_release_packet_keeps_infrastructure_as_current_landing_surface(self) -> None:
def test_stable_release_packet_describes_platform_shaped_frontend_on_unified_backend(self) -> None:
"""After the rc.6 IA revert, the stable v6 release docs must describe
the frontend as platform-shaped (Proxmox, Docker, Kubernetes, TrueNAS,
vSphere, Standalone) on a unified resource backend. Drift back to the
rc.1-rc.5 unified Infrastructure/Workloads/Storage/Recovery framing in
the stable packet would mislead operators reading the canonical v6
release notes.
"""
stable_docs = {
"release_notes": read("docs/releases/RELEASE_NOTES_v6.md"),
"changelog": read("docs/releases/V6_CHANGELOG.md"),
@ -216,7 +223,8 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
for name, content in stable_docs.items():
with self.subTest(name=name):
normalized = normalize_ws(content)
self.assertIn("Infrastructure", content)
# Anti-patterns for prior product shapes the stable packet
# must not describe as current truth.
self.assertNotIn("default route lands on `Dashboard`", normalized)
self.assertNotIn("around Dashboard, Infrastructure", normalized)
self.assertNotIn(
@ -224,15 +232,32 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
content,
)
self.assertNotIn("- `Dashboard`", content)
# Anti-patterns for the reverted unified IA so the stable
# packet does not silently drift back to it.
self.assertNotIn(
"Authenticated users now land on `Infrastructure`",
normalized,
)
self.assertNotIn(
"default route lands on `Infrastructure`",
normalized,
)
self.assertNotIn(
"Infrastructure as the default landing surface",
content,
)
self.assertIn(
"Authenticated users now land on `Infrastructure`",
"platform-shaped top-level navigation",
normalize_ws(stable_docs["release_notes"]),
)
self.assertIn("default route lands on `Infrastructure`", normalize_ws(stable_docs["changelog"]))
self.assertIn(
"Infrastructure as the default landing surface",
stable_docs["operator_support_pack"],
"platform-shaped",
normalize_ws(stable_docs["changelog"]),
)
self.assertIn(
"platform-shaped top-level pages",
normalize_ws(stable_docs["operator_support_pack"]),
)
def test_rc1_changelog_keeps_current_free_first_licensing_posture(self) -> None:

View file

@ -334,62 +334,6 @@ class SubsystemLookupTest(unittest.TestCase):
unified_resources_match["verification_requirement"]["exact_files"],
)
def test_lookup_paths_assigns_infrastructure_summary_model_to_shared_infrastructure_hot_path(
self,
) -> None:
result = lookup_paths(
["frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts"]
)
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"performance-and-scalability", "unified-resources"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"performance-and-scalability", "unified-resources"},
)
self.assertEqual(
file_entry["shared_ownership"]["subsystems"],
["performance-and-scalability", "unified-resources"],
)
matches_by_subsystem = {
match["subsystem"]: match for match in file_entry["matches"]
}
performance_match = matches_by_subsystem["performance-and-scalability"]
self.assertEqual(
performance_match["contract"],
"docs/release-control/v6/internal/subsystems/performance-and-scalability.md",
)
self.assertEqual(performance_match["lane_context"]["lane_id"], "L10")
self.assertEqual(
performance_match["verification_requirement"]["id"],
"workloads-hot-path",
)
self.assertIn(
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSummaryModel.test.ts",
performance_match["verification_requirement"]["exact_files"],
)
unified_resources_match = matches_by_subsystem["unified-resources"]
self.assertEqual(
unified_resources_match["contract"],
"docs/release-control/v6/internal/subsystems/unified-resources.md",
)
self.assertEqual(unified_resources_match["lane_context"]["lane_id"], "L13")
self.assertEqual(
unified_resources_match["verification_requirement"]["id"],
"resource-consumers",
)
self.assertIn(
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSummaryModel.test.ts",
unified_resources_match["verification_requirement"]["exact_files"],
)
def test_lookup_paths_assigns_resource_detail_drawer_discovery_model_to_unified_resources(self) -> None:
result = lookup_paths(
["frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts"]
@ -527,34 +471,6 @@ class SubsystemLookupTest(unittest.TestCase):
"organization-billing-surface",
)
def test_lookup_paths_assigns_pro_license_panel_to_cloud_paid(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Settings/ProLicensePanel.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"cloud-paid"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"cloud-paid"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/cloud-paid.md")
self.assertEqual(match["lane_context"]["lane_id"], "L3")
self.assertEqual(
match["verification_requirement"]["id"],
"pro-license-surface",
)
self.assertEqual(
match["verification_requirement"]["exact_files"],
[
"frontend-modern/src/components/Settings/__tests__/MonitoredSystemLedgerPanel.test.tsx",
"frontend-modern/src/components/Settings/__tests__/ProLicensePanel.test.tsx",
],
)
def test_lookup_paths_assigns_pro_license_state_owner_to_cloud_paid(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Settings/useProLicensePanelState.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
@ -576,24 +492,6 @@ class SubsystemLookupTest(unittest.TestCase):
"pro-license-surface",
)
def test_lookup_paths_assigns_monitored_system_ledger_panel_to_cloud_paid(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Settings/MonitoredSystemLedgerPanel.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"cloud-paid"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"cloud-paid"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/cloud-paid.md")
self.assertEqual(match["lane_context"]["lane_id"], "L3")
self.assertEqual(match["verification_requirement"]["id"], "pro-license-surface")
def test_lookup_paths_assigns_pricing_handoff_to_cloud_paid(self) -> None:
result = lookup_paths(["frontend-modern/src/pages/PricingHandoff.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
@ -630,26 +528,6 @@ class SubsystemLookupTest(unittest.TestCase):
self.assertEqual(match["lane_context"]["lane_id"], "L3")
self.assertEqual(match["verification_requirement"]["id"], "commercial-plan-models")
def test_lookup_paths_assigns_monitored_system_definition_disclosure_to_cloud_paid(self) -> None:
result = lookup_paths(
["frontend-modern/src/components/Commercial/MonitoredSystemDefinitionDisclosure.tsx"]
)
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"cloud-paid"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"cloud-paid"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/cloud-paid.md")
self.assertEqual(match["lane_context"]["lane_id"], "L3")
self.assertEqual(match["verification_requirement"]["id"], "commercial-plan-models")
def test_lookup_paths_assigns_relay_settings_state_owner_to_cloud_paid(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Settings/useRelaySettingsPanelState.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
@ -671,31 +549,6 @@ class SubsystemLookupTest(unittest.TestCase):
"relay-frontend-surfaces",
)
def test_lookup_paths_assigns_recovery_surface_to_storage_recovery(self) -> None:
# The legacy /recovery route shell (pages/Recovery.tsx) was retired
# with the platform-first migration; recovery is now exercised via
# the embedded surface at components/Recovery/Recovery.tsx.
result = lookup_paths(["frontend-modern/src/components/Recovery/Recovery.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_reports_retired_dashboard_page_as_unowned(self) -> None:
path = "frontend-modern/src/pages/Dashboard.tsx"
result = lookup_paths([path])
@ -741,60 +594,6 @@ class SubsystemLookupTest(unittest.TestCase):
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
def test_lookup_paths_assigns_recovery_points_facets_hook_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/hooks/useRecoveryPointsFacets.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
def test_lookup_paths_assigns_recovery_points_series_hook_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/hooks/useRecoveryPointsSeries.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
def test_lookup_paths_assigns_recovery_rollups_hook_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/hooks/useRecoveryRollups.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
def test_lookup_paths_reports_retired_dashboard_recovery_hook_as_unowned(self) -> None:
path = "frontend-modern/src/hooks/useDashboardRecovery.ts"
result = lookup_paths([path])
@ -804,24 +603,6 @@ class SubsystemLookupTest(unittest.TestCase):
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(file_entry["matches"], [])
def test_lookup_paths_assigns_recovery_summary_component_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Recovery/RecoverySummary.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
def test_lookup_paths_reports_retired_dashboard_recovery_presentation_as_unowned(self) -> None:
path = "frontend-modern/src/utils/dashboardRecoveryPresentation.ts"
result = lookup_paths([path])
@ -831,44 +612,6 @@ class SubsystemLookupTest(unittest.TestCase):
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(file_entry["matches"], [])
def test_lookup_paths_assigns_storage_panel_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Storage/Storage.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "storage-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
[
"frontend-modern/src/components/Storage/__tests__/DiskList.test.tsx",
"frontend-modern/src/components/Storage/__tests__/Storage.test.tsx",
"frontend-modern/src/components/Storage/__tests__/StorageGroupRow.test.tsx",
"frontend-modern/src/components/Storage/__tests__/StoragePoolDetail.test.tsx",
"frontend-modern/src/components/Storage/__tests__/useStoragePageSummary.test.ts",
"frontend-modern/src/components/Storage/code_standards.test.ts",
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/resourceStorageMapping.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storageAdapters.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storageAlertState.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storageDomain.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storageModelCore.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storagePagePresentation.test.ts",
"frontend-modern/src/features/storageBackups/__tests__/storagePoolsTablePresentation.test.ts",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
],
)
def test_lookup_paths_reports_retired_dashboard_storage_presentation_as_unowned(self) -> None:
path = "frontend-modern/src/utils/dashboardStoragePresentation.ts"
result = lookup_paths([path])
@ -902,72 +645,6 @@ class SubsystemLookupTest(unittest.TestCase):
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "storage-product-surface")
def test_lookup_paths_assigns_recovery_types_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/types/recovery.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_date_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryDatePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_status_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryStatusPresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_reports_retired_recovery_summary_presentation_as_unowned(self) -> None:
path = "frontend-modern/src/utils/recoverySummaryPresentation.ts"
result = lookup_paths([path])
@ -977,226 +654,6 @@ class SubsystemLookupTest(unittest.TestCase):
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(file_entry["matches"], [])
def test_lookup_paths_assigns_recovery_record_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryRecordPresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_outcome_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryOutcomePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_action_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryActionPresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_artifact_mode_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryArtifactModePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_empty_state_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryEmptyStatePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_filter_chip_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryFilterChipPresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_issue_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryIssuePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_table_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryTablePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_timeline_chart_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryTimelineChartPresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_recovery_timeline_presentation_to_storage_recovery(self) -> None:
result = lookup_paths(["frontend-modern/src/utils/recoveryTimelinePresentation.ts"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"storage-recovery"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"storage-recovery"},
)
match = file_entry["matches"][0]
self.assertEqual(match["contract"], "docs/release-control/v6/internal/subsystems/storage-recovery.md")
self.assertEqual(match["lane_context"]["lane_id"], "L15")
self.assertEqual(match["verification_requirement"]["id"], "recovery-product-surface")
self.assertEqual(
match["verification_requirement"]["exact_files"],
RECOVERY_PRODUCT_SURFACE_EXACT_FILES,
)
def test_lookup_paths_assigns_api_token_manager_to_api_contracts(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Settings/APITokenManager.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
@ -1516,43 +973,6 @@ class SubsystemLookupTest(unittest.TestCase):
],
)
def test_lookup_paths_assigns_results_step_to_agent_lifecycle(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Infrastructure/deploy/ResultsStep.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"agent-lifecycle"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"agent-lifecycle"},
)
match = file_entry["matches"][0]
self.assertEqual(
match["contract"],
"docs/release-control/v6/internal/subsystems/agent-lifecycle.md",
)
self.assertEqual(match["lane_context"]["lane_id"], "L16")
self.assertEqual(
match["verification_requirement"]["id"],
"deploy-fallback-install-surface",
)
self.assertEqual(
match["verification_requirement"]["exact_files"],
[
"frontend-modern/src/components/Infrastructure/__tests__/DeployStepComponents.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/CandidatesStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/ConfirmStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/DeployingStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/PreflightStep.test.tsx",
"frontend-modern/src/components/Infrastructure/deploy/__tests__/ResultsStep.test.tsx",
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
],
)
def test_lookup_paths_assigns_agent_profiles_panel_to_agent_lifecycle(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Settings/AgentProfilesPanel.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
@ -1619,70 +1039,6 @@ class SubsystemLookupTest(unittest.TestCase):
],
)
def test_lookup_paths_assigns_infrastructure_workspace_model_to_agent_lifecycle(self) -> None:
result = lookup_paths(
["frontend-modern/src/components/Settings/infrastructureWorkspaceModel.ts"]
)
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"agent-lifecycle"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"agent-lifecycle"},
)
match = file_entry["matches"][0]
self.assertEqual(
match["contract"],
"docs/release-control/v6/internal/subsystems/agent-lifecycle.md",
)
self.assertEqual(match["lane_context"]["lane_id"], "L16")
self.assertEqual(
match["verification_requirement"]["id"],
"platform-connections-workspace-surface",
)
self.assertEqual(
match["verification_requirement"]["exact_files"],
PLATFORM_CONNECTIONS_WORKSPACE_EXACT_FILES,
)
def test_lookup_paths_assigns_infrastructure_workspace_to_agent_lifecycle(
self,
) -> None:
result = lookup_paths(
[
"frontend-modern/src/components/Settings/InfrastructureWorkspace.tsx"
]
)
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"agent-lifecycle"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"agent-lifecycle"},
)
match = file_entry["matches"][0]
self.assertEqual(
match["contract"],
"docs/release-control/v6/internal/subsystems/agent-lifecycle.md",
)
self.assertEqual(match["lane_context"]["lane_id"], "L16")
self.assertEqual(
match["verification_requirement"]["id"],
"platform-connections-workspace-surface",
)
self.assertEqual(
match["verification_requirement"]["exact_files"],
PLATFORM_CONNECTIONS_WORKSPACE_EXACT_FILES,
)
def test_lookup_paths_assigns_connections_table_model_to_agent_lifecycle(self) -> None:
result = lookup_paths(
["frontend-modern/src/components/Settings/connectionsTableModel.ts"]
@ -1713,31 +1069,6 @@ class SubsystemLookupTest(unittest.TestCase):
CONNECTIONS_LEDGER_WORKSPACE_EXACT_FILES,
)
def test_lookup_paths_assigns_infrastructure_settings_model_to_agent_lifecycle(self) -> None:
result = lookup_paths(
["frontend-modern/src/components/Settings/infrastructureSettingsModel.ts"]
)
self.assertEqual(result["unowned_runtime_files"], [])
self.assertEqual(
{item["subsystem"] for item in result["impacted_subsystems"]},
{"agent-lifecycle"},
)
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(
{match["subsystem"] for match in file_entry["matches"]},
{"agent-lifecycle"},
)
match = file_entry["matches"][0]
self.assertEqual(
match["verification_requirement"]["id"],
"platform-connections-workspace-surface",
)
self.assertEqual(
match["verification_requirement"]["exact_files"],
PLATFORM_CONNECTIONS_WORKSPACE_EXACT_FILES,
)
def test_lookup_paths_reports_agent_install_backend_as_shared_boundary(self) -> None:
result = lookup_paths(["internal/api/agent_install_command_shared.go"])
self.assertEqual(result["unowned_runtime_files"], [])
@ -4713,63 +4044,6 @@ class SubsystemLookupTest(unittest.TestCase):
],
)
def test_lookup_paths_assigns_discovery_tab_to_unified_resources(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Discovery/DiscoveryTab.tsx"])
self.assertEqual(result["unowned_runtime_files"], [])
file_entry = result["files"][0]
self.assertEqual(file_entry["classification"], "runtime")
self.assertEqual(len(file_entry["matches"]), 1)
match = file_entry["matches"][0]
self.assertEqual(match["subsystem"], "unified-resources")
self.assertEqual(
match["contract"],
"docs/release-control/v6/internal/subsystems/unified-resources.md",
)
self.assertEqual(match["lane_context"]["lane_id"], "L13")
self.assertEqual(match["verification_requirement"]["id"], "resource-consumers")
self.assertEqual(
match["verification_requirement"]["exact_files"],
[
"frontend-modern/src/components/Infrastructure/__tests__/ResourceChangeSummary.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/ResourceCorrelationSummary.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.discovery.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/ResourcePolicySummary.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.workloads-link.test.tsx",
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSelectors.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/infrastructureSummaryModel.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/resourceDetailDrawerIdentityModel.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/resourceDetailDrawerOperationalModel.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/resourceDetailMappers.test.ts",
"frontend-modern/src/components/Infrastructure/__tests__/unifiedResourceTableStateModel.test.ts",
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts",
"frontend-modern/src/features/docker/__tests__/DockerNativeTables.test.tsx",
"frontend-modern/src/features/docker/__tests__/DockerPageSurface.test.tsx",
"frontend-modern/src/features/docker/__tests__/dockerPageModel.test.ts",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesAutoscalingTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesClustersTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesConfigTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesControllersTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesEventsTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesNetworkingTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesPageSurface.contract.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesPodsTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesPolicyTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesServicesTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/KubernetesStorageTable.test.tsx",
"frontend-modern/src/features/kubernetes/__tests__/kubernetesPageModel.test.ts",
"frontend-modern/src/hooks/__tests__/useUnifiedResources.test.ts",
"frontend-modern/src/routing/__tests__/resourceLinks.test.ts",
"frontend-modern/src/stores/__tests__/websocket-unified.test.ts",
"frontend-modern/src/types/__tests__/resource.test.ts",
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
"internal/unifiedresources/code_standards_test.go",
],
)
def test_lookup_paths_assigns_discovery_tab_state_owner_to_unified_resources(self) -> None:
result = lookup_paths(["frontend-modern/src/components/Discovery/useDiscoveryTabState.ts"])
self.assertEqual(result["unowned_runtime_files"], [])