{ "version": 1, "rules": [ { "id": "runtime-web-interface-name-link", "category": "resource-launch", "summary": "Runtime resource rows must launch saved or inferred web-interface URLs through the shared name-link template, not a page-local link or separate web column.", "canonical": { "path": "src/components/shared/WebInterfaceNameLink.tsx", "export": "WebInterfaceNameLink" }, "requiredConsumers": [ { "path": "src/components/shared/NodeGroupHeader.tsx" }, { "path": "src/components/Workloads/GuestRow.tsx" }, { "path": "src/features/standalone/AgentsMachinesTable.tsx" }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx" }, { "path": "src/components/Alerts/AlertResourceGroupHeader.tsx" }, { "path": "src/components/Alerts/AlertResourceTableRow.tsx" } ], "forbiddenPatterns": [ { "path": "src/components/Workloads/GuestRow.tsx", "patterns": ["target=\"_blank\"", "rel=\"noopener noreferrer\""] }, { "path": "src/components/shared/NodeGroupHeader.tsx", "patterns": ["target=\"_blank\"", "rel=\"noopener noreferrer\""] }, { "path": "src/features/standalone/AgentsMachinesTable.tsx", "patterns": [ "target=\"_blank\"", "rel=\"noopener noreferrer\"", "AgentMachineWebLinkCell", "data-agent-machine-web-link" ] }, { "path": "src/features/standalone/agentMachineTableModel.ts", "patterns": ["id: 'web'", "label: 'Web'"] }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx", "patterns": [ "target=\"_blank\"", "rel=\"noopener noreferrer\"", "data-proxmox-host-web-link" ] }, { "path": "src/features/proxmox/proxmoxHostTableModel.ts", "patterns": ["id: 'web'", "label: 'Web'"] }, { "path": "src/components/Alerts/AlertResourceGroupHeader.tsx", "patterns": ["target=\"_blank\"", "rel=\"noopener noreferrer\""] }, { "path": "src/components/Alerts/AlertResourceTableRow.tsx", "patterns": ["target=\"_blank\"", "rel=\"noopener noreferrer\""] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/WebInterfaceNameLink.test.tsx", "src/components/shared/__tests__/NodeGroupHeader.test.tsx", "src/features/standalone/__tests__/AgentsMachinesTable.test.tsx", "src/components/Workloads/__tests__/GuestRow.test.tsx", "src/features/proxmox/__tests__/ProxmoxNodesTable.test.tsx", "src/features/proxmox/__tests__/proxmoxHostTableModel.test.ts", "src/components/Alerts/ResourceTable.test.tsx" ] }, { "id": "inline-detail-table-row-shell", "category": "row-detail", "summary": "Platform, workload, and infrastructure inline detail rows must compose the shared row/cell/content shell instead of recreating page-local surface-alt drawer rows.", "canonical": { "path": "src/components/shared/InlineDetailTableRow.tsx", "export": "InlineDetailTableRow" }, "requiredConsumers": [ { "path": "src/components/Infrastructure/UnifiedResourceHostTableCard.tsx" }, { "path": "src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx" }, { "path": "src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx" }, { "path": "src/components/Workloads/WorkloadPanel.tsx" }, { "path": "src/features/docker/DockerAlertsTable.tsx" }, { "path": "src/features/docker/DockerHostsTable.tsx" }, { "path": "src/features/docker/DockerNetworksTable.tsx" }, { "path": "src/features/kubernetes/KubernetesAlertsTable.tsx" }, { "path": "src/features/platformPage/PlatformResourceDetailTableRow.tsx" }, { "path": "src/features/proxmox/ProxmoxCephTable.tsx" }, { "path": "src/features/proxmox/ProxmoxCoverageTable.tsx" }, { "path": "src/features/proxmox/ProxmoxMailGatewayTable.tsx" }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx" }, { "path": "src/features/truenas/TrueNASAlertsTable.tsx" }, { "path": "src/features/truenas/TrueNASProtectionTable.tsx" }, { "path": "src/features/truenas/TrueNASServicesTable.tsx" }, { "path": "src/features/vmware/VsphereActivityTable.tsx" }, { "path": "src/features/vmware/VsphereAlertsTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/components/Workloads/WorkloadPanel.tsx", "patterns": [ "p-0 border-b border-border bg-surface-alt", "border-b border-border bg-surface-alt p-0" ] }, { "path": "src/features/platformPage/PlatformResourceDetailTableRow.tsx", "patterns": ["border-b border-border bg-surface-alt p-0"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/__tests__/InlineDetailTableRow.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "toast-notification-shell", "category": "notification", "summary": "Toast notification status icons and dismiss actions must compose library icons and ActionIconButton so every page inherits shared notification chrome instead of raw SVG glyphs or local close-button classes.", "canonical": { "path": "src/components/Toast/Toast.tsx", "export": "Toast" }, "requiredConsumers": [{ "path": "src/components/Toast/Toast.tsx" }], "forbiddenPatterns": [ { "path": "src/components/Toast/Toast.tsx", "patterns": [ "= 100 ? 0 : value >= 10 ? 1 : 2)"] }, { "path": "src/features/kubernetes/KubernetesStorageTable.tsx", "patterns": ["formatBytes("] }, { "path": "src/features/proxmox/ProxmoxBackupServersTable.tsx", "patterns": ["formatBytes("] }, { "path": "src/features/proxmox/ProxmoxCephTable.tsx", "patterns": ["formatBytes("] }, { "path": "src/features/proxmox/ProxmoxCoverageTable.tsx", "patterns": ["formatBytes("] }, { "path": "src/features/proxmox/ProxmoxRecoverableTable.tsx", "patterns": ["formatBytes("] }, { "path": "src/features/truenas/TrueNASProtectionTable.tsx", "patterns": ["formatBytes(point.sizeBytes)"] }, { "path": "src/features/truenas/TrueNASStorageTopologyTable.tsx", "patterns": [ "formatBytes(size)", "formatBytes(row.resource.disk.used)", "formatBytes(row.resource.disk.total)" ] }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx", "patterns": ["const formatBytes", "value.toFixed(value >= 100 ? 0 : value >= 10 ? 1 : 2)"] }, { "path": "src/features/truenas/TrueNASVirtualMachinesTable.tsx", "patterns": ["const formatBytes", "value.toFixed(value >= 100 ? 0 : value >= 10 ? 1 : 2)"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-date-time-value", "category": "platform-table-text-value", "summary": "Platform tables must use the shared compact date-time value primitive for timestamp cells, tabular styling, and canonical empty-cell markers instead of recreating local compact toLocaleString helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableDateTimeValue" }, "requiredConsumers": [ { "path": "src/features/truenas/TrueNASProtectionTable.tsx" }, { "path": "src/features/vmware/VsphereActivityTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/truenas/TrueNASProtectionTable.tsx", "patterns": ["const formatPointTime", "formatPointTime(point)"] }, { "path": "src/features/vmware/VsphereActivityTable.tsx", "patterns": [ "const formatActivityDate", "formatActivityDate(activity.occurredAt || activity.observedAt)" ] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-relative-time-value", "category": "platform-table-text-value", "summary": "Platform tables must use the shared relative-time value primitive for dense timestamp-age cells, tabular styling, compact labels, and canonical empty-cell markers instead of calling formatRelativeTime directly.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableRelativeTimeValue" }, "requiredConsumers": [ { "path": "src/features/docker/DockerVolumesTable.tsx" }, { "path": "src/features/kubernetes/KubernetesDeploymentsTable.tsx" }, { "path": "src/features/kubernetes/KubernetesEventsTable.tsx" }, { "path": "src/features/proxmox/proxmoxBackupsTableShared.tsx" }, { "path": "src/features/proxmox/ProxmoxReplicationTable.tsx" }, { "path": "src/features/standalone/AvailabilityChecksTable.tsx" }, { "path": "src/features/standalone/AgentsMachinesTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/docker/DockerVolumesTable.tsx", "patterns": ["formatRelativeTime(parsed)"] }, { "path": "src/features/kubernetes/KubernetesDeploymentsTable.tsx", "patterns": ["formatRelativeTime(createdAt, { compact: true, emptyText: '—' })"] }, { "path": "src/features/kubernetes/KubernetesEventsTable.tsx", "patterns": [ "formatRelativeTime(observedTimestamp(resource), { compact: true, emptyText: '—' })" ] }, { "path": "src/features/proxmox/proxmoxBackupsTableShared.tsx", "patterns": ["formatRelativeTime(props.artifact.createdAt, { compact: true })"] }, { "path": "src/features/proxmox/ProxmoxReplicationTable.tsx", "patterns": [ "formatRelativeTime(job.lastSyncUnix * 1000, { compact: true })", "formatRelativeTime(job.lastSyncTime, { compact: true })" ] }, { "path": "src/features/standalone/AgentsMachinesTable.tsx", "patterns": ["const formatLastSeen", "Math.floor((Date.now() - timestampMillis) / 1000)"] }, { "path": "src/features/standalone/AvailabilityChecksTable.tsx", "patterns": ["formatRelativeTime(availability?.lastChecked", "const formatChecked"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-duration-value", "category": "platform-table-text-value", "summary": "Platform tables must use the shared duration value primitive for elapsed duration and polling interval cells, tabular styling, fallback labels, and canonical empty-cell markers instead of declaring local seconds/minutes helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableDurationValue" }, "requiredConsumers": [ { "path": "src/features/proxmox/ProxmoxReplicationTable.tsx" }, { "path": "src/features/standalone/AvailabilityChecksTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/proxmox/ProxmoxReplicationTable.tsx", "patterns": ["function formatDuration", "seconds < 3600"] }, { "path": "src/features/standalone/AvailabilityChecksTable.tsx", "patterns": ["const formatInterval", "toFixed(1)}m"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-weighted-column-width-style", "category": "platform-table-layout", "summary": "Responsive platform table models must use the shared weighted column-width helper for visible-column percentage normalization instead of declaring local percentage-format helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "getPlatformTableWeightedColumnWidthStyle" }, "requiredConsumers": [ { "path": "src/features/docker/dockerContainerTableModel.ts" }, { "path": "src/features/proxmox/proxmoxHostTableModel.ts" } ], "forbiddenPatterns": [ { "path": "src/features/docker/dockerContainerTableModel.ts", "patterns": ["const formatPercentage", "value.toFixed(4)"] }, { "path": "src/features/proxmox/proxmoxHostTableModel.ts", "patterns": ["const formatPercentage", "value.toFixed(4)"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-integer-value-format", "category": "platform-table-text-value", "summary": "Platform tables and drawers must use the shared integer formatter with PlatformTableNumberValue for locale-formatted count cells instead of recreating local toLocaleString or Math.round helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableIntegerValue" }, "requiredConsumers": [ { "path": "src/components/Kubernetes/K8sNamespacesDrawer.tsx" }, { "path": "src/features/proxmox/ProxmoxBackupServersTable.tsx" }, { "path": "src/features/proxmox/ProxmoxCephClusterDrawer.tsx" }, { "path": "src/features/proxmox/ProxmoxMailGatewayDrawer.tsx" }, { "path": "src/features/proxmox/ProxmoxMailGatewayTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/components/Kubernetes/K8sNamespacesDrawer.tsx", "patterns": ["const formatInteger", "Math.round(n).toLocaleString()"] }, { "path": "src/features/proxmox/ProxmoxBackupServersTable.tsx", "patterns": ["row.backupCount.toLocaleString()"] }, { "path": "src/features/proxmox/ProxmoxCephClusterDrawer.tsx", "patterns": ["pool.objects.toLocaleString()"] }, { "path": "src/features/proxmox/ProxmoxMailGatewayDrawer.tsx", "patterns": ["function formatNumber", "Math.round(value).toLocaleString()"] }, { "path": "src/features/proxmox/ProxmoxMailGatewayTable.tsx", "patterns": ["const formatLocaleCount", "value.toLocaleString()"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-number-value-fallback", "category": "platform-table-text-value", "summary": "Platform tables must use the shared number-value fallback primitive for finite numeric table cells, tabular styling, and canonical empty-cell markers instead of recreating local numberValue/countCell helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableNumberValue" }, "requiredConsumers": [ { "path": "src/features/docker/DockerNativeTableShared.tsx" }, { "path": "src/features/docker/DockerServicesTable.tsx" }, { "path": "src/components/Docker/SwarmServicesDrawer.tsx" }, { "path": "src/features/kubernetes/KubernetesAutoscalingTable.tsx" }, { "path": "src/features/kubernetes/KubernetesControllersTable.tsx" }, { "path": "src/features/kubernetes/KubernetesDeploymentsTable.tsx" }, { "path": "src/features/kubernetes/KubernetesEventsTable.tsx" }, { "path": "src/features/kubernetes/KubernetesPodsTable.tsx" }, { "path": "src/features/proxmox/ProxmoxMailGatewayTable.tsx" }, { "path": "src/features/truenas/TrueNASStorageTopologyTable.tsx" }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/docker/DockerNativeTableShared.tsx", "patterns": [ "typeof value === 'number' ? {value} : —" ] }, { "path": "src/features/docker/DockerServicesTable.tsx", "patterns": ["const replicaCount", "{value ?? 0}"] }, { "path": "src/components/Docker/SwarmServicesDrawer.tsx", "patterns": [ "{desired()}", "{running()}" ] }, { "path": "src/features/kubernetes/KubernetesAutoscalingTable.tsx", "patterns": [ "const numberValue", "typeof value === 'number' ? {value} : —" ] }, { "path": "src/features/kubernetes/KubernetesControllersTable.tsx", "patterns": [ "const numberValue", "typeof value === 'number' ? {value} : —" ] }, { "path": "src/features/kubernetes/KubernetesDeploymentsTable.tsx", "patterns": ["const replicaCount", "{value ?? 0}"] }, { "path": "src/features/kubernetes/KubernetesEventsTable.tsx", "patterns": [ "const numberValue", "typeof value === 'number' ? {value} : —" ] }, { "path": "src/features/kubernetes/KubernetesPodsTable.tsx", "patterns": [ "const numericValue", "typeof value === 'number' ? {value} : —" ] }, { "path": "src/features/proxmox/ProxmoxMailGatewayTable.tsx", "patterns": ["const countCell", "value.toLocaleString() : '—'"] }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx", "patterns": ["hidden text-base-content tabular-nums lg:table-cell"] }, { "path": "src/features/truenas/TrueNASStorageTopologyTable.tsx", "patterns": ["const diskCountLabel"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-count-ratio-value", "category": "platform-table-text-value", "summary": "Platform tables must use the shared count-ratio primitive for healthy/total or ready/total count cells instead of recreating local numerator/slash/denominator markup.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableCountRatioValue" }, "requiredConsumers": [{ "path": "src/features/kubernetes/KubernetesClustersTable.tsx" }], "forbiddenPatterns": [ { "path": "src/features/kubernetes/KubernetesClustersTable.tsx", "patterns": ["const childCountCell", "/{count.total}"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-count-ratio-label", "category": "platform-table-text-value", "summary": "Platform tables must use the shared count-ratio formatter for ready/total string labels instead of recreating local ready/total template literals.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableCountRatioValue" }, "requiredConsumers": [{ "path": "src/features/kubernetes/KubernetesNetworkingTable.tsx" }], "forbiddenPatterns": [ { "path": "src/features/kubernetes/KubernetesNetworkingTable.tsx", "patterns": [ "`${readyValue}/${totalValue} ready`", "`${ready ?? 0}/${total ?? ready ?? 0} ready`" ] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-percent-value-fallback", "category": "platform-table-text-value", "summary": "Platform tables and drawers must use the shared percent-value primitive/helper for one-decimal percent cells, overlay labels, tabular styling, ratio normalization, clamping, and canonical empty-cell markers instead of recreating local formatPercent helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTablePercentValue" }, "requiredConsumers": [ { "path": "src/features/proxmox/ProxmoxBackupServersTable.tsx" }, { "path": "src/features/proxmox/ProxmoxCephClusterDrawer.tsx" }, { "path": "src/features/proxmox/ProxmoxCephTable.tsx" }, { "path": "src/features/proxmox/ProxmoxMailGatewayDrawer.tsx" }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx" }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/proxmox/ProxmoxBackupServersTable.tsx", "patterns": [ "Math.round(row.cpuPercent ?? 0)}%", "Math.round(row.memoryPercent ?? 0)}%", "Math.round(pct() ?? 0)}%" ] }, { "path": "src/features/proxmox/ProxmoxCephClusterDrawer.tsx", "patterns": ["clamped.toFixed(1)}%"] }, { "path": "src/features/proxmox/ProxmoxCephTable.tsx", "patterns": ["pct.toFixed(1)}%"] }, { "path": "src/features/proxmox/ProxmoxMailGatewayDrawer.tsx", "patterns": ["share.toFixed(1)}%"] }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx", "patterns": ["const formatPercentLabel", "`${Math.round(Math.max(0, normalized))}%`"] }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx", "patterns": [ "const formatPercent", "{percent.toFixed(1)}%" ] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-temperature-value-fallback", "category": "platform-table-text-value", "summary": "Platform tables must use the shared temperature-value primitive for positive Celsius cells, tabular styling, and canonical empty-cell markers instead of recreating local formatTemperature helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableTemperatureValue" }, "requiredConsumers": [ { "path": "src/features/docker/DockerHostsTable.tsx" }, { "path": "src/features/truenas/TrueNASStorageTopologyTable.tsx" }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/docker/DockerHostsTable.tsx", "patterns": [ "const formatTemperature", "{celsius.toFixed(1)}°C" ] }, { "path": "src/features/truenas/TrueNASStorageTopologyTable.tsx", "patterns": ["const temperatureLabel", "${Math.round(value)}C"] }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx", "patterns": [ "const formatTemperature", "{celsius.toFixed(1)}°C" ] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-metric-fallback", "category": "platform-table-metric", "summary": "Platform tables must use shared metric fallback and finite-metric helpers instead of recreating local centered dash fallbacks and Number.isFinite wrappers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableMetricFallback" }, "requiredConsumers": [ { "path": "src/features/docker/DockerContainersTable.tsx" }, { "path": "src/features/docker/DockerHostsTable.tsx" }, { "path": "src/features/kubernetes/KubernetesClustersTable.tsx" }, { "path": "src/features/kubernetes/KubernetesNodesTable.tsx" }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx" }, { "path": "src/features/standalone/AgentsMachinesTable.tsx" }, { "path": "src/features/truenas/TrueNASAppsTable.tsx" }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx" }, { "path": "src/features/vmware/VsphereDatastoresTable.tsx" }, { "path": "src/features/vmware/VsphereHostsTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/features/docker/DockerContainersTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/docker/DockerHostsTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/kubernetes/KubernetesClustersTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/kubernetes/KubernetesNodesTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/proxmox/ProxmoxNodesTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/standalone/AgentsMachinesTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/truenas/TrueNASAppsTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/truenas/TrueNASSystemsTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/vmware/VsphereDatastoresTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] }, { "path": "src/features/vmware/VsphereHostsTable.tsx", "patterns": ["const finiteMetric", "const metricFallback"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-finite-metric-normalization", "category": "platform-table-metric", "summary": "Platform table models must use the shared finite-metric normalization helper instead of carrying model-local Number.isFinite wrappers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "getPlatformTableFiniteMetric" }, "requiredConsumers": [{ "path": "src/features/standalone/agentMachineTableModel.ts" }], "forbiddenPatterns": [ { "path": "src/features/standalone/agentMachineTableModel.ts", "patterns": ["const finiteMetric"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-error-state", "category": "platform-error-state", "summary": "Platform page and table load failures must compose PlatformErrorState instead of recreating a local EmptyState plus Refresh action.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformErrorState" }, "requiredConsumers": [ { "path": "src/features/docker/DockerPageSurface.tsx" }, { "path": "src/features/kubernetes/KubernetesPageSurface.tsx" }, { "path": "src/features/proxmox/ProxmoxBackupsTable.tsx" }, { "path": "src/features/proxmox/ProxmoxPageSurface.tsx" }, { "path": "src/features/proxmox/ProxmoxReplicationTable.tsx" }, { "path": "src/features/standalone/StandalonePageSurface.tsx" }, { "path": "src/features/truenas/TrueNASPageSurface.tsx" }, { "path": "src/features/truenas/TrueNASProtectionTable.tsx" }, { "path": "src/features/vmware/VmwarePageSurface.tsx" } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/platformOverviewLayout.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-section-tabs", "category": "platform-section-tabs", "summary": "Platform page workflow tabs must compose PlatformSectionTabs instead of recreating page-local nav tab bars.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformSectionTabs" }, "requiredConsumers": [ { "path": "src/features/docker/DockerPageSurface.tsx" }, { "path": "src/features/kubernetes/KubernetesPageSurface.tsx" }, { "path": "src/features/proxmox/ProxmoxPageSurface.tsx" }, { "path": "src/features/standalone/StandalonePageSurface.tsx" }, { "path": "src/features/truenas/TrueNASPageSurface.tsx" }, { "path": "src/features/vmware/VmwarePageSurface.tsx" } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/platformOverviewLayout.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "filter-bar-catalog-shell", "category": "filter-bar", "summary": "Resource-list filter surfaces must compose the shared FilterBar catalog shell instead of recreating page-local filter rows or legacy PageControls decks.", "canonical": { "path": "src/components/shared/FilterBar/FilterBar.tsx", "export": "FilterBar" }, "requiredConsumers": [ { "path": "src/components/Alerts/ThresholdsTable.tsx" }, { "path": "src/components/Settings/AuditLogPanel.tsx" }, { "path": "src/components/Storage/StoragePageControls.tsx" }, { "path": "src/components/Workloads/WorkloadsFilter.tsx" }, { "path": "src/features/alerts/AlertHistoryFiltersCard.tsx" }, { "path": "src/features/platformPage/sharedPlatformPage.tsx" }, { "path": "src/features/proxmox/ProxmoxBackupsTable.tsx" } ], "forbiddenPatterns": [ { "path": "src/components/Storage/StoragePageControls.tsx", "patterns": [ "from '@/components/shared/PageControls'", " setTagFilter"] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/__tests__/SearchField.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "search-input-shell", "category": "search-control", "summary": "Resource-list and drawer search boxes that need history, tips, or type-to-search behavior must compose SearchInput instead of rebuilding search-enhancement controls locally.", "canonical": { "path": "src/components/shared/SearchInput.tsx", "export": "SearchInput" }, "requiredConsumers": [ { "path": "src/components/Docker/SwarmServicesDrawer.tsx" }, { "path": "src/components/Kubernetes/K8sDeploymentsDrawer.tsx" }, { "path": "src/components/Kubernetes/K8sNamespacesDrawer.tsx" }, { "path": "src/components/Settings/SettingsPageShell.tsx" }, { "path": "src/components/shared/FilterBar/FilterBar.tsx" } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/__tests__/SearchInput.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "filter-button-group-shell", "category": "segmented-selector", "summary": "Settings and compact feature segmented selectors must compose FilterButtonGroup instead of recreating active-button group styling locally.", "canonical": { "path": "src/components/shared/FilterButtonGroup.tsx", "export": "FilterButtonGroup" }, "requiredConsumers": [ { "path": "src/components/Settings/GeneralSettingsPanel.tsx" }, { "path": "src/components/Settings/ResourcePicker.tsx" }, { "path": "src/components/Settings/ReportingPanel.tsx" }, { "path": "src/features/patrol/PatrolIntelligenceHeader.tsx" } ], "forbiddenPatterns": [ { "path": "src/components/Settings/ResourcePicker.tsx", "patterns": [ "min-h-10 sm:min-h-9 min-w-10 px-3 py-2 rounded-md text-sm font-medium transition-all", "" ] }, { "path": "src/features/proxmox/ProxmoxBackupsTable.tsx", "patterns": [ "const viewButtonClass", "inline-flex items-center gap-1 rounded-md border border-border bg-surface p-1", "inline-flex min-h-8 items-center gap-1.5 rounded-sm px-3 text-xs font-medium transition-colors" ] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/FilterButtonGroup.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "selectable-pill-button-shell", "category": "selectable-pill-button", "summary": "Pressed/unpressed rounded pill selectors must compose SelectablePillButton so active tone, disabled treatment, focus ring, and aria-pressed semantics stay shared instead of being recreated inside settings surfaces.", "canonical": { "path": "src/components/shared/SelectablePillButton.tsx", "export": "SelectablePillButton" }, "requiredConsumers": [{ "path": "src/components/Settings/APITokenManager.tsx" }], "forbiddenPatterns": [ { "path": "src/components/Settings/APITokenManager.tsx", "patterns": [ "inline-flex min-h-10 sm:min-h-10 items-center rounded-full border px-3 py-2 text-sm font-semibold transition", "min-h-10 sm:min-h-10 rounded-full border px-3 py-2 text-sm font-semibold transition", "border-blue-500 bg-blue-600 text-white shadow-sm", "hover:border-blue-400 hover:text-blue-600 dark:hover:border-blue-400 dark:hover:text-blue-200" ] } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/SelectablePillButton.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "chart-visibility-toggle-button", "category": "display-toggle", "summary": "Summary-bearing filter surfaces must compose ChartVisibilityToggleButton for chart show/hide display actions instead of recreating one-off chart toggle buttons.", "canonical": { "path": "src/components/shared/FilterToolbar.tsx", "export": "ChartVisibilityToggleButton" }, "requiredConsumers": [{ "path": "src/components/Workloads/WorkloadsFilter.tsx" }], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/FilterToolbar.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "column-picker-shell", "category": "column-picker", "summary": "Table column visibility controls must compose ColumnPicker instead of recreating column chooser trigger, panel, reset, or hidden-count chrome locally.", "canonical": { "path": "src/components/shared/ColumnPicker.tsx", "export": "ColumnPicker" }, "requiredConsumers": [ { "path": "src/components/Workloads/WorkloadsFilter.tsx" }, { "path": "src/features/standalone/AgentsMachinesTable.tsx" } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/ColumnPicker.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "selection-card-group-shell", "category": "selection-card-group", "summary": "Selectable settings card groups must compose SelectionCardGroup instead of recreating compact/detail active-card styling and disabled selection behavior locally.", "canonical": { "path": "src/components/shared/SelectionCardGroup.tsx", "export": "SelectionCardGroup" }, "requiredConsumers": [ { "path": "src/components/Settings/AISettingsDialogs.tsx" }, { "path": "src/components/Settings/UpdatesSettingsPanel.tsx" } ], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/SelectionCardGroup.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "grouped-table-mode-segmented-control", "category": "table-mode-control", "summary": "Grouped/list table-mode controls must compose GroupedTableModeSegmentedControl instead of recreating grouped/list labels, titles, or icons locally.", "canonical": { "path": "src/components/shared/GroupedTableModeSegmentedControl.tsx", "export": "GroupedTableModeSegmentedControl" }, "requiredConsumers": [{ "path": "src/components/Workloads/WorkloadsFilter.tsx" }], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] } ], "patternGuards": [ { "id": "alert-resource-action-local-svg-button-shell", "category": "action-button", "summary": "Alert resource threshold action controls must not recreate icon-only buttons with inline SVGs locally; compose ActionIconButton and lucide icons for row, mobile, and global actions.", "canonical": { "path": "src/components/shared/Button.tsx", "export": "ActionIconButton" }, "scopes": ["src/components/Alerts"], "extensions": [".tsx"], "allPatterns": ["= 100 ? 0 : value >= 10 ? 1 : 2)"], "legacyReason": "Retired migration debt. Platform table byte-size cells must use formatPlatformTableBytesValue for shared positive-byte formatting and empty-cell markers.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-format-bytes-import", "category": "platform-table-text-value", "summary": "Platform feature tables must not import the generic byte formatter directly for dense table byte cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableBytesValue" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/proxmox", "src/features/truenas" ], "extensions": [".tsx"], "pathIncludes": ["Table"], "pathExcludes": ["__tests__"], "allPatterns": ["import { formatBytes } from '@/utils/format';", "formatBytes("], "legacyReason": "Retired migration debt. Dense platform table byte cells must use formatPlatformTableBytesValue so positive-byte formatting and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-format-uptime-import", "category": "platform-table-text-value", "summary": "Platform table files must not call the generic formatUptime utility directly for uptime cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableUptimeValue" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/proxmox", "src/features/standalone", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "pathIncludes": ["Table"], "pathExcludes": ["__tests__"], "allPatterns": ["formatUptime("], "legacyReason": "Retired migration debt. Dense platform table uptime cells must compose formatPlatformTableUptimeValue so compact/full labels and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-number-value-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local numberValue helpers for optional numeric table cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableNumberValue" }, "scopes": ["src/features/docker", "src/features/kubernetes"], "extensions": [".tsx"], "allPatterns": ["{value}", "—"], "legacyReason": "Retired migration debt. Optional numeric platform table cells must use PlatformTableNumberValue for finite-number checks, tabular styling, and empty-cell markers.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-count-cell-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local countCell helpers for locale-formatted optional numeric table cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableNumberValue" }, "scopes": ["src/features/proxmox"], "extensions": [".tsx"], "allPatterns": ["const countCell", "value.toLocaleString() : '—'"], "legacyReason": "Retired migration debt. Locale-formatted count cells must compose PlatformTableNumberValue with a caller-owned format function instead of owning fallback markup locally.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-integer-format-helper", "category": "platform-table-text-value", "summary": "Platform tables and drawers must not recreate local integer format helpers for count labels.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableIntegerValue" }, "scopes": ["src/components/Kubernetes", "src/features/proxmox"], "extensions": [".tsx"], "allPatterns": ["Math.round(", ".toLocaleString()"], "legacyReason": "Retired migration debt. Dense platform integer count labels must use formatPlatformTableIntegerValue so rounding, locale formatting, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-integer-count-tolocalestring", "category": "platform-table-text-value", "summary": "Platform tables and drawers must not call toLocaleString directly for count cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableIntegerValue" }, "scopes": ["src/components/Kubernetes", "src/features/proxmox"], "extensions": [".tsx"], "allPatterns": ["toLocaleString()"], "legacyReason": "Retired migration debt. Dense platform integer count cells must use formatPlatformTableIntegerValue and PlatformTableNumberValue instead of local count formatting.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-replica-count-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local replicaCount helpers for scheduler/service count columns.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableNumberValue" }, "scopes": ["src/features/docker", "src/features/kubernetes"], "extensions": [".tsx"], "allPatterns": ["const replicaCount", "{value ?? 0}"], "legacyReason": "Retired migration debt. Scheduler and service count cells must compose PlatformTableNumberValue with caller-owned zero-default semantics instead of local tabular span helpers.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-swarm-count-spans", "category": "platform-table-text-value", "summary": "Docker Swarm table drawers must not render desired/running service counts with local tabular spans.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableNumberValue" }, "scopes": ["src/components/Docker"], "extensions": [".tsx"], "allPatterns": ["desiredTasks", "runningTasks", "class=\"tabular-nums\""], "legacyReason": "Retired migration debt. Swarm desired/running count cells must route through PlatformTableNumberValue so drawer tables and platform tables share numeric cell rendering.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-count-ratio-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local healthy/total count-ratio helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableCountRatioValue" }, "scopes": ["src/features/kubernetes"], "extensions": [".tsx"], "allPatterns": ["const childCountCell", "/{count.total}"], "legacyReason": "Retired migration debt. Healthy/total and ready/total table count cells must compose PlatformTableCountRatioValue so ratio markup, tabular styling, muted denominator styling, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-ready-count-ratio-label", "category": "platform-table-text-value", "summary": "Kubernetes networking tables must not recreate ready/total endpoint count labels with local template strings.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTableCountRatioValue" }, "scopes": ["src/features/kubernetes/KubernetesNetworkingTable.tsx"], "extensions": [".tsx"], "allPatterns": ["readyEndpointCount", "/${", " ready`"], "legacyReason": "Retired migration debt. Ready/total endpoint labels must compose formatPlatformTableCountRatioValue so zero-default, suffix, and empty-label behavior stay shared with count-ratio cells.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-truenas-date-time-helper", "category": "platform-table-text-value", "summary": "TrueNAS protection tables must not recreate compact completed-time helpers for timestamp cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableDateTimeValue" }, "scopes": ["src/features/truenas/TrueNASProtectionTable.tsx"], "extensions": [".tsx"], "allPatterns": ["const formatPointTime", "toLocaleString(undefined, {", "minute: '2-digit'"], "legacyReason": "Retired migration debt. Compact platform timestamp cells must compose PlatformTableDateTimeValue so date parsing, empty markers, and tabular styling stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-vsphere-activity-date-helper", "category": "platform-table-text-value", "summary": "vSphere activity tables must not recreate compact activity-date helpers for timestamp cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableDateTimeValue" }, "scopes": ["src/features/vmware/VsphereActivityTable.tsx"], "extensions": [".tsx"], "allPatterns": [ "const formatActivityDate", "toLocaleString(undefined, {", "minute: '2-digit'" ], "legacyReason": "Retired migration debt. Compact platform timestamp cells must compose PlatformTableDateTimeValue so date parsing, empty markers, minimum-year handling, and tabular styling stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-relative-time-helper", "category": "platform-table-text-value", "summary": "Platform tables must not call formatRelativeTime directly for dense timestamp-age cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableRelativeTimeValue" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/proxmox", "src/features/standalone" ], "extensions": [".tsx"], "pathIncludes": ["Table"], "pathExcludes": ["__tests__"], "allPatterns": ["formatRelativeTime("], "legacyReason": "Retired migration debt. Platform table relative timestamp cells must compose PlatformTableRelativeTimeValue so compact labels, empty markers, and tabular styling stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-duration-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local elapsed-duration helpers for seconds/minutes duration cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableDurationValue" }, "scopes": ["src/features/proxmox/ProxmoxReplicationTable.tsx"], "extensions": [".tsx"], "allPatterns": ["function formatDuration", "seconds < 3600", "return `${h}h ${m}m`"], "legacyReason": "Retired migration debt. Platform duration cells must compose PlatformTableDurationValue so elapsed-duration labels, fallback text, empty markers, and tabular styling stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-interval-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local interval helpers for polling interval cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableDurationValue" }, "scopes": ["src/features/standalone/AvailabilityChecksTable.tsx"], "extensions": [".tsx"], "allPatterns": ["const formatInterval", "pollIntervalSeconds", "toFixed(1)}m"], "legacyReason": "Retired migration debt. Platform interval cells must compose PlatformTableDurationValue so seconds/minutes labels, empty markers, and tabular styling stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-weighted-width-helper", "category": "platform-table-layout", "summary": "Responsive platform table models must not recreate local weighted-width percentage helpers.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "getPlatformTableWeightedColumnWidthStyle" }, "scopes": [ "src/features/docker/dockerContainerTableModel.ts", "src/features/proxmox/proxmoxHostTableModel.ts" ], "extensions": [".ts"], "allPatterns": [ "const formatPercentage", "value.toFixed(4)", "return { width: formatPercentage(width) }" ], "legacyReason": "Retired migration debt. Responsive platform table width normalization must use getPlatformTableWeightedColumnWidthStyle so percentage rounding and zero-width fallback stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-truenas-count-cell-class", "category": "platform-table-text-value", "summary": "TrueNAS table scalar value cells must not put tabular-number styling on table cells instead of shared value primitives.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableNumberValue" }, "scopes": ["src/features/truenas"], "extensions": [".tsx"], "allPatterns": ["TrueNAS", "hidden text-base-content tabular-nums lg:table-cell"], "legacyReason": "Retired migration debt. TrueNAS share/service and adjacent scalar value cells must render through shared platform value primitives so finite-number checks, tabular styling, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-percent-value-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local formatPercent helpers for one-decimal percent cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTablePercentValue" }, "scopes": ["src/features/truenas"], "extensions": [".tsx"], "allPatterns": [ "const formatPercent", "{percent.toFixed(1)}%" ], "legacyReason": "Retired migration debt. One-decimal percent cells must compose PlatformTablePercentValue so percent formatting, tabular styling, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-proxmox-percent-tofixed-label", "category": "platform-table-text-value", "summary": "Proxmox platform tables and drawers must not recreate one-decimal percent labels with toFixed.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTablePercentValue" }, "scopes": ["src/features/proxmox"], "extensions": [".tsx"], "allPatterns": ["toFixed(1)}%"], "legacyReason": "Retired migration debt. Proxmox platform percent labels must compose formatPlatformTablePercentValue or PlatformTablePercentValue so formatting stays shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-proxmox-percent-label-helper", "category": "platform-table-text-value", "summary": "Proxmox platform tables must not recreate local percent label helpers for metric cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "formatPlatformTablePercentValue" }, "scopes": ["src/features/proxmox"], "extensions": [".tsx"], "allPatterns": ["formatPercentLabel"], "legacyReason": "Retired migration debt. Proxmox platform percent metric labels must compose formatPlatformTablePercentValue so ratio normalization, clamping, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-temperature-value-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local formatTemperature helpers for positive Celsius cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableTemperatureValue" }, "scopes": ["src/features/docker", "src/features/truenas"], "extensions": [".tsx"], "allPatterns": [ "const formatTemperature", "{celsius.toFixed(1)}°C" ], "legacyReason": "Retired migration debt. Positive Celsius table cells must compose PlatformTableTemperatureValue so validity, formatting, tabular styling, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-temperature-label-helper", "category": "platform-table-text-value", "summary": "Platform tables must not recreate local temperatureLabel helpers for positive Celsius cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableTemperatureValue" }, "scopes": ["src/features/truenas"], "extensions": [".tsx"], "allPatterns": ["const temperatureLabel", "${Math.round(value)}C"], "legacyReason": "Retired migration debt. Positive Celsius table cells must compose PlatformTableTemperatureValue so validity, formatting, tabular styling, and empty markers stay shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-metric-fallback-helper", "category": "platform-table-metric", "summary": "Platform tables must not recreate local finiteMetric and metricFallback helpers for metric cells.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableMetricFallback" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/proxmox", "src/features/standalone", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["const finiteMetric", "const metricFallback", "Number.isFinite(value)"], "legacyReason": "Retired migration debt. Platform table metric cells must use PlatformTableMetricFallback and getPlatformTableFiniteMetric instead of carrying page-local metric fallback helpers.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-local-metric-empty-marker", "category": "platform-table-metric", "summary": "Platform metric cells must not recreate centered muted dash fallback markup inline.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformTableMetricFallback" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/proxmox", "src/features/standalone", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["flex justify-center", "text-xs text-muted", "aria-hidden=\"true\""], "legacyReason": "Retired migration debt. Platform metric cells must compose PlatformTableMetricFallback instead of carrying inline centered dash fallback markup.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/platformPage/__tests__/sharedPlatformPage.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-table-model-local-finite-metric-helper", "category": "platform-table-metric", "summary": "Platform table models must not recreate local finiteMetric helpers for metric normalization.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "getPlatformTableFiniteMetric" }, "scopes": ["src/features/standalone/agentMachineTableModel.ts"], "extensions": [".ts"], "allPatterns": ["const finiteMetric"], "legacyReason": "Retired migration debt. Platform table model-side metric normalization must use getPlatformTableFiniteMetric so table presentation validity remains shared.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-section-tabs-local-nav", "category": "platform-section-tabs", "summary": "Platform page surfaces must not recreate the active-link tab navigation owned by PlatformSectionTabs.", "canonical": { "path": "src/features/platformPage/sharedPlatformPage.tsx", "export": "PlatformSectionTabs" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/proxmox", "src/features/standalone", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["aria-current={", "border-b-2", "border-b border-border"], "legacyReason": "Retired migration debt. Platform page section tabs must use PlatformSectionTabs; no page-local tab-bar exceptions are currently allowed.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "chart-visibility-local-toggle-labels", "category": "display-toggle", "summary": "Summary-bearing surfaces must not copy the Show charts / Hide charts display-toggle labels owned by ChartVisibilityToggleButton.", "canonical": { "path": "src/components/shared/FilterToolbar.tsx", "export": "ChartVisibilityToggleButton" }, "scopes": ["src/components/Workloads", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": ["Show charts", "Hide charts"], "legacyReason": "Retired migration debt. Chart visibility display actions must use ChartVisibilityToggleButton; local show/hide chart toggle copies are not allowed.", "allowedPaths": [], "ignoredPaths": ["src/components/Workloads/__tests__/WorkloadsFilter.test.tsx"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "column-picker-local-column-chooser", "category": "column-picker", "summary": "Table surfaces must not copy the column chooser title and panel copy owned by ColumnPicker.", "canonical": { "path": "src/components/shared/ColumnPicker.tsx", "export": "ColumnPicker" }, "scopes": ["src/components", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": ["Choose which columns to display", "Show Columns"], "legacyReason": "Retired migration debt. Table column visibility controls must use ColumnPicker; local column chooser copies are not allowed.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/ColumnPicker.test.tsx"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "selection-card-group-local-active-card-styles", "category": "selection-card-group", "summary": "Settings and feature surfaces must not copy the compact/detail active-card styling owned by SelectionCardGroup.", "canonical": { "path": "src/components/shared/selectionCardGroupModel.ts", "export": "getSelectionCardButtonClass" }, "scopes": ["src/components/Settings", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": [ "p-4 rounded-md border-2 transition-all text-left", "p-3 rounded-md border-2 transition-all text-center" ], "legacyReason": "Retired migration debt. Selectable settings card groups must use SelectionCardGroup; local compact/detail active-card styling forks are not allowed.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "grouped-table-mode-local-segmented-control", "category": "table-mode-control", "summary": "Resource surfaces must not copy the grouped/list table-mode segmented-control labels owned by GroupedTableModeSegmentedControl.", "canonical": { "path": "src/components/shared/GroupedTableModeSegmentedControl.tsx", "export": "GroupedTableModeSegmentedControl" }, "scopes": ["src/components", "src/features"], "extensions": [".tsx"], "allPatterns": ["Grouped table view", "Flat list view"], "legacyReason": "Retired migration debt. Grouped/list table-mode controls must use GroupedTableModeSegmentedControl; no local label/title copies are currently allowed.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "filter-bar-consumer-legacy-page-controls", "category": "filter-bar", "summary": "Page and feature filter surfaces must not import the legacy PageControls deck when they should be expressed as a FilterBar catalog.", "canonical": { "path": "src/components/shared/FilterBar/FilterBar.tsx", "export": "FilterBar" }, "scopes": ["src/components", "src/features"], "extensions": [".tsx"], "allPatterns": ["@/components/shared/PageControls"], "legacyReason": "Retired migration debt. Resource-list filter surfaces must compose FilterBar; no page-level filter exceptions are currently allowed.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "search-control-native-search-input", "category": "search-control", "summary": "Product surfaces must not render native type=search inputs; they must compose SearchField or SearchInput so search chrome, clear behavior, keyboard handling, and aria labels stay canonical.", "canonical": { "path": "src/components/shared/SearchField.tsx", "export": "SearchField" }, "scopes": ["src/components", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": ["type=\"search\""], "legacyReason": "Retired migration debt. Search controls must use SearchField or SearchInput; native search inputs are not allowed in product surfaces.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "status-dot-local-linked-disk-health-dot", "category": "status-indicator", "summary": "Storage linked-disk health rows must not recreate the retired green/yellow raw dot classes owned by StatusDot.", "canonical": { "path": "src/components/shared/StatusDot.tsx", "export": "StatusDot" }, "scopes": ["src/components/Storage", "src/features/storageBackups"], "extensions": [".ts", ".tsx"], "allPatterns": ["h-2 w-2 rounded-full bg-yellow-500", "h-2 w-2 rounded-full bg-green-500"], "legacyReason": "Retired migration debt. Linked-disk health indicators must use StatusDot variants instead of local green/yellow dot classes.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "status-indicator-badge-local-tone-helper", "category": "status-indicator", "summary": "Product components must compose StatusIndicatorBadge instead of calling getStatusIndicatorBadgeToneClasses directly for read-only health/state badges.", "canonical": { "path": "src/components/shared/StatusIndicatorBadge.tsx", "export": "StatusIndicatorBadge" }, "scopes": ["src/components", "src/features"], "extensions": [".tsx"], "allPatterns": ["getStatusIndicatorBadgeToneClasses("], "legacyReason": "Retired migration debt. Read-only status badges must route through StatusIndicatorBadge; status utilities may still expose tone classes for the shared primitive and non-component tests.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/StatusIndicatorBadge.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-severity-local-helper", "category": "status-indicator", "summary": "Platform alert tables must not recreate severityVariant/severityTextClass helpers; alert severity tone and label presentation belongs to AlertSeverityBadge and alertSeverityPresentation.", "canonical": { "path": "src/components/shared/AlertSeverityBadge.tsx", "export": "AlertSeverityBadge" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["severityVariant", "severityTextClass"], "legacyReason": "Retired migration debt. Platform alert rows must compose AlertSeverityBadge and AlertSeverityDot instead of page-local severity tone helpers.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/AlertSeverityBadge.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-severity-local-detail-tone-helper", "category": "detail-tone", "summary": "Platform alert tables must not recreate alertTone severity detail-row helpers; detail tone mapping belongs to alertSeverityPresentation.", "canonical": { "path": "src/utils/alertSeverityPresentation.ts", "export": "getAlertSeverityDetailTone" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["const alertTone", "severityBucket"], "legacyReason": "Retired migration debt. Platform alert detail rows must call getAlertSeverityDetailTone instead of carrying page-local severity tone helpers.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/utils/__tests__/alertSeverityPresentation.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-local-code-formatter-helper", "category": "detail-formatting", "summary": "Platform alert tables must not recreate provider-code formatter helpers; alert code labels belong to alertDetailPresentation.", "canonical": { "path": "src/utils/alertDetailPresentation.ts", "export": "formatPlatformAlertCode" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["const formatCode", "IncidentRow"], "legacyReason": "Retired migration debt. Platform alert code labels must call formatPlatformAlertCode instead of carrying page-local prefix stripping and title casing.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/utils/__tests__/alertDetailPresentation.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-local-resource-type-formatter-helper", "category": "detail-formatting", "summary": "Platform alert tables must not recreate resource-type formatter helpers; provider-specific resource labels belong to alertDetailPresentation.", "canonical": { "path": "src/utils/alertDetailPresentation.ts", "export": "formatPlatformAlertResourceType" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["const formatResourceType", "IncidentRow"], "legacyReason": "Retired migration debt. Platform alert resource-type labels must call formatPlatformAlertResourceType instead of carrying page-local switch maps.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/utils/__tests__/alertDetailPresentation.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-local-started-at-formatter-helper", "category": "detail-formatting", "summary": "Platform alert tables must not recreate started-at timestamp formatter helpers; alert timestamp labels belong to alertDetailPresentation.", "canonical": { "path": "src/utils/alertDetailPresentation.ts", "export": "formatPlatformAlertStartedAt" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["const formatStartedAt", "IncidentRow"], "legacyReason": "Retired migration debt. Platform alert row timestamps must call formatPlatformAlertStartedAt instead of carrying page-local date formatting.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/utils/__tests__/alertDetailPresentation.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-local-detail-date-formatter-helper", "category": "detail-formatting", "summary": "Platform alert tables must not recreate detail timestamp formatter helpers; alert detail date labels belong to alertDetailPresentation.", "canonical": { "path": "src/utils/alertDetailPresentation.ts", "export": "formatPlatformAlertDetailDateTime" }, "scopes": [ "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": ["const detailDateTime", "IncidentRow"], "legacyReason": "Retired migration debt. Platform alert detail timestamps must call formatPlatformAlertDetailDateTime instead of carrying page-local date formatting.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/utils/__tests__/alertDetailPresentation.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "platform-alert-local-entity-type-formatter-helper", "category": "detail-formatting", "summary": "vSphere alert tables must not recreate entity-type formatter helpers; vSphere alert entity labels belong to alertDetailPresentation.", "canonical": { "path": "src/utils/alertDetailPresentation.ts", "export": "formatPlatformAlertEntityType" }, "scopes": ["src/features/vmware"], "extensions": [".tsx"], "allPatterns": ["const formatEntityType", "VmwareIncidentRow"], "legacyReason": "Retired migration debt. vSphere alert entity labels must call formatPlatformAlertEntityType instead of carrying page-local normalization.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/utils/__tests__/alertDetailPresentation.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "patrol-finding-metadata-badge-local-shell", "category": "metadata-badge", "summary": "Patrol finding-row metadata chips must compose MetadataBadge instead of recreating local bordered xs badge spans.", "canonical": { "path": "src/components/shared/MetadataBadge.tsx", "export": "MetadataBadge" }, "scopes": ["src/components/AI"], "extensions": [".tsx"], "allPatterns": ["px-1.5 py-0.5 border text-[10px] font-medium rounded"], "legacyReason": "Retired migration debt. Patrol finding row state, source, severity, investigation, confidence, and regression chips use MetadataBadge for shared shell, sizing, tone, and outline behavior.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/MetadataBadge.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "patrol-investigation-metadata-badge-local-shell", "category": "metadata-badge", "summary": "Patrol investigation metadata chips must compose MetadataBadge instead of recreating local bordered xs status, outcome, record, or tool badges.", "canonical": { "path": "src/components/shared/MetadataBadge.tsx", "export": "MetadataBadge" }, "scopes": ["src/components/patrol"], "extensions": [".tsx"], "allPatterns": ["px-1.5 py-0.5 border text-[10px] font-medium rounded"], "legacyReason": "Retired migration debt. Patrol investigation status, outcome, durable-record, and tool chips use MetadataBadge for shared shell, sizing, tone, and outline behavior.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/patrol/__tests__/InvestigationSection.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "patrol-approval-tool-call-metadata-badge-local-shell", "category": "metadata-badge", "summary": "Patrol approval and tool-call metadata chips must compose MetadataBadge instead of recreating local xs risk, expiry, or result badges.", "canonical": { "path": "src/components/shared/MetadataBadge.tsx", "export": "MetadataBadge" }, "scopes": ["src/components/patrol"], "extensions": [".tsx"], "allPatterns": ["px-1.5 py-0.5", "text-[10px] font-medium rounded"], "legacyReason": "Retired migration debt. Patrol approval risk, approval state, and tool-call result chips use MetadataBadge for shared shell, sizing, tone, and whitespace behavior.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/patrol/__tests__/ApprovalSection.test.tsx", "src/components/patrol/__tests__/RunToolCallTrace.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "patrol-run-metadata-badge-local-shell", "category": "metadata-badge", "summary": "Patrol run-history metadata chips must compose MetadataBadge instead of recreating local rounded resource or outcome pill spans.", "canonical": { "path": "src/components/shared/MetadataBadge.tsx", "export": "MetadataBadge" }, "scopes": ["src/components/patrol"], "extensions": [".tsx"], "allPatterns": ["inline-flex items-center gap-1", "rounded-full text-xs font-medium"], "legacyReason": "Retired migration debt. Patrol run-history resource and outcome chips use MetadataBadge for shared shell, sizing, tone, and whitespace behavior.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/MetadataBadge.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "proxmox-backup-metadata-badge-local-shell", "category": "metadata-badge", "summary": "Proxmox backup source and state metadata chips must compose MetadataBadge instead of recreating local rounded-sm xs badge spans.", "canonical": { "path": "src/components/shared/MetadataBadge.tsx", "export": "MetadataBadge" }, "scopes": ["src/features/proxmox"], "extensions": [".tsx"], "allPatterns": ["rounded-sm px-1.5 py-0.5 text-[10px] font-semibold"], "legacyReason": "Retired migration debt. Proxmox backup source/state chips use MetadataBadge for shared shell, sizing, tone vocabulary, and whitespace behavior.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/features/proxmox/__tests__/proxmoxBackupsTableShared.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "organization-role-badge-local-shell", "category": "metadata-badge", "summary": "Organization settings sections must not call roleBadgeClass directly; compose OrganizationRoleBadge so role tone mapping and badge shell stay canonical.", "canonical": { "path": "src/components/shared/OrganizationBadges.tsx", "export": "OrganizationRoleBadge" }, "scopes": ["src/components/Settings"], "extensions": [".tsx"], "allPatterns": ["roleBadgeClass("], "legacyReason": "Retired migration debt. Organization role badges belong to OrganizationRoleBadge and MetadataBadge.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/OrganizationBadges.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "organization-share-status-badge-local-shell", "category": "metadata-badge", "summary": "Organization share tables must not recreate local statusBadgeClass helpers; compose OrganizationShareStatusBadge for canonical pending/active labels and tones.", "canonical": { "path": "src/components/shared/OrganizationBadges.tsx", "export": "OrganizationShareStatusBadge" }, "scopes": ["src/components/Settings"], "extensions": [".tsx"], "allPatterns": ["statusBadgeClass("], "legacyReason": "Retired migration debt. Organization share status badges belong to OrganizationShareStatusBadge and MetadataBadge.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/OrganizationBadges.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "form-select-local-native-select", "category": "form-control", "summary": "Product and shared filter components must compose FormSelect instead of recreating labelled native select shells locally.", "canonical": { "path": "src/components/shared/FormSelect.tsx", "export": "FormSelect" }, "scopes": ["src/components", "src/features"], "extensions": [".tsx"], "allPatterns": ["}" ], "legacyReason": "Retired migration debt. Neutral copy-value icon buttons belong to CopyValueButton.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/Button.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "copy-value-muted-local-button-shell", "category": "copy-action", "summary": "Copy-value controls must not copy the muted icon-button shell; use CopyValueButton with the ghost variant.", "canonical": { "path": "src/components/shared/Button.tsx", "export": "CopyValueButton" }, "scopes": ["src/components", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": [ "inline-flex min-h-8 min-w-8 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-hover hover:text-base-content", "fallback={}" ], "legacyReason": "Retired migration debt. Muted copy-value icon buttons belong to CopyValueButton.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/Button.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "copy-value-accent-local-button-shell", "category": "copy-action", "summary": "Copy-value controls must not copy the blue suggested-value icon-button shell; use CopyValueButton with the accent variant.", "canonical": { "path": "src/components/shared/Button.tsx", "export": "CopyValueButton" }, "scopes": ["src/components", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": [ "inline-flex min-h-7 min-w-7 shrink-0 items-center justify-center rounded text-blue-700 transition-colors hover:bg-blue-100 dark:text-blue-200 dark:hover:bg-blue-950", "fallback={}" ], "legacyReason": "Retired migration debt. Accent copy-value icon buttons belong to CopyValueButton.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/Button.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "copyable-code-row-local-shell", "category": "copy-action", "summary": "Copyable code/value rows must not copy the local surface-alt font-mono row shell; use CopyableCodeRow.", "canonical": { "path": "src/components/shared/CopyableCodeRow.tsx", "export": "CopyableCodeRow" }, "scopes": ["src/components", "src/features", "src/pages"], "extensions": [".tsx"], "allPatterns": [ "flex items-start gap-2 rounded bg-surface-alt px-2 py-1.5", "break-all font-mono text-xs text-base-content" ], "legacyReason": "Retired migration debt. Copyable code rows belong to CopyableCodeRow so code styling and copy behavior stay canonical.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/Button.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "inline-detail-local-field-grid-shell", "category": "row-detail", "summary": "Inline platform detail content must not recreate local DetailField grids; use DetailSectionTable or InlineDetailPanel.", "canonical": { "path": "src/components/shared/DetailSectionTable.tsx", "export": "InlineDetailPanel" }, "scopes": [ "src/components/Infrastructure", "src/features/docker", "src/features/kubernetes", "src/features/truenas", "src/features/vmware" ], "extensions": [".tsx"], "allPatterns": [ "const DetailField", "font-semibold uppercase tracking-wide text-muted", "grid gap-3 sm:grid-cols-2 lg:grid-cols-3" ], "legacyReason": "Retired migration debt. Inline detail field grids belong to DetailSectionTable / InlineDetailPanel so detail sections, value tones, truncation, and close actions stay canonical.", "allowedPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/__tests__/DetailSectionTable.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "inline-detail-provider-named-primitive-import", "category": "row-detail", "summary": "Platform-neutral detail section tables must not import the retired provider-named TrueNAS detail primitive.", "canonical": { "path": "src/components/shared/DetailSectionTable.tsx", "export": "DetailSectionTable" }, "scopes": ["src/components", "src/features"], "extensions": [".ts", ".tsx"], "allPatterns": ["TrueNASDetailTable"], "legacyReason": "Retired migration debt. Provider-specific names must not own platform-neutral detail tables; use DetailSectionTable / detailSectionModel.", "allowedPaths": [], "ignoredPaths": ["src/components/shared/SharedPrimitives.guardrails.test.ts"], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/shared/__tests__/DetailSectionTable.test.tsx", "scripts/shared-template-audit.mjs" ] }, { "id": "resource-detail-local-provider-row-model", "category": "row-detail", "summary": "Provider resource detail drawer models must not define provider-local row and section primitives.", "canonical": { "path": "src/components/shared/detailSectionModel.ts", "export": "DetailSection" }, "scopes": ["src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts"], "extensions": [".ts"], "allPatterns": ["ResourceDetailDrawerVMwareRow", "ResourceDetailDrawerVMwareSection"], "legacyReason": "Retired migration debt. Provider detail drawers own fields and labels, while row and section shape belongs to DetailSection / DetailRow in detailSectionModel.", "allowedPaths": [], "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "src/components/Infrastructure/__tests__/resourceDetailDrawerVmwareModel.test.ts", "scripts/shared-template-audit.mjs" ] }, { "id": "resource-detail-local-vmware-detail-shell", "category": "row-detail", "summary": "The resource detail drawer must not render VMware detail sections with a local vSphere card/table shell.", "canonical": { "path": "src/components/shared/DetailSectionTable.tsx", "export": "DetailSectionTable" }, "scopes": ["src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx"], "extensions": [".tsx"], "allPatterns": ["vmwareRowToneClass", "