@skyhook-io/k8s-ui 1.14.1 → 1.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/src/components/issues/IssuesView.tsx +1 -1
  3. package/src/components/issues/types.ts +2 -0
  4. package/src/components/resources/ResourcesView.tsx +261 -188
  5. package/src/components/resources/column-settings-merge.test.ts +104 -0
  6. package/src/components/resources/istio-gateway-filter.test.ts +5 -3
  7. package/src/components/resources/istio-policy-accessors.test.ts +193 -0
  8. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +11 -24
  9. package/src/components/resources/renderers/capi-cells.tsx +3 -0
  10. package/src/components/resources/renderers/certmanager-cells.tsx +2 -2
  11. package/src/components/resources/renderers/eso-cells.tsx +0 -5
  12. package/src/components/resources/renderers/istio-cells.tsx +36 -26
  13. package/src/components/resources/renderers/knative-cells.tsx +13 -0
  14. package/src/components/resources/renderers/kueue-cells.tsx +0 -9
  15. package/src/components/resources/renderers/kyverno-cells.tsx +8 -1
  16. package/src/components/resources/renderers/kyverno-modern-cells.tsx +6 -0
  17. package/src/components/resources/renderers/prometheus-cells.tsx +3 -3
  18. package/src/components/resources/renderers/ray-cells.tsx +12 -4
  19. package/src/components/resources/renderers/trivy-cells.tsx +7 -0
  20. package/src/components/resources/renderers/velero-cells.tsx +3 -0
  21. package/src/components/resources/renderers/volcano-cells.tsx +0 -9
  22. package/src/components/resources/resource-utils-calico.test.ts +6 -0
  23. package/src/components/resources/resource-utils-calico.ts +6 -2
  24. package/src/components/resources/resource-utils-cnpg.ts +5 -0
  25. package/src/components/resources/resource-utils-eso.test.ts +56 -0
  26. package/src/components/resources/resource-utils-eso.ts +37 -5
  27. package/src/components/resources/resource-utils-istio.ts +152 -44
  28. package/src/components/resources/resource-utils-kyverno.ts +3 -6
  29. package/src/components/resources/resource-utils-prometheus.ts +51 -23
  30. package/src/components/resources/resource-utils-ray.test.ts +36 -0
  31. package/src/components/resources/resource-utils-ray.ts +6 -0
  32. package/src/components/resources/resource-utils-trivy.ts +3 -0
  33. package/src/components/resources/resource-utils.ts +183 -19
  34. package/src/components/resources/resources-column-filter.test.ts +20 -0
  35. package/src/components/resources/status-accessors.test.ts +363 -0
  36. package/src/components/resources/status-overclaim.test.ts +158 -0
  37. package/src/components/timeline/DiffViewer.test.tsx +26 -0
  38. package/src/components/timeline/DiffViewer.tsx +16 -12
  39. package/src/components/topology/TopologyGraph.tsx +7 -2
  40. package/src/components/ui/Collapse.tsx +9 -2
  41. package/src/components/ui/ConfirmDialog.test.tsx +37 -0
  42. package/src/components/ui/ConfirmDialog.tsx +3 -1
  43. package/src/components/ui/drawer-components.tsx +1 -1
  44. package/src/components/workload/WorkloadView.tsx +2 -1
@@ -110,6 +110,7 @@ import {
110
110
  getGatewayClassController,
111
111
  getGatewayClassDescription,
112
112
  getRouteStatus,
113
+ getRouteStatusReason,
113
114
  getRouteParents,
114
115
  getRouteHostnames,
115
116
  getRouteBackends,
@@ -566,7 +567,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
566
567
  { key: 'name', label: 'Name' },
567
568
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
568
569
  { key: 'status', label: 'Status', width: 'w-40' },
569
- { key: 'desired', label: 'Desired', width: 'w-20', tooltip: 'Number of nodes that should run the daemon pod (based on node selector)' },
570
+ { key: 'desired', label: 'Desired', width: 'w-20', tooltip: 'Nodes eligible for this daemon after node selectors, required node affinity and taints/tolerations are applied' },
570
571
  { key: 'ready', label: 'Ready', width: 'w-20', tooltip: 'Number of pods that are ready (passing readiness probes)' },
571
572
  { key: 'upToDate', label: 'Up-to-date', width: 'w-32', tooltip: 'Number of pods running the current pod template spec' },
572
573
  { key: 'available', label: 'Available', width: 'w-28', tooltip: 'Number of pods available (ready for minReadySeconds duration)' },
@@ -578,7 +579,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
578
579
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
579
580
  { key: 'status', label: 'Status', width: 'w-40' },
580
581
  { key: 'ready', label: 'Ready', width: 'w-24', tooltip: 'Ready pods / Desired replicas' },
581
- { key: 'upToDate', label: 'Up-to-date', width: 'w-32', tooltip: 'Number of pods running the current pod template' },
582
+ { key: 'upToDate', label: 'Up-to-date', width: 'w-32', tooltip: 'Pods running the current pod template; a rolling-update partition or OnDelete strategy can intentionally leave pods on an older template' },
582
583
  { key: 'images', label: 'Images', width: 'w-48' },
583
584
  { key: 'age', label: 'Age', width: 'w-24' },
584
585
  ],
@@ -587,7 +588,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
587
588
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
588
589
  { key: 'ready', label: 'Ready', width: 'w-24' },
589
590
  { key: 'owner', label: 'Owner', width: 'w-48' },
590
- { key: 'status', label: 'Status', width: 'w-24' },
591
+ { key: 'status', label: 'Status', width: 'w-24', tooltip: 'Active means desired replicas are greater than zero; Old means desired replicas are zero, regardless of Deployment revision' },
591
592
  { key: 'age', label: 'Age', width: 'w-24' },
592
593
  ],
593
594
  services: [
@@ -623,7 +624,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
623
624
  nodes: [
624
625
  { key: 'name', label: 'Name' },
625
626
  { key: 'status', label: 'Status', width: 'w-44' },
626
- { key: 'roles', label: 'Roles', width: 'w-28' },
627
+ { key: 'roles', label: 'Roles', width: 'w-28', tooltip: 'Node roles from node-role.kubernetes.io/* labels. A node with no role label shows as worker.' },
627
628
  { key: 'cpu', label: 'CPU', width: 'w-40', tooltip: 'Current CPU usage / allocatable' },
628
629
  { key: 'memory', label: 'Memory', width: 'w-40', tooltip: 'Current memory usage / allocatable' },
629
630
  { key: 'pods', label: 'Pods', width: 'w-28', tooltip: 'Pods running / allocatable' },
@@ -637,7 +638,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
637
638
  { key: 'name', label: 'Name' },
638
639
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
639
640
  { key: 'keys', label: 'Keys', width: 'w-48' },
640
- { key: 'size', label: 'Size', width: 'w-24' },
641
+ { key: 'size', label: 'Size', width: 'w-24', tooltip: 'Estimated size of data values, including decoded binary data. Kubernetes rejects ConfigMaps whose combined data exceeds 1 MiB.' },
641
642
  { key: 'age', label: 'Age', width: 'w-24' },
642
643
  ],
643
644
  secrets: [
@@ -645,7 +646,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
645
646
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
646
647
  { key: 'type', label: 'Type', width: 'w-28' },
647
648
  { key: 'keys', label: 'Keys', width: 'w-20' },
648
- { key: 'expires', label: 'Expires', width: 'w-24', tooltip: 'Certificate expiry for TLS secrets' },
649
+ { key: 'expires', label: 'Expires', width: 'w-24', tooltip: "Days until the certificate in a TLS Secret expires. Blank for Secrets carrying no readable TLS certificate; ! means the expiry lookup itself failed." },
649
650
  { key: 'age', label: 'Age', width: 'w-24' },
650
651
  ],
651
652
  jobs: [
@@ -659,7 +660,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
659
660
  cronjobs: [
660
661
  { key: 'name', label: 'Name' },
661
662
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
662
- { key: 'schedule', label: 'Schedule', width: 'w-40' },
663
+ { key: 'schedule', label: 'Schedule', width: 'w-40', tooltip: "Cron schedule; fires in spec.timeZone when set, otherwise the controller's zone" },
663
664
  { key: 'status', label: 'Status', width: 'w-28' },
664
665
  { key: 'lastRun', label: 'Last Run', width: 'w-28' },
665
666
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -685,10 +686,10 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
685
686
  persistentvolumeclaims: [
686
687
  { key: 'name', label: 'Name' },
687
688
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
688
- { key: 'status', label: 'Status', width: 'w-24' },
689
+ { key: 'status', label: 'Status', width: 'w-24', tooltip: 'With WaitForFirstConsumer, Pending can mean no pod has triggered binding yet. If a pod is waiting, check PVC events.' },
689
690
  { key: 'capacity', label: 'Capacity', width: 'w-24' },
690
691
  { key: 'storageClass', label: 'Storage Class', width: 'w-40' },
691
- { key: 'accessModes', label: 'Access', width: 'w-20', tooltip: 'Access modes: RWO=ReadWriteOnce, RWX=ReadWriteMany, ROX=ReadOnlyMany' },
692
+ { key: 'accessModes', label: 'Access', width: 'w-20', tooltip: 'RWO=ReadWriteOnce (one node, possibly several pods), ROX=ReadOnlyMany (read-only on many nodes), RWX=ReadWriteMany (read-write on many nodes), RWOP=ReadWriteOncePod (one pod only)' },
692
693
  { key: 'volume', label: 'Volume', width: 'w-48' },
693
694
  { key: 'age', label: 'Age', width: 'w-24' },
694
695
  ],
@@ -715,7 +716,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
715
716
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
716
717
  { key: 'status', label: 'Phase', width: 'w-28' },
717
718
  { key: 'duration', label: 'Duration', width: 'w-24' },
718
- { key: 'progress', label: 'Progress', width: 'w-24', tooltip: 'Succeeded steps / Total steps' },
719
+ { key: 'progress', label: 'Progress', width: 'w-24', tooltip: 'Completed tasks / currently known total; the total can grow during execution, and completion does not imply success' },
719
720
  { key: 'template', label: 'Template', width: 'w-40' },
720
721
  { key: 'age', label: 'Age', width: 'w-24' },
721
722
  ],
@@ -733,16 +734,16 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
733
734
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
734
735
  { key: 'status', label: 'Ready', width: 'w-24' },
735
736
  { key: 'domains', label: 'Domains', width: 'w-56' },
736
- { key: 'issuer', label: 'Issuer', width: 'w-36' },
737
+ { key: 'issuer', label: 'Issuer', width: 'w-36', tooltip: "Issuer name alone may be ambiguous. Check the certificate's issuer reference for its kind and API group." },
737
738
  { key: 'expires', label: 'Expires', width: 'w-24', tooltip: 'Days until certificate expires' },
738
739
  { key: 'age', label: 'Age', width: 'w-24' },
739
740
  ],
740
741
  persistentvolumes: [
741
742
  { key: 'name', label: 'Name' },
742
- { key: 'status', label: 'Status', width: 'w-24' },
743
+ { key: 'status', label: 'Status', width: 'w-24', tooltip: "Released means the claim is gone but the volume still holds the previous claimant's data, so it will not be re-bound — check Reclaim before reusing it." },
743
744
  { key: 'capacity', label: 'Capacity', width: 'w-24' },
744
- { key: 'accessModes', label: 'Access', width: 'w-20', tooltip: 'RWO=ReadWriteOnce, ROX=ReadOnlyMany, RWX=ReadWriteMany' },
745
- { key: 'reclaimPolicy', label: 'Reclaim', width: 'w-20' },
745
+ { key: 'accessModes', label: 'Access', width: 'w-20', tooltip: 'RWO=ReadWriteOnce (one node, possibly several pods), ROX=ReadOnlyMany (read-only on many nodes), RWX=ReadWriteMany (read-write on many nodes), RWOP=ReadWriteOncePod (one pod only)' },
746
+ { key: 'reclaimPolicy', label: 'Reclaim', width: 'w-20', tooltip: 'When the claim is deleted, Delete removes the backing storage and its data; Retain leaves it for manual reclamation.' },
746
747
  { key: 'storageClass', label: 'Storage Class', width: 'w-40' },
747
748
  { key: 'claim', label: 'Claim', width: 'w-48' },
748
749
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -750,8 +751,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
750
751
  storageclasses: [
751
752
  { key: 'name', label: 'Name' },
752
753
  { key: 'provisioner', label: 'Provisioner', width: 'w-48' },
753
- { key: 'reclaimPolicy', label: 'Reclaim', width: 'w-20' },
754
- { key: 'bindingMode', label: 'Binding Mode', width: 'w-36' },
754
+ { key: 'reclaimPolicy', label: 'Reclaim', width: 'w-20', tooltip: 'Default policy for volumes this class provisions — Delete removes the backing storage when the claim goes away; Retain leaves it for manual reclamation.' },
755
+ { key: 'bindingMode', label: 'Binding Mode', width: 'w-36', tooltip: "When the volume is provisioned — WaitForFirstConsumer waits for a pod so the disk lands in the right zone; Immediate provisions before the pod's placement is known, which can leave the pod unschedulable." },
755
756
  { key: 'expansion', label: 'Expansion', width: 'w-24', tooltip: 'Whether volumes can be expanded after creation' },
756
757
  { key: 'age', label: 'Age', width: 'w-24' },
757
758
  ],
@@ -779,7 +780,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
779
780
  orders: [
780
781
  { key: 'name', label: 'Name' },
781
782
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
782
- { key: 'state', label: 'State', width: 'w-24' },
783
+ { key: 'state', label: 'State', width: 'w-24', tooltip: 'Valid means ACME issuance finished; it does not confirm the TLS Secret is ready. If Pending persists, inspect the challenges and the order reason.' },
783
784
  { key: 'domains', label: 'Domains', width: 'w-48' },
784
785
  { key: 'issuer', label: 'Issuer', width: 'w-36' },
785
786
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -788,25 +789,27 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
788
789
  { key: 'name', label: 'Name' },
789
790
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
790
791
  { key: 'challengeType', label: 'Type', width: 'w-20' },
791
- { key: 'state', label: 'State', width: 'w-24' },
792
+ { key: 'state', label: 'State', width: 'w-24', tooltip: 'Valid means domain authorization succeeded. If Pending persists, inspect the reason for scheduling, solver, or propagation problems.' },
792
793
  { key: 'domain', label: 'Domain', width: 'w-48' },
793
- { key: 'presented', label: 'Presented', width: 'w-24' },
794
+ { key: 'presented', label: 'Presented', width: 'w-24', tooltip: 'While Pending, Yes means the solver presented the token, not that it is reachable. Check the reason for propagation failures; No can also mean queued.' },
794
795
  { key: 'age', label: 'Age', width: 'w-24' },
795
796
  ],
796
797
  gateways: [
797
798
  { key: 'name', label: 'Name' },
798
799
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
799
- { key: 'status', label: 'Status', width: 'w-28' },
800
+ { key: 'status', label: 'Status', width: 'w-28', tooltip: "Programmed = the controller reports configuration should take effect soon; traffic has not been tested. Accepted = the controller accepted this Gateway but has not reported Programmed. If it stays Accepted, check the Gateway's conditions and addresses." },
800
801
  { key: 'class', label: 'Class', width: 'w-36' },
801
802
  { key: 'listeners', label: 'Listeners', width: 'w-40', tooltip: 'Protocol:Port for each listener' },
802
- { key: 'routes', label: 'Routes', width: 'w-20', tooltip: 'Total attached routes across all listeners' },
803
+ // attachedRoutes is reported per listener, so a route attached to several
804
+ // listeners is counted once for each — the total is not a route count.
805
+ { key: 'routes', label: 'Routes', width: 'w-20', tooltip: "Sum of route counts reported by listeners; one route can count more than once. If 0 is unexpected, check the routes' Gateway names, listener permissions, and controller status." },
803
806
  { key: 'addresses', label: 'Addresses', width: 'w-48' },
804
807
  { key: 'age', label: 'Age', width: 'w-24' },
805
808
  ],
806
809
  httproutes: [
807
810
  { key: 'name', label: 'Name' },
808
811
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
809
- { key: 'status', label: 'Status', width: 'w-28' },
812
+ { key: 'status', label: 'Status', width: 'w-28', tooltip: 'Accepted = controllers accepted this route and confirmed it can use the backends and other objects it names. Traffic has not been tested. Degraded or Not Accepted: open the route to see which Gateway or backend failed. Pending or Unknown: check the named Gateways or Services and their controllers.' },
810
813
  { key: 'hostnames', label: 'Hostnames', width: 'w-48' },
811
814
  { key: 'parents', label: 'Gateways', width: 'w-36' },
812
815
  { key: 'backends', label: 'Backends', width: 'w-48', tooltip: 'Backend services receiving traffic' },
@@ -818,7 +821,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
818
821
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
819
822
  { key: 'status', label: 'Status', width: 'w-28' },
820
823
  { key: 'servers', label: 'Servers', width: 'w-24' },
821
- { key: 'selector', label: 'Selector', width: 'w-56' },
824
+ { key: 'selector', label: 'Selector', width: 'w-56', tooltip: "Selects gateway workloads that receive this configuration. An omitted selector applies to all workloads in Istio's selection scope; it does not mean none." },
822
825
  { key: 'age', label: 'Age', width: 'w-24' },
823
826
  ],
824
827
  gatewayclasses: [
@@ -832,14 +835,13 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
832
835
  { key: 'name', label: 'Name' },
833
836
  { key: 'status', label: 'Status', width: 'w-24' },
834
837
  { key: 'nodeClass', label: 'Node Class', width: 'w-36' },
835
- { key: 'limits', label: 'Limits', width: 'w-36', tooltip: 'CPU and memory limits' },
836
- { key: 'disruption', label: 'Disruption', width: 'w-40' },
838
+ { key: 'limits', label: 'Limits', width: 'w-36', tooltip: 'Configured provisioning ceilings — not what the pool has provisioned. Concurrent provisioning can exceed them during rapid scale-out, and a limit does not remove nodes already running.' },
839
+ { key: 'disruption', label: 'Disruption', width: 'w-40', tooltip: 'Which nodes Karpenter may consolidate; budgets and timing controls can still prevent removal or replacement' },
837
840
  { key: 'age', label: 'Age', width: 'w-24' },
838
841
  ],
839
842
  nodeclaims: [
840
843
  { key: 'name', label: 'Name' },
841
- { key: 'namespace', label: 'Namespace', width: 'w-36' },
842
- { key: 'status', label: 'Status', width: 'w-24' },
844
+ { key: 'status', label: 'Status', width: 'w-24', tooltip: 'Launched means a cloud instance exists; Registered means it has joined Kubernetes; Ready means the node is fully initialized' },
843
845
  { key: 'instanceType', label: 'Instance Type', width: 'w-32' },
844
846
  { key: 'capacityType', label: 'Capacity', width: 'w-24', tooltip: 'Spot or On-Demand' },
845
847
  { key: 'zone', label: 'Zone', width: 'w-28' },
@@ -868,7 +870,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
868
870
  { key: 'name', label: 'Name' },
869
871
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
870
872
  { key: 'status', label: 'Status', width: 'w-28', tooltip: 'Allocated + reserved, allocated-but-unreserved, or pending' },
871
- { key: 'deviceClass', label: 'Device Class', width: 'w-44' },
873
+ { key: 'deviceClass', label: 'Device Class', width: 'w-44', tooltip: 'DeviceClasses whose selectors constrain eligible devices. A request may list alternative classes; this column does not show which alternative was allocated.' },
872
874
  { key: 'allocated', label: 'Allocated Driver', width: 'w-44' },
873
875
  { key: 'reservedFor', label: 'Reserved For', width: 'w-44' },
874
876
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -881,37 +883,39 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
881
883
  ],
882
884
  deviceclasses: [
883
885
  { key: 'name', label: 'Name' },
884
- { key: 'selectors', label: 'Selectors', width: 'w-28', tooltip: 'CEL device selector count' },
886
+ // Selectors are ANDed, so an empty list restricts nothing rather than
887
+ // matching nothing — the reading most people reach for first.
888
+ { key: 'selectors', label: 'Selectors', width: 'w-28', tooltip: 'Devices must satisfy every selector. Zero selectors means this class imposes no selector restriction; claim selectors and other allocation requirements still apply.' },
885
889
  { key: 'age', label: 'Age', width: 'w-24' },
886
890
  ],
887
891
  resourceslices: [
888
892
  { key: 'name', label: 'Name' },
889
893
  { key: 'driver', label: 'Driver', width: 'w-44' },
890
- { key: 'pool', label: 'Pool', width: 'w-36' },
891
- { key: 'node', label: 'Node', width: 'w-44' },
892
- { key: 'devices', label: 'Devices', width: 'w-24' },
894
+ { key: 'pool', label: 'Pool', width: 'w-36', tooltip: "Pools are identified by driver and pool name. The scheduler needs a complete set of slices at the pool's highest generation before allocating from it." },
895
+ { key: 'node', label: 'Node', width: 'w-44', tooltip: "Node providing this slice's devices. A dash means node access is described by a node selector, all-nodes access, or per-device node selection." },
896
+ { key: 'devices', label: 'Devices', width: 'w-24', tooltip: 'Devices published in this slice, including devices already allocated. This is not a count of free devices or the whole pool.' },
893
897
  { key: 'age', label: 'Age', width: 'w-24' },
894
898
  ],
895
899
  nvidiaclusterpolicies: [
896
900
  { key: 'name', label: 'Name' },
897
901
  { key: 'status', label: 'Status', width: 'w-28' },
898
- { key: 'components', label: 'Components', width: 'w-64', tooltip: 'Enabled GPU Operator components' },
899
- { key: 'mig', label: 'MIG', width: 'w-24' },
902
+ { key: 'components', label: 'Components', width: 'w-64', tooltip: 'Enabled GPU Operator components" with "GPU Operator components enabled in spec — not whether they are running' },
903
+ { key: 'mig', label: 'MIG', width: 'w-24', tooltip: 'Multi-Instance GPU exposure: single advertises uniform partitions as nvidia.com/gpu; mixed advertises separate partition profiles; none disables MIG-aware exposure' },
900
904
  { key: 'age', label: 'Age', width: 'w-24' },
901
905
  ],
902
906
  nvidiadrivers: [
903
907
  { key: 'name', label: 'Name' },
904
908
  { key: 'status', label: 'Status', width: 'w-28' },
905
- { key: 'driverType', label: 'Type', width: 'w-28' },
906
- { key: 'version', label: 'Version', width: 'w-32' },
909
+ { key: 'driverType', label: 'Type', width: 'w-28', tooltip: 'Driver role: gpu for standard GPU workloads, vgpu for virtual GPU guests, or vgpu-host-manager for virtualization hosts' },
910
+ { key: 'version', label: 'Version', width: 'w-32', tooltip: 'Driver version requested in spec — nodes may still be running the previous one' },
907
911
  { key: 'age', label: 'Age', width: 'w-24' },
908
912
  ],
909
913
  clusterqueues: [
910
914
  { key: 'name', label: 'Name' },
911
915
  { key: 'status', label: 'Status', width: 'w-24' },
912
- { key: 'cohort', label: 'Cohort', width: 'w-32' },
913
- { key: 'pendingWorkloads', label: 'Pending', width: 'w-24', tooltip: 'Pending workloads' },
914
- { key: 'admittedWorkloads', label: 'Admitted', width: 'w-24', tooltip: 'Admitted workloads' },
916
+ { key: 'cohort', label: 'Cohort', width: 'w-32', tooltip: 'Queues in this cohort can share unused quota, subject to borrowing and lending limits' },
917
+ { key: 'pendingWorkloads', label: 'Pending', width: 'w-24', tooltip: 'Workloads in this ClusterQueue waiting for admission, across namespaces; inspect workload status for the blocker' },
918
+ { key: 'admittedWorkloads', label: 'Admitted', width: 'w-24', tooltip: 'Unfinished workloads admitted through this ClusterQueue, across namespaces; admission does not mean their pods are running' },
915
919
  { key: 'flavors', label: 'Flavors', width: 'w-40' },
916
920
  { key: 'age', label: 'Age', width: 'w-24' },
917
921
  ],
@@ -920,8 +924,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
920
924
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
921
925
  { key: 'status', label: 'Status', width: 'w-24' },
922
926
  { key: 'clusterQueue', label: 'Cluster Queue', width: 'w-40' },
923
- { key: 'pendingWorkloads', label: 'Pending', width: 'w-24' },
924
- { key: 'admittedWorkloads', label: 'Admitted', width: 'w-24' },
927
+ { key: 'pendingWorkloads', label: 'Pending', width: 'w-24', tooltip: 'Workloads submitted to this LocalQueue that are waiting for ClusterQueue admission' },
928
+ { key: 'admittedWorkloads', label: 'Admitted', width: 'w-24', tooltip: 'Unfinished workloads from this LocalQueue admitted to its ClusterQueue; their pods may still be waiting to run' },
925
929
  { key: 'age', label: 'Age', width: 'w-24' },
926
930
  ],
927
931
  workloads: [
@@ -935,7 +939,6 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
935
939
  ],
936
940
  resourceflavors: [
937
941
  { key: 'name', label: 'Name' },
938
- { key: 'status', label: 'Status', width: 'w-24' },
939
942
  { key: 'nodeLabels', label: 'Node Labels', width: 'w-28', tooltip: 'Node label selector count' },
940
943
  { key: 'taints', label: 'Taints', width: 'w-24' },
941
944
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -943,14 +946,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
943
946
  admissionchecks: [
944
947
  { key: 'name', label: 'Name' },
945
948
  { key: 'status', label: 'Status', width: 'w-24' },
946
- { key: 'controllerName', label: 'Controller', width: 'w-64' },
949
+ { key: 'controllerName', label: 'Controller', width: 'w-64', tooltip: 'Controller responsible for this check; workloads requiring it cannot be admitted until the check passes' },
947
950
  { key: 'age', label: 'Age', width: 'w-24' },
948
951
  ],
949
952
  provisioningrequests: [
950
953
  { key: 'name', label: 'Name' },
951
954
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
952
955
  { key: 'status', label: 'Status', width: 'w-28' },
953
- { key: 'provisioningClassName', label: 'Class', width: 'w-56' },
956
+ { key: 'provisioningClassName', label: 'Class', width: 'w-56', tooltip: 'Provisioning mode: check-capacity checks existing capacity without reserving it; best-effort-atomic-scale-up creates capacity atomically' },
954
957
  { key: 'podSets', label: 'Pod Sets', width: 'w-24' },
955
958
  { key: 'age', label: 'Age', width: 'w-24' },
956
959
  ],
@@ -976,7 +979,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
976
979
  { key: 'name', label: 'Name' },
977
980
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
978
981
  { key: 'status', label: 'Status', width: 'w-28' },
979
- { key: 'serviceStatus', label: 'Service Status', width: 'w-28' },
982
+ { key: 'serviceStatus', label: 'Service Status', width: 'w-28', tooltip: "KubeRay's serving-readiness flag; this does not describe the active rollout phase." },
980
983
  { key: 'clusters', label: 'Clusters', width: 'w-56', tooltip: 'Active and pending RayCluster names' },
981
984
  { key: 'age', label: 'Age', width: 'w-24' },
982
985
  ],
@@ -985,8 +988,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
985
988
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
986
989
  { key: 'status', label: 'Status', width: 'w-24' },
987
990
  { key: 'schedule', label: 'Schedule', width: 'w-32' },
988
- { key: 'suspend', label: 'Suspend', width: 'w-20' },
989
- { key: 'lastSchedule', label: 'Last Schedule', width: 'w-28' },
991
+ { key: 'timeZone', label: 'Time Zone', width: 'w-36', tooltip: 'IANA zone the schedule is read in; unset follows the local zone of the KubeRay operator pod' },
992
+ { key: 'lastSchedule', label: 'Last Schedule', width: 'w-28', tooltip: 'Time since the last recorded schedule; this does not confirm the job started or succeeded.' },
990
993
  { key: 'age', label: 'Age', width: 'w-24' },
991
994
  ],
992
995
  leaderworkersets: [
@@ -1007,7 +1010,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1007
1010
  { key: 'ready', label: 'Ready', width: 'w-20' },
1008
1011
  { key: 'succeeded', label: 'Succeeded', width: 'w-24' },
1009
1012
  { key: 'failed', label: 'Failed', width: 'w-20' },
1010
- { key: 'restarts', label: 'Restarts', width: 'w-24' },
1013
+ { key: 'restarts', label: 'Restarts', width: 'w-24', tooltip: 'Whole-JobSet restarts by the failure policy — every child Job is recreated' },
1011
1014
  { key: 'age', label: 'Age', width: 'w-24' },
1012
1015
  ],
1013
1016
  inferenceservices: [
@@ -1062,7 +1065,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1062
1065
  inferencepools: [
1063
1066
  { key: 'name', label: 'Name' },
1064
1067
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1065
- { key: 'status', label: 'Status', width: 'w-36', tooltip: 'Gateway acceptance and EPP resolution' },
1068
+ { key: 'status', label: 'Status', width: 'w-36', tooltip: 'Gateway acceptance and reference errors across reported parents; Accepted does not prove backend Pods are ready.' },
1066
1069
  { key: 'selector', label: 'Selector', width: 'w-48' },
1067
1070
  { key: 'targetPorts', label: 'Target Ports', width: 'w-28' },
1068
1071
  { key: 'extensionRef', label: 'Endpoint Picker', width: 'w-40', tooltip: 'EPP extension service' },
@@ -1073,7 +1076,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1073
1076
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1074
1077
  { key: 'status', label: 'Status', width: 'w-24' },
1075
1078
  { key: 'poolRef', label: 'Pool', width: 'w-40', tooltip: 'Target InferencePool' },
1076
- { key: 'priority', label: 'Priority', width: 'w-24' },
1079
+ { key: 'priority', label: 'Priority', width: 'w-24', tooltip: 'Higher values are served first when pool resources are scarce; an unset priority is treated as 0.' },
1077
1080
  { key: 'age', label: 'Age', width: 'w-24' },
1078
1081
  ],
1079
1082
  volcanojobs: [
@@ -1081,7 +1084,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1081
1084
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1082
1085
  { key: 'status', label: 'Status', width: 'w-28' },
1083
1086
  { key: 'queue', label: 'Queue', width: 'w-32' },
1084
- { key: 'minAvailable', label: 'Min Available', width: 'w-28' },
1087
+ { key: 'minAvailable', label: 'Min Available', width: 'w-28', tooltip: 'Minimum pods that must be schedulable together for gang scheduling to start the job' },
1085
1088
  { key: 'pods', label: 'Pods', width: 'w-44', tooltip: 'Running / succeeded / failed pod counts' },
1086
1089
  { key: 'age', label: 'Age', width: 'w-24' },
1087
1090
  ],
@@ -1089,8 +1092,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1089
1092
  { key: 'name', label: 'Name' },
1090
1093
  { key: 'status', label: 'Status', width: 'w-24' },
1091
1094
  { key: 'weight', label: 'Weight', width: 'w-20' },
1092
- { key: 'capability', label: 'Capability', width: 'w-48' },
1093
- { key: 'allocated', label: 'Allocated', width: 'w-48' },
1095
+ { key: 'capability', label: 'Capability', width: 'w-48', tooltip: 'Configured resource limits for this queue; actual allocation also depends on available resources and scheduler policy' },
1096
+ { key: 'allocated', label: 'Allocated', width: 'w-48', tooltip: 'Resources the scheduler reports as allocated to this queue; these are allocations, not measured CPU or memory use' },
1094
1097
  { key: 'podGroups', label: 'PodGroups', width: 'w-44', tooltip: 'PodGroup counts by state' },
1095
1098
  { key: 'age', label: 'Age', width: 'w-24' },
1096
1099
  ],
@@ -1106,23 +1109,22 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1106
1109
  { key: 'name', label: 'Name' },
1107
1110
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1108
1111
  { key: 'status', label: 'Status', width: 'w-28' },
1109
- { key: 'flows', label: 'Flows', width: 'w-20' },
1112
+ { key: 'flows', label: 'Flows', width: 'w-20', tooltip: 'Declared job stages; dependencies control when each stage can start' },
1110
1113
  { key: 'age', label: 'Age', width: 'w-24' },
1111
1114
  ],
1112
1115
  jobtemplates: [
1113
1116
  { key: 'name', label: 'Name' },
1114
1117
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1115
- { key: 'status', label: 'Status', width: 'w-24' },
1116
1118
  { key: 'tasks', label: 'Tasks', width: 'w-20' },
1117
1119
  { key: 'age', label: 'Age', width: 'w-24' },
1118
1120
  ],
1119
1121
  kaiqueues: [
1120
1122
  { key: 'name', label: 'Name' },
1121
1123
  { key: 'status', label: 'Status', width: 'w-24' },
1122
- { key: 'parentQueue', label: 'Parent', width: 'w-32' },
1123
- { key: 'priority', label: 'Priority', width: 'w-20' },
1124
- { key: 'quota', label: 'Quota', width: 'w-52', tooltip: 'GPU fractions, CPU millicpus, memory MB; -1 = unlimited' },
1125
- { key: 'allocated', label: 'Allocated', width: 'w-48' },
1124
+ { key: 'parentQueue', label: 'Parent', width: 'w-32', tooltip: 'Parent queue whose resources are shared among its children; ancestor limits constrain allocations' },
1125
+ { key: 'priority', label: 'Priority', width: 'w-20', tooltip: "Higher priority favors allocation of resources above quota and delays reclaim; it does not increase the queue's guaranteed quota" },
1126
+ { key: 'quota', label: 'Quota', width: 'w-52', tooltip: 'Guaranteed resource allocation; the queue may exceed it when spare resources and configured limits allow' },
1127
+ { key: 'allocated', label: 'Allocated', width: 'w-48', tooltip: 'Resources allocated to this queue and its descendants, including scheduled pods still starting; this is not measured utilization' },
1126
1128
  { key: 'age', label: 'Age', width: 'w-24' },
1127
1129
  ],
1128
1130
  kaipodgroups: [
@@ -1137,9 +1139,9 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1137
1139
  { key: 'name', label: 'Name' },
1138
1140
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1139
1141
  { key: 'status', label: 'Status', width: 'w-28' },
1140
- { key: 'instanceType', label: 'Instance Type', width: 'w-40', tooltip: 'GPU node SKU provisioned for the workspace' },
1142
+ { key: 'instanceType', label: 'Instance Type', width: 'w-40', tooltip: 'Requested node instance type; actual node hardware is not checked.' },
1141
1143
  { key: 'preset', label: 'Model', width: 'w-44', tooltip: 'Preset model (inference or tuning)' },
1142
- { key: 'nodes', label: 'Nodes', width: 'w-20', tooltip: 'Worker nodes ready / target' },
1144
+ { key: 'nodes', label: 'Nodes', width: 'w-20', tooltip: 'Worker nodes listed by KAITO, with the target count when available; listing does not confirm readiness.' },
1143
1145
  { key: 'age', label: 'Age', width: 'w-24' },
1144
1146
  ],
1145
1147
  ragengines: [
@@ -1147,7 +1149,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1147
1149
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1148
1150
  { key: 'status', label: 'Status', width: 'w-28' },
1149
1151
  { key: 'embedding', label: 'Embedding Model', width: 'w-56', tooltip: 'Local model ID/image or remote endpoint' },
1150
- { key: 'instanceType', label: 'Instance Type', width: 'w-40' },
1152
+ { key: 'instanceType', label: 'Instance Type', width: 'w-40', tooltip: 'Requested node instance type; actual node hardware is not checked.' },
1151
1153
  { key: 'age', label: 'Age', width: 'w-24' },
1152
1154
  ],
1153
1155
  nimservices: [
@@ -1210,7 +1212,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1210
1212
  { key: 'name', label: 'Name' },
1211
1213
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1212
1214
  { key: 'status', label: 'Status', width: 'w-28' },
1213
- { key: 'runtime', label: 'Runtime', width: 'w-44', tooltip: 'Training runtime reference' },
1215
+ { key: 'runtime', label: 'Runtime', width: 'w-44', tooltip: 'Runtime template supplying training defaults; TrainJob settings can override its image and node count.' },
1214
1216
  { key: 'suspended', label: 'Suspended', width: 'w-24' },
1215
1217
  { key: 'age', label: 'Age', width: 'w-24' },
1216
1218
  ],
@@ -1219,7 +1221,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1219
1221
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1220
1222
  { key: 'status', label: 'Status', width: 'w-24' },
1221
1223
  { key: 'target', label: 'Job Target', width: 'w-48' },
1222
- { key: 'strategy', label: 'Strategy', width: 'w-28' },
1224
+ { key: 'strategy', label: 'Strategy', width: 'w-28', tooltip: 'Controls how queue length and running or pending Jobs determine how many new Jobs KEDA creates' },
1223
1225
  { key: 'triggerTypes', label: 'Trigger Types', width: 'w-40' },
1224
1226
  { key: 'age', label: 'Age', width: 'w-24' },
1225
1227
  ],
@@ -1241,7 +1243,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1241
1243
  servicemonitors: [
1242
1244
  { key: 'name', label: 'Name' },
1243
1245
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1244
- { key: 'status', label: 'Status', width: 'w-24' },
1246
+ { key: 'status', label: 'Status', width: 'w-36' },
1245
1247
  { key: 'endpoints', label: 'Endpoints', width: 'w-20', tooltip: 'Number of scrape endpoints' },
1246
1248
  { key: 'jobLabel', label: 'Job Label', width: 'w-32' },
1247
1249
  { key: 'selector', label: 'Selector', width: 'w-48' },
@@ -1250,7 +1252,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1250
1252
  prometheusrules: [
1251
1253
  { key: 'name', label: 'Name' },
1252
1254
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1253
- { key: 'status', label: 'Status', width: 'w-24' },
1255
+ { key: 'status', label: 'Status', width: 'w-36' },
1254
1256
  { key: 'groups', label: 'Groups', width: 'w-20' },
1255
1257
  { key: 'rules', label: 'Rules', width: 'w-20', tooltip: 'Total alert + recording rules' },
1256
1258
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -1258,7 +1260,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1258
1260
  podmonitors: [
1259
1261
  { key: 'name', label: 'Name' },
1260
1262
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1261
- { key: 'status', label: 'Status', width: 'w-24' },
1263
+ { key: 'status', label: 'Status', width: 'w-36' },
1262
1264
  { key: 'endpoints', label: 'Endpoints', width: 'w-20', tooltip: 'Number of pod metrics endpoints' },
1263
1265
  { key: 'selector', label: 'Selector', width: 'w-48' },
1264
1266
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -1269,18 +1271,18 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1269
1271
  policyreports: [
1270
1272
  { key: 'name', label: 'Name' },
1271
1273
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1272
- { key: 'scope', label: 'Subject', width: 'w-56' },
1274
+ { key: 'scope', label: 'Subject', width: 'w-56', tooltip: "The resource this report is about. Kyverno names the report after the resource's UID; use Subject to identify it." },
1273
1275
  { key: 'status', label: 'Status', width: 'w-32' },
1274
1276
  { key: 'pass', label: 'Pass', width: 'w-16' },
1275
1277
  { key: 'fail', label: 'Fail', width: 'w-16' },
1276
1278
  { key: 'warn', label: 'Warn', width: 'w-20' },
1277
- { key: 'error', label: 'Err', width: 'w-16' },
1279
+ { key: 'error', label: 'Err', width: 'w-16', tooltip: 'Checks that could not be evaluated, for example because of missing context or a broken expression. Errors are not violations, but zero failures does not establish compliance while checks remain unevaluated.' },
1278
1280
  { key: 'skip', label: 'Skip', width: 'w-16' },
1279
1281
  { key: 'age', label: 'Age', width: 'w-24' },
1280
1282
  ],
1281
1283
  clusterpolicyreports: [
1282
1284
  { key: 'name', label: 'Name' },
1283
- { key: 'scope', label: 'Subject', width: 'w-56' },
1285
+ { key: 'scope', label: 'Subject', width: 'w-56', tooltip: "The resource this report is about. Kyverno names the report after the resource's UID; use Subject to identify it." },
1284
1286
  { key: 'status', label: 'Status', width: 'w-32' },
1285
1287
  { key: 'pass', label: 'Pass', width: 'w-16' },
1286
1288
  { key: 'fail', label: 'Fail', width: 'w-16' },
@@ -1294,14 +1296,16 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1294
1296
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1295
1297
  { key: 'status', label: 'Status', width: 'w-24' },
1296
1298
  { key: 'action', label: 'Action', width: 'w-36', tooltip: 'Enforcement at admission — Enforce blocks, Audit reports; Background only or Inactive when admission is disabled' },
1297
- { key: 'rules', label: 'Rules', width: 'w-20' },
1299
+ { key: 'ruleTypes', label: 'Types', width: 'w-40', tooltip: 'Rule kinds this policy declares. A policy can carry several, and the mix decides what it can do to a request.' },
1300
+ { key: 'rules', label: 'Rules', width: 'w-20', defaultVisible: false },
1298
1301
  { key: 'age', label: 'Age', width: 'w-24' },
1299
1302
  ],
1300
1303
  clusterpolicies: [
1301
1304
  { key: 'name', label: 'Name' },
1302
1305
  { key: 'status', label: 'Status', width: 'w-24' },
1303
1306
  { key: 'action', label: 'Action', width: 'w-36', tooltip: 'Enforcement at admission — Enforce blocks, Audit reports; Background only or Inactive when admission is disabled' },
1304
- { key: 'rules', label: 'Rules', width: 'w-20' },
1307
+ { key: 'ruleTypes', label: 'Types', width: 'w-40', tooltip: 'Rule kinds this policy declares. A policy can carry several, and the mix decides what it can do to a request.' },
1308
+ { key: 'rules', label: 'Rules', width: 'w-20', defaultVisible: false },
1305
1309
  { key: 'age', label: 'Age', width: 'w-24' },
1306
1310
  ],
1307
1311
  // Kyverno modern CEL family (policies.kyverno.io). "Enforcement" is the
@@ -1382,7 +1386,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1382
1386
  // getModernKyvernoPolicyStatus), so a second one would be redundant.
1383
1387
  deletingpolicies: [
1384
1388
  { key: 'name', label: 'Name' },
1385
- { key: 'lastRun', label: 'Last Run', width: 'w-28', tooltip: 'When the schedule last fired. A scheduled policy that has never run is the failure worth catching — Kyverno rejects uncompilable policies at admission, so a broken one never exists to flag.' },
1389
+ { key: 'lastRun', label: 'Last Run', width: 'w-28', tooltip: 'Last execution time reported by Kyverno. A timestamp does not prove every matching resource was deleted.' },
1386
1390
  { key: 'schedule', label: 'Schedule', width: 'w-32', tooltip: 'Cron schedule on which matched resources are deleted' },
1387
1391
  { key: 'appliesTo', label: 'Deletes', width: 'min-w-40' },
1388
1392
  { key: 'rules', label: 'Conditions', width: 'w-28', tooltip: 'CEL conditions narrowing what is deleted; none means every matched resource' },
@@ -1391,7 +1395,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1391
1395
  namespaceddeletingpolicies: [
1392
1396
  { key: 'name', label: 'Name' },
1393
1397
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1394
- { key: 'lastRun', label: 'Last Run', width: 'w-28', tooltip: 'When the schedule last fired. A scheduled policy that has never run is the failure worth catching — Kyverno rejects uncompilable policies at admission, so a broken one never exists to flag.' },
1398
+ { key: 'lastRun', label: 'Last Run', width: 'w-28', tooltip: 'Last execution time reported by Kyverno. A timestamp does not prove every matching resource was deleted.' },
1395
1399
  { key: 'schedule', label: 'Schedule', width: 'w-32' },
1396
1400
  { key: 'appliesTo', label: 'Deletes', width: 'min-w-40' },
1397
1401
  { key: 'rules', label: 'Conditions', width: 'w-28' },
@@ -1408,20 +1412,22 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1408
1412
  { key: 'name', label: 'Name' },
1409
1413
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1410
1414
  { key: 'schedule', label: 'Schedule', width: 'w-32' },
1415
+ { key: 'lastRun', label: 'Last Run', width: 'w-28', tooltip: 'Time since the controller last recorded an execution. A dash means it has not recorded one.' },
1411
1416
  { key: 'appliesTo', label: 'Deletes', width: 'min-w-40' },
1412
1417
  { key: 'age', label: 'Age', width: 'w-24' },
1413
1418
  ],
1414
1419
  clustercleanuppolicies: [
1415
1420
  { key: 'name', label: 'Name' },
1416
1421
  { key: 'schedule', label: 'Schedule', width: 'w-32' },
1422
+ { key: 'lastRun', label: 'Last Run', width: 'w-28', tooltip: 'Time since the controller last recorded an execution. A dash means it has not recorded one.' },
1417
1423
  { key: 'appliesTo', label: 'Deletes', width: 'min-w-40' },
1418
1424
  { key: 'age', label: 'Age', width: 'w-24' },
1419
1425
  ],
1420
1426
  grpcroutes: [
1421
1427
  { key: 'name', label: 'Name' },
1422
1428
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1423
- { key: 'status', label: 'Status', width: 'w-28' },
1424
- { key: 'hostnames', label: 'Hostnames', width: 'w-48' },
1429
+ { key: 'status', label: 'Status', width: 'w-28', tooltip: 'Accepted = controllers accepted this route and confirmed it can use the backends and other objects it names. Traffic has not been tested. Degraded or Not Accepted: open the route to see which Gateway or backend failed. Pending or Unknown: check the named Gateways or Services and their controllers.' },
1430
+ { key: 'hostnames', label: 'Hostnames', width: 'w-48', tooltip: "gRPC hostnames this route matches. Any means the route adds no hostname restriction; the Gateway listener and the route's other matching rules still apply." },
1425
1431
  { key: 'parents', label: 'Gateways', width: 'w-36' },
1426
1432
  { key: 'backends', label: 'Backends', width: 'w-48', tooltip: 'Backend services receiving traffic' },
1427
1433
  { key: 'rules', label: 'Rules', width: 'w-20' },
@@ -1430,7 +1436,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1430
1436
  tcproutes: [
1431
1437
  { key: 'name', label: 'Name' },
1432
1438
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1433
- { key: 'status', label: 'Status', width: 'w-28' },
1439
+ { key: 'status', label: 'Status', width: 'w-28', tooltip: 'Accepted = controllers accepted this route and confirmed it can use the backends and other objects it names. Traffic has not been tested. Degraded or Not Accepted: open the route to see which Gateway or backend failed. Pending or Unknown: check the named Gateways or Services and their controllers.' },
1434
1440
  { key: 'parents', label: 'Gateways', width: 'w-36' },
1435
1441
  { key: 'backends', label: 'Backends', width: 'w-48', tooltip: 'Backend services receiving traffic' },
1436
1442
  { key: 'rules', label: 'Rules', width: 'w-20' },
@@ -1439,8 +1445,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1439
1445
  tlsroutes: [
1440
1446
  { key: 'name', label: 'Name' },
1441
1447
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1442
- { key: 'status', label: 'Status', width: 'w-28' },
1443
- { key: 'hostnames', label: 'Hostnames', width: 'w-48', tooltip: 'SNI hostnames for TLS routing' },
1448
+ { key: 'status', label: 'Status', width: 'w-28', tooltip: 'Accepted = controllers accepted this route and confirmed it can use the backends and other objects it names. Traffic has not been tested. Degraded or Not Accepted: open the route to see which Gateway or backend failed. Pending or Unknown: check the named Gateways or Services and their controllers.' },
1449
+ { key: 'hostnames', label: 'Hostnames', width: 'w-48', tooltip: 'TLS SNI names requested by this route. Any means no route-level hostname restriction; the Gateway listener still applies.' },
1444
1450
  { key: 'parents', label: 'Gateways', width: 'w-36' },
1445
1451
  { key: 'backends', label: 'Backends', width: 'w-48', tooltip: 'Backend services receiving traffic' },
1446
1452
  { key: 'rules', label: 'Rules', width: 'w-20' },
@@ -1457,7 +1463,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1457
1463
  workflowtemplates: [
1458
1464
  { key: 'name', label: 'Name' },
1459
1465
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1460
- { key: 'entrypoint', label: 'Entrypoint', width: 'w-36' },
1466
+ { key: 'entrypoint', label: 'Entrypoint', width: 'w-36', tooltip: 'Default template invoked when submitted as a workflow; submission may override it' },
1461
1467
  { key: 'templates', label: 'Templates', width: 'w-24' },
1462
1468
  { key: 'age', label: 'Age', width: 'w-24' },
1463
1469
  ],
@@ -1472,29 +1478,28 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1472
1478
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1473
1479
  { key: 'policyTypes', label: 'Types', width: 'w-28' },
1474
1480
  { key: 'selector', label: 'Pod Selector', width: 'w-48' },
1475
- { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1481
+ { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress allow-rule counts. For a direction listed in Types, 0 means this policy allows none; other policies can still allow traffic.' },
1476
1482
  { key: 'age', label: 'Age', width: 'w-24' },
1477
1483
  ],
1478
1484
  caliconetworkpolicies: [
1479
1485
  { key: 'name', label: 'Name' },
1480
1486
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1481
1487
  { key: 'selector', label: 'Selector', width: 'w-48' },
1482
- { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
1483
- { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
1484
- { key: 'tier', label: 'Tier', width: 'w-28' },
1485
- { key: 'order', label: 'Order', width: 'w-24' },
1486
- { key: 'types', label: 'Types', width: 'w-28' },
1488
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false, tooltip: 'Endpoints must match both this service-account selector and the endpoint Selector; matching either one alone is insufficient.' },
1489
+ { key: 'tier', label: 'Tier', width: 'w-28', tooltip: 'Tier precedence applies before policy Order. An earlier tier can allow or deny traffic before this policy is reached.' },
1490
+ { key: 'order', label: 'Order', width: 'w-24', tooltip: 'Evaluation order within the tier — lowest runs first. Unset runs after every ordered policy.' },
1491
+ { key: 'types', label: 'Types', width: 'w-28', tooltip: "Traffic directions this policy governs. If no applicable policy in the tier takes action, the tier's default action applies (Deny unless configured as Pass)." },
1487
1492
  { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1488
1493
  { key: 'age', label: 'Age', width: 'w-24' },
1489
1494
  ],
1490
1495
  calicoglobalnetworkpolicies: [
1491
1496
  { key: 'name', label: 'Name' },
1492
1497
  { key: 'selector', label: 'Selector', width: 'w-48' },
1493
- { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
1494
- { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
1495
- { key: 'tier', label: 'Tier', width: 'w-28' },
1496
- { key: 'order', label: 'Order', width: 'w-24' },
1497
- { key: 'types', label: 'Types', width: 'w-28' },
1498
+ { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false, tooltip: 'Adds a namespace restriction to the endpoint Selector. Unset adds no namespace restriction; the other selectors still apply.' },
1499
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false, tooltip: 'Endpoints must match both this service-account selector and the endpoint Selector; matching either one alone is insufficient.' },
1500
+ { key: 'tier', label: 'Tier', width: 'w-28', tooltip: 'Tier precedence applies before policy Order. An earlier tier can allow or deny traffic before this policy is reached.' },
1501
+ { key: 'order', label: 'Order', width: 'w-24', tooltip: 'Evaluation order within the tier — lowest runs first. Unset runs after every ordered policy.' },
1502
+ { key: 'types', label: 'Types', width: 'w-28', tooltip: "Traffic directions this policy governs. If no applicable policy in the tier takes action, the tier's default action applies (Deny unless configured as Pass)." },
1498
1503
  { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1499
1504
  { key: 'age', label: 'Age', width: 'w-24' },
1500
1505
  ],
@@ -1502,58 +1507,57 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1502
1507
  { key: 'name', label: 'Name' },
1503
1508
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1504
1509
  { key: 'selector', label: 'Selector', width: 'w-48' },
1505
- { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
1506
- { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
1507
- { key: 'tier', label: 'Tier', width: 'w-28' },
1508
- { key: 'order', label: 'Order', width: 'w-24' },
1509
- { key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
1510
- { key: 'types', label: 'Types', width: 'w-28' },
1510
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false, tooltip: 'Endpoints must match both this service-account selector and the endpoint Selector; matching either one alone is insufficient.' },
1511
+ { key: 'tier', label: 'Tier', width: 'w-28', tooltip: 'Tier precedence applies before policy Order. An earlier tier can allow or deny traffic before this policy is reached.' },
1512
+ { key: 'order', label: 'Order', width: 'w-24', tooltip: 'Evaluation order within the tier — lowest runs first. Unset runs after every ordered policy.' },
1513
+ { key: 'stagedAction', label: 'Staged Action', width: 'w-32', tooltip: 'Staged policies do not enforce traffic. Set and Learn preview rules; Delete stages removal of the matching enforced policy; Ignore is skipped. Omitted means Set.' },
1514
+ { key: 'types', label: 'Types', width: 'w-28', tooltip: "Traffic directions this policy governs. If no applicable policy in the tier takes action, the tier's default action applies (Deny unless configured as Pass)." },
1511
1515
  { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1512
1516
  { key: 'age', label: 'Age', width: 'w-24' },
1513
1517
  ],
1514
1518
  calicostagedglobalnetworkpolicies: [
1515
1519
  { key: 'name', label: 'Name' },
1516
1520
  { key: 'selector', label: 'Selector', width: 'w-48' },
1517
- { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
1518
- { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
1519
- { key: 'tier', label: 'Tier', width: 'w-28' },
1520
- { key: 'order', label: 'Order', width: 'w-24' },
1521
- { key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
1522
- { key: 'types', label: 'Types', width: 'w-28' },
1521
+ { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false, tooltip: 'Adds a namespace restriction to the endpoint Selector. Unset adds no namespace restriction; the other selectors still apply.' },
1522
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false, tooltip: 'Endpoints must match both this service-account selector and the endpoint Selector; matching either one alone is insufficient.' },
1523
+ { key: 'tier', label: 'Tier', width: 'w-28', tooltip: 'Tier precedence applies before policy Order. An earlier tier can allow or deny traffic before this policy is reached.' },
1524
+ { key: 'order', label: 'Order', width: 'w-24', tooltip: 'Evaluation order within the tier — lowest runs first. Unset runs after every ordered policy.' },
1525
+ { key: 'stagedAction', label: 'Staged Action', width: 'w-32', tooltip: 'Staged policies do not enforce traffic. Set and Learn preview rules; Delete stages removal of the matching enforced policy; Ignore is skipped. Omitted means Set.' },
1526
+ { key: 'types', label: 'Types', width: 'w-28', tooltip: "Traffic directions this policy governs. If no applicable policy in the tier takes action, the tier's default action applies (Deny unless configured as Pass)." },
1523
1527
  { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1524
1528
  { key: 'age', label: 'Age', width: 'w-24' },
1525
1529
  ],
1526
1530
  calicoippools: [
1527
1531
  { key: 'name', label: 'Name' },
1528
1532
  { key: 'cidr', label: 'CIDR', width: 'w-44' },
1529
- { key: 'blockSize', label: 'Block Size', width: 'w-28' },
1530
- { key: 'encapsulation', label: 'Encapsulation', width: 'w-36' },
1531
- { key: 'natOutgoing', label: 'NAT Outgoing', width: 'w-32' },
1533
+ { key: 'blockSize', label: 'Block Size', width: 'w-28', tooltip: 'CIDR prefix of each allocation block. A node can hold several blocks, so this is not a per-node pod cap. Unset means /26 for IPv4, /122 for IPv6.' },
1534
+ { key: 'encapsulation', label: 'Encapsulation', width: 'w-36', tooltip: 'Overlay used for pod traffic between nodes. None routes pod addresses directly; CrossSubnet tunnels only between nodes on different subnets.' },
1535
+ { key: 'natOutgoing', label: 'NAT Outgoing', width: 'w-32', tooltip: 'When enabled, outgoing traffic from this pool is masqueraded for destinations outside all Calico IP pools.' },
1532
1536
  { key: 'disabled', label: 'Disabled', width: 'w-28' },
1533
- { key: 'allowedUses', label: 'Allowed Uses', width: 'w-40', defaultVisible: false },
1537
+ { key: 'allowedUses', label: 'Allowed Uses', width: 'w-40', defaultVisible: false, tooltip: 'Pools without Workload are skipped for automatic pod IP allocation. Omitted or empty allows Workload and Tunnel; existing allocations are unchanged.' },
1534
1538
  { key: 'nodeSelector', label: 'Node Selector', width: 'w-48', defaultVisible: false },
1535
1539
  { key: 'age', label: 'Age', width: 'w-24' },
1536
1540
  ],
1537
1541
  calicohostendpoints: [
1538
1542
  { key: 'name', label: 'Name' },
1539
1543
  { key: 'node', label: 'Node', width: 'w-48' },
1540
- { key: 'interfaceName', label: 'Interface', width: 'w-32' },
1544
+ { key: 'interfaceName', label: 'Interface', width: 'w-32', tooltip: '* applies policy to all host interfaces. When unset, Calico identifies the interface using Expected IPs.' },
1541
1545
  { key: 'expectedIPs', label: 'Expected IPs', width: 'w-48' },
1542
- { key: 'profiles', label: 'Profiles', width: 'w-48', defaultVisible: false },
1546
+ { key: 'profiles', label: 'Profiles', width: 'w-48', defaultVisible: false, tooltip: "Profiles apply only if policy evaluation reaches them. A tier's implicit deny stops traffic before profiles, even when no rule matched." },
1543
1547
  { key: 'age', label: 'Age', width: 'w-24' },
1544
1548
  ],
1545
1549
  calicotiers: [
1546
1550
  { key: 'name', label: 'Name' },
1547
- { key: 'order', label: 'Order', width: 'w-24' },
1548
- { key: 'defaultAction', label: 'Default Action', width: 'w-36' },
1551
+ { key: 'order', label: 'Order', width: 'w-24', tooltip: 'Order this tier is evaluated in — lowest first. Unset runs after every ordered tier.' },
1552
+ { key: 'defaultAction', label: 'Default Action', width: 'w-36', tooltip: 'If this tier selects the endpoint and traffic direction but no rule decides the packet, Deny drops it and Pass continues evaluation. Tiers with no applicable policy are skipped.' },
1549
1553
  { key: 'age', label: 'Age', width: 'w-24' },
1550
1554
  ],
1551
1555
  calicostagedkubernetesnetworkpolicies: [
1552
1556
  { key: 'name', label: 'Name' },
1553
1557
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1554
1558
  { key: 'selector', label: 'Pod Selector', width: 'w-48' },
1555
- { key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
1556
- { key: 'types', label: 'Types', width: 'w-28' },
1559
+ { key: 'stagedAction', label: 'Staged Action', width: 'w-32', tooltip: 'Staged policies do not enforce traffic. Set and Learn preview rules; Delete stages removal of the matching enforced policy; Ignore is skipped. Omitted means Set.' },
1560
+ { key: 'types', label: 'Types', width: 'w-28', tooltip: 'An explicitly listed direction can have zero rules. When policyTypes is omitted, Ingress is included and Egress is added when egress rules exist.' },
1557
1561
  { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1558
1562
  { key: 'age', label: 'Age', width: 'w-24' },
1559
1563
  ],
@@ -1561,7 +1565,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1561
1565
  { key: 'name', label: 'Name' },
1562
1566
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1563
1567
  { key: 'status', label: 'Status', width: 'w-24' },
1564
- { key: 'budget', label: 'Budget', width: 'w-36' },
1568
+ { key: 'budget', label: 'Budget', width: 'w-36', tooltip: 'min keeps at least this many pods available; max unavail permits at most this many unavailable during voluntary evictions; percentages use the expected pod count' },
1565
1569
  { key: 'healthy', label: 'Healthy', width: 'w-24' },
1566
1570
  { key: 'allowed', label: 'Allowed', width: 'w-24', tooltip: 'Number of disruptions currently allowed' },
1567
1571
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -1569,7 +1573,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1569
1573
  serviceaccounts: [
1570
1574
  { key: 'name', label: 'Name' },
1571
1575
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1572
- { key: 'automount', label: 'Automount', width: 'w-32', tooltip: 'Whether token is automatically mounted in pods' },
1576
+ { key: 'automount', label: 'Automount', width: 'w-32', tooltip: 'Default automatic API-token mounting for pods using this ServiceAccount. A pod can override this setting. Disable it for workloads that do not need Kubernetes API credentials.' },
1573
1577
  { key: 'secrets', label: 'Secrets', width: 'w-24' },
1574
1578
  { key: 'age', label: 'Age', width: 'w-24' },
1575
1579
  ],
@@ -1607,12 +1611,12 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1607
1611
  { key: 'name', label: 'Name' },
1608
1612
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
1609
1613
  { key: 'holder', label: 'Holder', width: 'w-48' },
1610
- { key: 'renewTime', label: 'Last Renewed', width: 'w-32' },
1614
+ { key: 'renewTime', label: 'Last Renewed', width: 'w-32', tooltip: 'Time since the recorded renewal; red means it exceeds leaseDurationSeconds. Check holder health and API connectivity' },
1611
1615
  { key: 'age', label: 'Age', width: 'w-24' },
1612
1616
  ],
1613
1617
  priorityclasses: [
1614
1618
  { key: 'name', label: 'Name' },
1615
- { key: 'value', label: 'Value', width: 'w-24' },
1619
+ { key: 'value', label: 'Value', width: 'w-24', tooltip: 'Higher values receive scheduling preference; Pods can still be preempted by Pods with higher priority' },
1616
1620
  { key: 'globalDefault', label: 'Global Default', width: 'w-36' },
1617
1621
  { key: 'preemptionPolicy', label: 'Preemption', width: 'w-32' },
1618
1622
  { key: 'description', label: 'Description', width: 'w-64' },
@@ -1626,7 +1630,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1626
1630
  mutatingwebhookconfigurations: [
1627
1631
  { key: 'name', label: 'Name' },
1628
1632
  { key: 'webhooks', label: 'Webhooks', width: 'w-28' },
1629
- { key: 'failurePolicy', label: 'Failure Policy', width: 'w-36' },
1633
+ { key: 'failurePolicy', label: 'Failure Policy', width: 'w-36', tooltip: 'If a matching webhook call errors or times out, Fail rejects the API request; Ignore continues without that webhook. This describes failure handling, not current webhook health.' },
1630
1634
  { key: 'target', label: 'Target', width: 'w-48' },
1631
1635
  { key: 'age', label: 'Age', width: 'w-24' },
1632
1636
  ],
@@ -1672,7 +1676,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1672
1676
  { key: 'name', label: 'Name' },
1673
1677
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1674
1678
  { key: 'url', label: 'URL', width: 'w-64' },
1675
- { key: 'type', label: 'Type', width: 'w-20', tooltip: 'default (Helm) or oci' },
1679
+ { key: 'type', label: 'Type', width: 'w-20', tooltip: 'default: Flux fetches a Helm repository index. oci: a registry reference with no reconciliation status of its own' },
1676
1680
  { key: 'status', label: 'Status', width: 'w-24' },
1677
1681
  { key: 'age', label: 'Age', width: 'w-24' },
1678
1682
  ],
@@ -1684,7 +1688,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1684
1688
  { key: 'status', label: 'Status', width: 'w-24' },
1685
1689
  { key: 'revision', label: 'Revision', width: 'w-48', tooltip: 'Applied git revision' },
1686
1690
  { key: 'inventory', label: 'Resources', width: 'w-24', tooltip: 'Number of managed resources' },
1687
- { key: 'lastUpdated', label: 'Last Updated', width: 'w-28', tooltip: 'Time since last successful reconciliation' },
1691
+ { key: 'lastUpdated', label: 'Last Updated', width: 'w-28', tooltip: 'Time since the last recorded reconciliation attempt; falls back to the last Ready transition. Neither guarantees success.' },
1688
1692
  { key: 'age', label: 'Age', width: 'w-24' },
1689
1693
  ],
1690
1694
  helmreleases: [
@@ -1712,8 +1716,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1712
1716
  applications: [
1713
1717
  { key: 'name', label: 'Name' },
1714
1718
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1715
- { key: 'project', label: 'Project', width: 'w-28' },
1716
- { key: 'sync', label: 'Sync', width: 'w-24' },
1719
+ { key: 'project', label: 'Project', width: 'w-28', tooltip: 'AppProject the app belongs to — bounds which repos and clusters it may deploy to' },
1720
+ { key: 'sync', label: 'Sync', width: 'w-24', tooltip: "Whether live resources match Argo's desired manifests; Synced does not mean Healthy" },
1717
1721
  { key: 'health', label: 'Health', width: 'w-24' },
1718
1722
  { key: 'repo', label: 'Repository', width: 'w-48' },
1719
1723
  { key: 'age', label: 'Age', width: 'w-24' },
@@ -1740,7 +1744,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1740
1744
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1741
1745
  { key: 'container', label: 'Container', width: 'w-28' },
1742
1746
  { key: 'image', label: 'Image', width: 'w-48' },
1743
- { key: 'critical', label: 'C', width: 'w-12', tooltip: 'Critical vulnerabilities' },
1747
+ { key: 'critical', label: 'C', width: 'w-12', tooltip: 'Critical-severity vulnerabilities. C/H/M/L = Critical, High, Medium, Low.' },
1744
1748
  { key: 'high', label: 'H', width: 'w-12', tooltip: 'High vulnerabilities' },
1745
1749
  { key: 'medium', label: 'M', width: 'w-12', tooltip: 'Medium vulnerabilities' },
1746
1750
  { key: 'low', label: 'L', width: 'w-12', tooltip: 'Low vulnerabilities' },
@@ -1750,7 +1754,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1750
1754
  { key: 'name', label: 'Name' },
1751
1755
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1752
1756
  { key: 'status', label: 'Status', width: 'w-28' },
1753
- { key: 'critical', label: 'C', width: 'w-12', tooltip: 'Critical findings' },
1757
+ { key: 'critical', label: 'C', width: 'w-12', tooltip: 'Critical-severity failed checks. C/H/M/L = Critical, High, Medium, Low.' },
1754
1758
  { key: 'high', label: 'H', width: 'w-12', tooltip: 'High findings' },
1755
1759
  { key: 'medium', label: 'M', width: 'w-12', tooltip: 'Medium findings' },
1756
1760
  { key: 'low', label: 'L', width: 'w-12', tooltip: 'Low findings' },
@@ -1761,7 +1765,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1761
1765
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1762
1766
  { key: 'container', label: 'Container', width: 'w-28' },
1763
1767
  { key: 'image', label: 'Image', width: 'w-48' },
1764
- { key: 'critical', label: 'C', width: 'w-12', tooltip: 'Critical secrets' },
1768
+ { key: 'critical', label: 'C', width: 'w-12', tooltip: 'Critical-severity exposed secrets. C/H/M/L = Critical, High, Medium, Low.' },
1765
1769
  { key: 'high', label: 'H', width: 'w-12', tooltip: 'High secrets' },
1766
1770
  { key: 'medium', label: 'M', width: 'w-12', tooltip: 'Medium secrets' },
1767
1771
  { key: 'low', label: 'L', width: 'w-12', tooltip: 'Low secrets' },
@@ -1797,13 +1801,15 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1797
1801
  sbomreports: [
1798
1802
  { key: 'name', label: 'Name' },
1799
1803
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1804
+ { key: 'image', label: 'Image', width: 'w-48', tooltip: 'Image the SBOM describes. The container name alone does not identify what was scanned.' },
1800
1805
  { key: 'container', label: 'Container', width: 'w-28' },
1801
- { key: 'components', label: 'Components', width: 'w-24' },
1806
+ { key: 'components', label: 'Components', width: 'w-24', tooltip: 'Packages and libraries found in the image' },
1802
1807
  { key: 'status', label: 'Status', width: 'w-28' },
1803
1808
  { key: 'age', label: 'Age', width: 'w-16' },
1804
1809
  ],
1805
1810
  clustersbomreports: [
1806
1811
  { key: 'name', label: 'Name' },
1812
+ { key: 'image', label: 'Image', width: 'w-48', tooltip: 'Image the SBOM describes. The container name alone does not identify what was scanned.' },
1807
1813
  { key: 'components', label: 'Components', width: 'w-24' },
1808
1814
  { key: 'status', label: 'Status', width: 'w-28' },
1809
1815
  { key: 'age', label: 'Age', width: 'w-16' },
@@ -1833,8 +1839,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1833
1839
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1834
1840
  { key: 'status', label: 'Status', width: 'w-24' },
1835
1841
  { key: 'store', label: 'Store', width: 'w-36' },
1836
- { key: 'provider', label: 'Provider', width: 'w-28' },
1837
- { key: 'refreshInterval', label: 'Refresh', width: 'w-24' },
1842
+ { key: 'refreshInterval', label: 'Refresh', width: 'w-24', tooltip: 'How often the value is re-pulled from the provider. A zero interval means it is fetched once and not updated afterward.' },
1838
1843
  { key: 'lastSync', label: 'Last Sync', width: 'w-28' },
1839
1844
  { key: 'age', label: 'Age', width: 'w-24' },
1840
1845
  ],
@@ -1865,10 +1870,10 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1865
1870
  { key: 'name', label: 'Name' },
1866
1871
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1867
1872
  { key: 'status', label: 'Status', width: 'w-36' },
1868
- { key: 'storageLocation', label: 'Storage', width: 'w-36' },
1873
+ { key: 'storageLocation', label: 'Storage', width: 'w-36', tooltip: "The backup's storage location. A completed backup still needs this location reachable to restore from." },
1869
1874
  { key: 'namespaces', label: 'Scope', width: 'w-24', tooltip: 'Included namespaces (* = all)' },
1870
1875
  { key: 'duration', label: 'Duration', width: 'w-24' },
1871
- { key: 'expiry', label: 'Expires', width: 'w-24' },
1876
+ { key: 'expiry', label: 'Expires', width: 'w-24', tooltip: "When Velero's retention makes this backup eligible for cleanup. Expired means Velero intends to delete it, not that it is gone — and not that a restore will work." },
1872
1877
  { key: 'errors', label: 'Errors', width: 'w-28' },
1873
1878
  { key: 'age', label: 'Age', width: 'w-24' },
1874
1879
  ],
@@ -1912,7 +1917,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1912
1917
  { key: 'name', label: 'Name' },
1913
1918
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1914
1919
  { key: 'status', label: 'Status', width: 'w-36' },
1915
- { key: 'repositoryType', label: 'Type', width: 'w-24' },
1920
+ { key: 'repositoryType', label: 'Type', width: 'w-24', tooltip: 'kopia or restic. Restic is deprecated — Velero 1.17 stopped creating restic backups and 1.19 drops restic restore.' },
1921
+ { key: 'volumeNamespace', label: 'Volume NS', width: 'w-36', tooltip: 'Namespace whose volumes this repository stores. Distinct from the namespace the repository object lives in.' },
1916
1922
  { key: 'age', label: 'Age', width: 'w-24' },
1917
1923
  ],
1918
1924
  backupstoragelocations: [
@@ -1921,8 +1927,8 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1921
1927
  { key: 'status', label: 'Status', width: 'w-36' },
1922
1928
  { key: 'provider', label: 'Provider', width: 'w-24' },
1923
1929
  { key: 'bucket', label: 'Bucket', width: 'w-40' },
1924
- { key: 'default', label: 'Default', width: 'w-24' },
1925
- { key: 'lastValidation', label: 'Validated', width: 'w-28' },
1930
+ { key: 'default', label: 'Default', width: 'w-24', tooltip: 'Whether this location is marked default. A backup naming no storage location is written to the default one.' },
1931
+ { key: 'lastValidation', label: 'Validated', width: 'w-28', tooltip: 'Time since Velero last validated this location. An old value means availability has not been checked recently; validation may be disabled or configured to run less often.' },
1926
1932
  { key: 'age', label: 'Age', width: 'w-24' },
1927
1933
  ],
1928
1934
  // ============================================================================
@@ -1942,7 +1948,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1942
1948
  { key: 'status', label: 'Status', width: 'w-44' },
1943
1949
  { key: 'instances', label: 'Instances', width: 'w-28', tooltip: 'Ready/Total' },
1944
1950
  { key: 'primary', label: 'Primary', width: 'w-36' },
1945
- { key: 'image', label: 'Image', width: 'w-28' },
1951
+ { key: 'image', label: 'Image', width: 'w-28', tooltip: 'Configured image tag, or the operator-resolved image for a catalog-backed cluster. This does not confirm that every instance has finished updating.' },
1946
1952
  // No Storage column: it rendered spec.storage.size, the configured REQUEST.
1947
1953
  // The useful number is actual usage (kubectl cnpg status shows "Size: 158M")
1948
1954
  // and that isn't in the CR. A plausible-but-wrong-meaning number is worse
@@ -1960,7 +1966,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1960
1966
  updaterequests: [
1961
1967
  { key: 'name', label: 'Name' },
1962
1968
  { key: 'namespace', label: 'Namespace', width: 'w-32' },
1963
- { key: 'status', label: 'State', width: 'w-28' },
1969
+ { key: 'status', label: 'State', width: 'w-28', tooltip: 'Pending work Kyverno has queued but not yet applied; Failed is work it tried and could not.' },
1964
1970
  { key: 'type', label: 'Type', width: 'w-24' },
1965
1971
  { key: 'policy', label: 'Policy', width: 'w-52' },
1966
1972
  { key: 'triggers', label: 'Triggered By', width: 'w-56' },
@@ -1971,7 +1977,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1971
1977
  { key: 'namespace', label: 'Namespace', width: 'w-32' },
1972
1978
  { key: 'status', label: 'Outcome', width: 'w-28' },
1973
1979
  { key: 'subject', label: 'About', width: 'w-56' },
1974
- { key: 'source', label: 'Produced By', width: 'w-36' },
1980
+ { key: 'source', label: 'Produced By', width: 'w-36', tooltip: 'Admission: evaluated during an API admission request. Background scan: evaluated against existing resources.' },
1975
1981
  { key: 'results', label: 'Results', width: 'w-24' },
1976
1982
  { key: 'age', label: 'Age', width: 'w-24' },
1977
1983
  ],
@@ -1986,7 +1992,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1986
1992
  cnpgdatabases: [
1987
1993
  { key: 'name', label: 'Name' },
1988
1994
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1989
- { key: 'status', label: 'Applied', width: 'w-32' },
1995
+ { key: 'status', label: 'Applied', width: 'w-32', tooltip: 'Whether the operator has applied this to PostgreSQL. Pending means it has not reconciled it yet — not that it failed.' },
1990
1996
  { key: 'cluster', label: 'Cluster', width: 'w-36' },
1991
1997
  { key: 'target', label: 'Database', width: 'w-40' },
1992
1998
  { key: 'message', label: 'Message', width: 'w-64' },
@@ -2082,24 +2088,24 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2082
2088
  { key: 'name', label: 'Name' },
2083
2089
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2084
2090
  { key: 'status', label: 'Status', width: 'w-24' },
2085
- { key: 'hosts', label: 'Hosts', width: 'w-48' },
2086
- { key: 'gateways', label: 'Gateways', width: 'w-40' },
2091
+ { key: 'hosts', label: 'Hosts', width: 'w-48', tooltip: 'Request hostnames this VirtualService matches — not the backends it routes to' },
2092
+ { key: 'gateways', label: 'Gateways', width: 'w-40', tooltip: "Where these routing rules apply. '-' means the default mesh gateway — sidecar traffic; individual route matches can override this." },
2087
2093
  { key: 'routes', label: 'Routes', width: 'w-20', tooltip: 'HTTP + TCP + TLS routes' },
2088
2094
  { key: 'age', label: 'Age', width: 'w-24' },
2089
2095
  ],
2090
2096
  destinationrules: [
2091
2097
  { key: 'name', label: 'Name' },
2092
2098
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2093
- { key: 'status', label: 'Status', width: 'w-24' },
2094
- { key: 'host', label: 'Host', width: 'w-48' },
2095
- { key: 'subsets', label: 'Subsets', width: 'w-20' },
2096
- { key: 'loadBalancer', label: 'LB Policy', width: 'w-28' },
2099
+ { key: 'status', label: 'Status', width: 'w-24', defaultVisible: false },
2100
+ { key: 'host', label: 'Host', width: 'w-48', tooltip: "The service this rule's traffic policy applies to" },
2101
+ { key: 'subsets', label: 'Subsets', width: 'w-20', tooltip: 'Named backend groups that routes can select, often for canary releases. Defining subsets does not send traffic to them.' },
2102
+ { key: 'tlsMode', label: 'Client TLS', width: 'w-28', tooltip: 'Declared client-side TLS mode for this host at the rule level. Subset and port policies can override it; this does not establish the effective mTLS posture.' },
2103
+ { key: 'loadBalancer', label: 'LB Policy', width: 'w-28', tooltip: "Load balancing algorithm for this host. A dash means Istio's mesh default applies (LEAST_REQUEST since Istio 1.14). Subset and port policies can differ." },
2097
2104
  { key: 'age', label: 'Age', width: 'w-24' },
2098
2105
  ],
2099
2106
  serviceentries: [
2100
2107
  { key: 'name', label: 'Name' },
2101
2108
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2102
- { key: 'status', label: 'Status', width: 'w-24' },
2103
2109
  { key: 'hosts', label: 'Hosts', width: 'w-48' },
2104
2110
  { key: 'location', label: 'Location', width: 'w-28' },
2105
2111
  { key: 'ports', label: 'Ports', width: 'w-32' },
@@ -2108,18 +2114,17 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2108
2114
  peerauthentications: [
2109
2115
  { key: 'name', label: 'Name' },
2110
2116
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2111
- { key: 'status', label: 'Status', width: 'w-24' },
2112
- { key: 'mode', label: 'mTLS Mode', width: 'w-28' },
2113
- { key: 'selector', label: 'Selector', width: 'w-48' },
2117
+ { key: 'mode', label: 'mTLS Mode', width: 'w-28', tooltip: 'Incoming connection policy: STRICT requires mTLS; PERMISSIVE also accepts plaintext; DISABLE turns mTLS off (unsupported in ambient). UNSET inherits the broader policy, defaulting to PERMISSIVE. Port-level overrides can differ.' },
2118
+ { key: 'selector', label: 'Selector', width: 'w-48', tooltip: 'Selects workloads in this namespace. No selector defines a namespace default, or a mesh default in the root namespace; more specific policies can override it.' },
2114
2119
  { key: 'age', label: 'Age', width: 'w-24' },
2115
2120
  ],
2116
2121
  authorizationpolicies: [
2117
2122
  { key: 'name', label: 'Name' },
2118
2123
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2119
- { key: 'status', label: 'Status', width: 'w-24' },
2120
- { key: 'action', label: 'Action', width: 'w-24' },
2121
- { key: 'rules', label: 'Rules', width: 'w-20' },
2122
- { key: 'selector', label: 'Selector', width: 'w-48' },
2124
+ { key: 'status', label: 'Status', width: 'w-24', defaultVisible: false },
2125
+ { key: 'action', label: 'Action', width: 'w-24', tooltip: 'ALLOW contributes permitted matches; DENY blocks matches; AUDIT marks matches for a configured audit plugin; CUSTOM adds an external check. Other policies also affect the decision.' },
2126
+ { key: 'rules', label: 'Rules', width: 'w-20', tooltip: 'Rules are alternatives — zero rules match nothing, an empty rule matches everything. An ALLOW policy with zero rules permits nothing on its own.' },
2127
+ { key: 'selector', label: 'Applies to', width: 'w-48', tooltip: 'Declared workload labels or resource attachments. With neither, scope is inherited — the namespace, or the whole mesh when the policy sits in the mesh root namespace.' },
2123
2128
  { key: 'age', label: 'Age', width: 'w-24' },
2124
2129
  ],
2125
2130
  // Knative Serving
@@ -2128,7 +2133,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2128
2133
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2129
2134
  { key: 'status', label: 'Status', width: 'w-28' },
2130
2135
  { key: 'url', label: 'URL', width: 'w-56' },
2131
- { key: 'latestRevision', label: 'Latest Revision', width: 'w-44' },
2136
+ { key: 'latestRevision', label: 'Latest Revision', width: 'w-44', tooltip: 'Newest revision that has reported Ready. A newer revision may still be starting or may have failed; this revision need not receive traffic.' },
2132
2137
  { key: 'traffic', label: 'Traffic', width: 'w-48' },
2133
2138
  { key: 'age', label: 'Age', width: 'w-24' },
2134
2139
  ],
@@ -2137,16 +2142,16 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2137
2142
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2138
2143
  { key: 'status', label: 'Status', width: 'w-28' },
2139
2144
  { key: 'latestCreated', label: 'Latest Created', width: 'w-48' },
2140
- { key: 'latestReady', label: 'Latest Ready', width: 'w-48' },
2145
+ { key: 'latestReady', label: 'Latest Ready', width: 'w-48', tooltip: 'Newest revision that has reported Ready. If it differs from Latest Created, the newer revision may still be starting or may have failed; inspect Status.' },
2141
2146
  { key: 'age', label: 'Age', width: 'w-24' },
2142
2147
  ],
2143
2148
  knativerevisions: [
2144
2149
  { key: 'name', label: 'Name' },
2145
2150
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2146
2151
  { key: 'status', label: 'Status', width: 'w-28' },
2147
- { key: 'routing', label: 'Traffic', width: 'w-20', tooltip: 'Whether this revision is receiving traffic' },
2152
+ { key: 'routing', label: 'Traffic', width: 'w-20', tooltip: 'Active: marked for Route traffic, even if scaled to zero. Reserve: not currently marked for Route traffic. Neither value measures incoming requests.' },
2148
2153
  { key: 'image', label: 'Image', width: 'w-48' },
2149
- { key: 'concurrency', label: 'Concurrency', width: 'w-32' },
2154
+ { key: 'concurrency', label: 'Concurrency', width: 'w-32', tooltip: "Hard limit on simultaneous requests handled by each pod; excess requests are buffered. Unlimited removes this cap, not autoscaling's target." },
2150
2155
  { key: 'age', label: 'Age', width: 'w-24' },
2151
2156
  ],
2152
2157
  knativeroutes: [
@@ -2171,7 +2176,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2171
2176
  { key: 'status', label: 'Status', width: 'w-28' },
2172
2177
  { key: 'broker', label: 'Broker', width: 'w-36' },
2173
2178
  { key: 'subscriber', label: 'Subscriber', width: 'w-48' },
2174
- { key: 'filter', label: 'Filter', width: 'w-48' },
2179
+ { key: 'filter', label: 'Filter', width: 'w-48', tooltip: 'Selects which broker events are eligible for delivery. No filtering admits every event; it does not guarantee successful delivery. Expression filters override attribute filters.' },
2175
2180
  { key: 'age', label: 'Age', width: 'w-24' },
2176
2181
  ],
2177
2182
  eventtypes: [
@@ -2188,7 +2193,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2188
2193
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2189
2194
  { key: 'status', label: 'Status', width: 'w-28' },
2190
2195
  { key: 'schedule', label: 'Schedule', width: 'w-36' },
2191
- { key: 'sink', label: 'Sink', width: 'w-48' },
2196
+ { key: 'sink', label: 'Sink', width: 'w-48', tooltip: 'Requested event destination. A displayed reference does not prove it resolved or received events; inspect conditions when delivery fails.' },
2192
2197
  { key: 'data', label: 'Data', width: 'w-36' },
2193
2198
  { key: 'age', label: 'Age', width: 'w-24' },
2194
2199
  ],
@@ -2245,14 +2250,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2245
2250
  { key: 'name', label: 'Name' },
2246
2251
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2247
2252
  { key: 'status', label: 'Status', width: 'w-28' },
2248
- { key: 'steps', label: 'Steps', width: 'w-16' },
2253
+ { key: 'steps', label: 'Steps', width: 'w-16', tooltip: "Stages events pass through in order; each stage's reply feeds the next" },
2249
2254
  { key: 'age', label: 'Age', width: 'w-24' },
2250
2255
  ],
2251
2256
  parallels: [
2252
2257
  { key: 'name', label: 'Name' },
2253
2258
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2254
2259
  { key: 'status', label: 'Status', width: 'w-28' },
2255
- { key: 'branches', label: 'Branches', width: 'w-20' },
2260
+ { key: 'branches', label: 'Branches', width: 'w-20', tooltip: 'Independent filter-and-subscriber pairs; every event is offered to all of them' },
2256
2261
  { key: 'age', label: 'Age', width: 'w-24' },
2257
2262
  ],
2258
2263
  // Knative Networking
@@ -2262,7 +2267,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2262
2267
  { key: 'status', label: 'Status', width: 'w-28' },
2263
2268
  { key: 'ingressClass', label: 'Class', width: 'w-24' },
2264
2269
  { key: 'hosts', label: 'Hosts', width: 'w-56' },
2265
- { key: 'visibility', label: 'Visibility', width: 'w-28' },
2270
+ { key: 'visibility', label: 'Visibility', width: 'w-28', tooltip: 'Requested exposure across all rules. ExternalIP means at least one rule requests external exposure; ClusterLocal means all rules request cluster-only exposure. This does not verify connectivity.' },
2266
2271
  { key: 'age', label: 'Age', width: 'w-24' },
2267
2272
  ],
2268
2273
  knativecertificates: [
@@ -2271,13 +2276,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2271
2276
  { key: 'status', label: 'Status', width: 'w-28' },
2272
2277
  { key: 'dnsNames', label: 'DNS Names', width: 'w-56' },
2273
2278
  { key: 'secretName', label: 'Secret', width: 'w-44' },
2279
+ { key: 'expires', label: 'Expires', width: 'w-28', tooltip: 'Expiry of the certificate in the named Secret, as the controller reported it.' },
2274
2280
  { key: 'age', label: 'Age', width: 'w-24' },
2275
2281
  ],
2276
2282
  serverlessservices: [
2277
2283
  { key: 'name', label: 'Name' },
2278
2284
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
2279
2285
  { key: 'status', label: 'Status', width: 'w-28' },
2280
- { key: 'mode', label: 'Mode', width: 'w-20' },
2286
+ { key: 'mode', label: 'Mode', width: 'w-20', tooltip: 'Requested backend mode: Serve selects revision pods; Proxy selects activators. Activators may remain involved with healthy pods for burst handling, so Proxy does not prove a cold start.' },
2281
2287
  { key: 'age', label: 'Age', width: 'w-24' },
2282
2288
  ],
2283
2289
  domainmappings: [
@@ -2291,7 +2297,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2291
2297
  ingressroutes: [
2292
2298
  { key: 'name', label: 'Name', width: 'min-w-40' },
2293
2299
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2294
- { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0' },
2300
+ { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0', tooltip: 'Entry points this route binds to. When unset, Traefik uses the default entry points from its static configuration.' },
2295
2301
  { key: 'hosts', label: 'Hosts', width: 'min-w-44' },
2296
2302
  { key: 'routes', label: 'Routes', width: 'min-w-48' },
2297
2303
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
@@ -2301,7 +2307,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2301
2307
  ingressroutetcps: [
2302
2308
  { key: 'name', label: 'Name', width: 'min-w-40' },
2303
2309
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2304
- { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0' },
2310
+ { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0', tooltip: 'Entry points this route binds to. When unset, Traefik uses the default entry points from its static configuration.' },
2305
2311
  { key: 'hosts', label: 'Hosts', width: 'min-w-44' },
2306
2312
  { key: 'routes', label: 'Routes', width: 'min-w-48' },
2307
2313
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
@@ -2311,7 +2317,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2311
2317
  ingressrouteudps: [
2312
2318
  { key: 'name', label: 'Name', width: 'min-w-40' },
2313
2319
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2314
- { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0' },
2320
+ { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0', tooltip: 'Entry points this route binds to. When unset it is attached to every available UDP entry point; asDefault has no effect on UDP.' },
2315
2321
  { key: 'routes', label: 'Routes', width: 'min-w-48' },
2316
2322
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2317
2323
  ],
@@ -2351,7 +2357,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2351
2357
  tlsoptions: [
2352
2358
  { key: 'name', label: 'Name', width: 'min-w-40' },
2353
2359
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2354
- { key: 'minVersion', label: 'Min TLS', width: 'w-24 shrink-0' },
2360
+ { key: 'minVersion', label: 'Min TLS', width: 'w-24 shrink-0', tooltip: "Lowest TLS version accepted; connections below it are rejected. A dash means the resource sets no minimum, so Traefik's own default applies." },
2355
2361
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2356
2362
  ],
2357
2363
  tlsstores: [
@@ -2367,7 +2373,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2367
2373
  { key: 'class', label: 'Class', width: 'w-32 shrink-0' },
2368
2374
  { key: 'cpReplicas', label: 'CP Ready', width: 'w-24 shrink-0', tooltip: 'Control plane replicas (ready/desired)' },
2369
2375
  { key: 'workerReplicas', label: 'Workers', width: 'w-20 shrink-0', tooltip: 'Worker replicas (ready/desired)' },
2370
- { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
2376
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0', tooltip: 'Lifecycle phase while the cluster is still provisioning or deleting; the Available/Ready condition once it has landed.' },
2371
2377
  { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
2372
2378
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2373
2379
  ],
@@ -2376,6 +2382,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2376
2382
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2377
2383
  { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
2378
2384
  { key: 'ready', label: 'Ready', width: 'w-20 shrink-0', tooltip: 'Ready replicas / desired' },
2385
+ { key: 'upToDate', label: 'Up-to-date', width: 'w-24', tooltip: 'Machines already running the current spec. Fewer than Ready means a rollout is still in progress.' },
2379
2386
  { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
2380
2387
  { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
2381
2388
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
@@ -2385,7 +2392,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2385
2392
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2386
2393
  { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
2387
2394
  { key: 'role', label: 'Role', width: 'w-28 shrink-0' },
2388
- { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
2395
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0', tooltip: 'Lifecycle milestone; Running does not guarantee that the node is currently Ready.' },
2389
2396
  { key: 'node', label: 'Node', width: 'w-32 shrink-0' },
2390
2397
  { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
2391
2398
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
@@ -2443,7 +2450,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2443
2450
  { key: 'name', label: 'Name', width: 'min-w-40' },
2444
2451
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2445
2452
  { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
2446
- { key: 'replicas', label: 'Ready', width: 'w-20 shrink-0', tooltip: 'Ready replicas' },
2453
+ { key: 'replicas', label: 'Ready', width: 'w-20 shrink-0', tooltip: "Replicas the provider reports, over the autoscaling maximum when one is set. The second number is a ceiling, not a target, so this is not a ready/desired ratio." },
2447
2454
  { key: 'capacityType', label: 'Capacity', width: 'w-28 shrink-0' },
2448
2455
  { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
2449
2456
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
@@ -2461,7 +2468,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2461
2468
  { key: 'name', label: 'Name', width: 'min-w-40' },
2462
2469
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2463
2470
  { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
2464
- { key: 'capacity', label: 'Capacity', width: 'w-32 shrink-0', tooltip: 'Computed CPU/memory' },
2471
+ { key: 'capacity', label: 'Capacity', width: 'w-32 shrink-0', tooltip: 'Per-node CPU and memory reported by the provider for scale-from-zero estimation.' },
2465
2472
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2466
2473
  ],
2467
2474
  awsmanagedclusters: [
@@ -2486,7 +2493,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2486
2493
  { key: 'name', label: 'Name', width: 'min-w-40' },
2487
2494
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2488
2495
  { key: 'machineType', label: 'Machine Type', width: 'w-32 shrink-0' },
2489
- { key: 'replicas', label: 'Replicas', width: 'w-20 shrink-0' },
2496
+ { key: 'replicas', label: 'Replicas', width: 'w-20 shrink-0', tooltip: "Replicas the provider reports as observed. Not a ready/desired ratio, and the pool's autoscaling bounds are not shown here." },
2490
2497
  { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
2491
2498
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2492
2499
  ],
@@ -2525,9 +2532,9 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2525
2532
  { key: 'name', label: 'Name', width: 'min-w-40' },
2526
2533
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2527
2534
  { key: 'sku', label: 'VM Size', width: 'w-32 shrink-0' },
2528
- { key: 'mode', label: 'Mode', width: 'w-20 shrink-0' },
2529
- { key: 'replicas', label: 'Replicas', width: 'w-20 shrink-0' },
2530
- { key: 'priority', label: 'Priority', width: 'w-24 shrink-0' },
2535
+ { key: 'mode', label: 'Mode', width: 'w-20 shrink-0', tooltip: 'System pools are preferred for critical AKS system pods; User pools are intended for applications. Mode does not enforce workload isolation.' },
2536
+ { key: 'replicas', label: 'Replicas', width: 'w-20 shrink-0', tooltip: "Replicas the provider reports as observed. Not a ready/desired ratio, and the pool's autoscaling bounds are not shown here." },
2537
+ { key: 'priority', label: 'Priority', width: 'w-24 shrink-0', tooltip: 'Spot VMs may be evicted for capacity or price; Regular VMs are not Spot. This is unrelated to Kubernetes scheduling priority.' },
2531
2538
  { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
2532
2539
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2533
2540
  ],
@@ -2554,11 +2561,11 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2554
2561
  httpproxies: [
2555
2562
  { key: 'name', label: 'Name', width: 'min-w-40' },
2556
2563
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
2557
- { key: 'fqdn', label: 'FQDN', width: 'min-w-44' },
2564
+ { key: 'fqdn', label: 'FQDN', width: 'min-w-44', tooltip: 'Virtual-host hostname this proxy declares. A dash means it declares none, which is normal for an included proxy — its routes are served under the roots that include it.' },
2558
2565
  { key: 'routes', label: 'Routes', width: 'w-20 shrink-0' },
2559
- { key: 'includes', label: 'Includes', width: 'w-24 shrink-0' },
2566
+ { key: 'includes', label: 'Includes', width: 'w-24 shrink-0', tooltip: "Child HTTPProxies this one directly includes. They contribute routing configuration, so a proxy can serve traffic even when its own Routes count is zero." },
2560
2567
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
2561
- { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
2568
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0', tooltip: "Contour's verdict on this proxy. Orphaned means no root proxy includes it, so it serves no traffic." },
2562
2569
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2563
2570
  ],
2564
2571
  // Crossplane — Managed Resources are unbounded (one kind per provider CRD);
@@ -2568,7 +2575,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2568
2575
  { key: 'namespace', label: 'Namespace', width: 'w-32 shrink-0' },
2569
2576
  { key: 'kind', label: 'Kind', width: 'w-32 shrink-0' },
2570
2577
  { key: 'external', label: 'External Name', width: 'min-w-48' },
2571
- { key: 'provider', label: 'Provider Config', width: 'w-40 shrink-0' },
2578
+ { key: 'provider', label: 'Provider Config', width: 'w-40 shrink-0', tooltip: 'Provider configuration selecting the credentials and connection settings used to manage this external resource.' },
2572
2579
  { key: 'status', label: 'Status', width: 'w-32 shrink-0' },
2573
2580
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2574
2581
  ],
@@ -2588,14 +2595,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
2588
2595
  compositions: [
2589
2596
  { key: 'name', label: 'Name', width: 'min-w-40' },
2590
2597
  { key: 'composite', label: 'Composite Kind', width: 'w-44 shrink-0' },
2591
- { key: 'mode', label: 'Mode', width: 'w-24 shrink-0' },
2598
+ { key: 'mode', label: 'Mode', width: 'w-24 shrink-0', tooltip: 'Pipeline runs composition functions. Resources uses native patch-and-transform composition and must be migrated before upgrading to Crossplane v2.' },
2592
2599
  { key: 'functions', label: 'Functions', width: 'w-28 shrink-0' },
2593
2600
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2594
2601
  ],
2595
2602
  compositeresourcedefinitions: [
2596
2603
  { key: 'name', label: 'Name', width: 'min-w-40' },
2597
2604
  { key: 'kind', label: 'Kind', width: 'w-40 shrink-0' },
2598
- { key: 'claim', label: 'Claim Kind', width: 'w-40 shrink-0' },
2605
+ { key: 'claim', label: 'Claim Kind', width: 'w-40 shrink-0', tooltip: 'Optional namespaced claim kind. Crossplane v2 supports claims only for XRDs using LegacyCluster scope.' },
2599
2606
  { key: 'age', label: 'Age', width: 'w-24 shrink-0' },
2600
2607
  ],
2601
2608
  }
@@ -3138,6 +3145,8 @@ export function mergeSavedVisibleColumns(
3138
3145
  savedVisible: string[],
3139
3146
  extraKeys: string[],
3140
3147
  printerColumns: ExtraColumn[],
3148
+ effective?: Column[],
3149
+ known?: string[],
3141
3150
  ): Set<string> {
3142
3151
  const merged = new Set(savedVisible)
3143
3152
  for (const k of extraKeys) merged.add(k)
@@ -3146,6 +3155,24 @@ export function mergeSavedVisibleColumns(
3146
3155
  if (c.defaultVisible !== false) merged.add(c.key)
3147
3156
  }
3148
3157
  }
3158
+ // Curated columns the blob predates. Same principle as the two above, but it
3159
+ // needs a record of what the user was actually offered: the blob stores only
3160
+ // what is visible, so a curated key's absence is ambiguous between "hidden on
3161
+ // purpose" and "not offered yet". `known` is that record. Without one the two
3162
+ // cases are indistinguishable, so nothing is inferred and visibility is left
3163
+ // to the saved set alone.
3164
+ //
3165
+ // Printer columns are excluded even when `known` lists none of them. They are
3166
+ // fetched, so `known` can be written during the window before the printer
3167
+ // table arrives; treating their absence as "new" would re-show ones the user
3168
+ // hid. Their own rule above already covers them.
3169
+ if (effective && Array.isArray(known)) {
3170
+ const seen = new Set(known)
3171
+ for (const c of effective) {
3172
+ if (c.key.startsWith(PRINTER_COLUMN_PREFIX)) continue
3173
+ if (!seen.has(c.key) && c.defaultVisible !== false) merged.add(c.key)
3174
+ }
3175
+ }
3149
3176
  return merged
3150
3177
  }
3151
3178
 
@@ -3201,6 +3228,10 @@ interface ColumnSettings {
3201
3228
  visible: string[]
3202
3229
  widths: Record<string, number>
3203
3230
  custom?: CustomColumnDef[]
3231
+ /** Every column key the user was offered when this was written. Lets a later
3232
+ * load tell a column they hid from one that did not exist yet. Absent on
3233
+ * blobs written before this field. */
3234
+ known?: string[]
3204
3235
  }
3205
3236
 
3206
3237
  function loadColumnSettings(kind: string, group?: string): ColumnSettings | null {
@@ -3778,6 +3809,12 @@ export function ResourcesView({
3778
3809
  return [...filteredExtras, ...kindColumns, ...builtCustomColumns]
3779
3810
  }, [selectedKind.name, selectedKind.group, extraLeadingColumns, builtCustomColumns, builtPrinterColumns])
3780
3811
 
3812
+ // The offered key set, and a stable signature for it. allColumns is a new
3813
+ // identity on every printer-column refetch; depending on the array itself
3814
+ // would rewrite the settings blob on each poll.
3815
+ const allColumnKeys = useMemo(() => allColumns.map(c => c.key), [allColumns])
3816
+ const allColumnKeysSig = allColumnKeys.join('\u0000')
3817
+
3781
3818
  // Map of extra column keys for fast O(1) lookup on each render path
3782
3819
  // (cell render, sort, column-filter unique-values).
3783
3820
  // Recompute header-derived widths once the web font swaps in (see watchHeaderFont).
@@ -3794,6 +3831,13 @@ export function ResourcesView({
3794
3831
  // Guards the save effect from persisting on the initial load of each kind
3795
3832
  // (set false by the load effect, flipped true on its first skipped save).
3796
3833
  const isColumnSettingsLoaded = useRef(false)
3834
+ // Every column key this kind has ever offered, accumulated. A single save
3835
+ // cannot stand in for it: the offered set shrinks as well as grows — an
3836
+ // uncurated kind's generic Status disappears once its printer columns arrive
3837
+ // — and a shrunk snapshot would forget that a column absent from `visible`
3838
+ // was one the user hid rather than one never shown.
3839
+ const knownColumnKeys = useRef<Set<string>>(new Set())
3840
+ const knownColumnKeysKind = useRef('')
3797
3841
  // Whether the user has a persisted column blob for this kind — data-driven
3798
3842
  // column defaults (GPU auto-show) must never override explicit user choices.
3799
3843
  const hadSavedColumnSettings = useRef(false)
@@ -3819,6 +3863,15 @@ export function ResourcesView({
3819
3863
  // (non-array, blank path, bad source) must not crash the later .map or add dead columns.
3820
3864
  const savedCustom = sanitizeCustomColumnDefs(saved?.custom)
3821
3865
  setCustomColumns(savedCustom)
3866
+ // Reset on a kind change only. This effect also re-runs when the printer
3867
+ // columns arrive, and that is the moment the offered set shrinks — clearing
3868
+ // here would drop the evidence for the column they replaced just before the
3869
+ // next save persists the record without it.
3870
+ const knownKeysIdentity = selectedKindIdentityOf(selectedKind)
3871
+ if (knownColumnKeysKind.current !== knownKeysIdentity) {
3872
+ knownColumnKeys.current = new Set()
3873
+ knownColumnKeysKind.current = knownKeysIdentity
3874
+ }
3822
3875
  const kindColumns = columnsForKindWithPrinter(selectedKind.name, selectedKind.group, builtPrinterColumns)
3823
3876
  const builtinKeys = new Set(kindColumns.map(c => c.key))
3824
3877
  const extras = filterHostExtras(extraLeadingColumns, builtinKeys, selectedKind.name)
@@ -3835,18 +3888,32 @@ export function ResourcesView({
3835
3888
  // discard the stale save and use the specialized columns instead. User-added
3836
3889
  // custom columns mean the blob isn't a stale pure-defaults save — keep it, or
3837
3890
  // the migration would clear them from storage (and un-hide hidden ones).
3891
+ // Persisted JSON: only an array of keys is usable, anything else reads as
3892
+ // no record at all.
3893
+ const savedKnown = Array.isArray(saved.known)
3894
+ ? saved.known.filter((k): k is string => typeof k === 'string')
3895
+ : undefined
3896
+ for (const k of savedKnown ?? []) knownColumnKeys.current.add(k)
3897
+
3838
3898
  const defaultKeys = DEFAULT_COLUMNS.map(c => c.key)
3899
+ // A blob carrying an offered-key record is not a pre-curation leftover:
3900
+ // its visible set matching the generic defaults means the user hid their
3901
+ // way down to them, and clearing it would undo exactly that.
3839
3902
  const isStaleDefaults = kindColumns !== DEFAULT_COLUMNS &&
3903
+ !savedKnown &&
3840
3904
  !savedCustom.length &&
3841
3905
  saved.visible.length === defaultKeys.length &&
3842
3906
  saved.visible.every(v => defaultKeys.includes(v))
3843
3907
  if (isStaleDefaults) {
3844
3908
  clearColumnSettings(selectedKind.name, selectedKind.group)
3909
+ knownColumnKeys.current = new Set()
3845
3910
  hadSavedColumnSettings.current = false
3846
3911
  setVisibleColumns(getDefaultVisibleColumns(effective))
3847
3912
  setColumnWidths({})
3848
3913
  } else {
3849
- setVisibleColumns(mergeSavedVisibleColumns(saved.visible, extraKeys, builtPrinterColumns))
3914
+ setVisibleColumns(mergeSavedVisibleColumns(
3915
+ saved.visible, extraKeys, builtPrinterColumns, effective, savedKnown,
3916
+ ))
3850
3917
  setColumnWidths(saved.widths || {})
3851
3918
  }
3852
3919
  } else {
@@ -3862,6 +3929,7 @@ export function ResourcesView({
3862
3929
  // Save column settings when they change (skip the initial load of each kind)
3863
3930
  useEffect(() => {
3864
3931
  if (visibleColumns.size === 0) return // not loaded yet
3932
+ for (const k of allColumnKeys) knownColumnKeys.current.add(k)
3865
3933
  if (!isColumnSettingsLoaded.current) {
3866
3934
  isColumnSettingsLoaded.current = true
3867
3935
  return
@@ -3870,11 +3938,13 @@ export function ResourcesView({
3870
3938
  visible: Array.from(visibleColumns),
3871
3939
  widths: columnWidths,
3872
3940
  custom: customColumns,
3941
+ known: Array.from(knownColumnKeys.current),
3873
3942
  })
3874
3943
  // A persisted blob blocks GPU auto-show from here on — same rule in-session
3875
3944
  // as across sessions, so a later data refresh can't override user choices.
3876
3945
  hadSavedColumnSettings.current = true
3877
- }, [visibleColumns, columnWidths, customColumns, selectedKind.name, selectedKind.group])
3946
+ // allColumnKeysSig, not allColumnKeys: see the memo above.
3947
+ }, [visibleColumns, columnWidths, customColumns, selectedKind.name, selectedKind.group, allColumnKeysSig])
3878
3948
 
3879
3949
  // Close column picker on outside click or Escape
3880
3950
  useEffect(() => {
@@ -3977,6 +4047,7 @@ export function ResourcesView({
3977
4047
  // Reset column settings to defaults (also drops user-added custom columns)
3978
4048
  const resetColumnSettings = useCallback(() => {
3979
4049
  clearColumnSettings(selectedKind.name, selectedKind.group)
4050
+ knownColumnKeys.current = new Set()
3980
4051
  setCustomColumns([])
3981
4052
  const customKeys = new Set(builtCustomColumns.map(c => c.key))
3982
4053
  setVisibleColumns(getDefaultVisibleColumns(allColumns.filter(c => !customKeys.has(c.key))))
@@ -5016,8 +5087,10 @@ export function ResourcesView({
5016
5087
  // DaemonSet: numberAvailable, others: availableReplicas
5017
5088
  return status.numberAvailable ?? status.availableReplicas ?? 0
5018
5089
  case 'upToDate':
5019
- // DaemonSet: updatedNumberScheduled, others: updatedReplicas
5020
- return status.updatedNumberScheduled ?? status.updatedReplicas ?? 0
5090
+ // DaemonSet: updatedNumberScheduled; CAPI v1beta2: upToDateReplicas;
5091
+ // others: updatedReplicas. Same precedence the cells display, or a row
5092
+ // sorts as zero while showing a real count.
5093
+ return status.updatedNumberScheduled ?? status.upToDateReplicas ?? status.updatedReplicas ?? 0
5021
5094
  case 'restarts':
5022
5095
  if (kindLower === 'jobsets') return getJobSetRestarts(resource)
5023
5096
  return getPodRestarts(resource)
@@ -8899,11 +8972,11 @@ function RouteCell({ resource, column }: { resource: any; column: string }) {
8899
8972
  switch (column) {
8900
8973
  case 'status': {
8901
8974
  const status = getRouteStatus(resource)
8902
- return (
8903
- <span className={clsx('badge', status.color)}>
8904
- {status.text}
8905
- </span>
8906
- )
8975
+ const reason = getRouteStatusReason(resource)
8976
+ const badge = <span className={clsx('badge', status.color)}>{status.text}</span>
8977
+ // "Degraded" on its own says something is wrong and not what. The
8978
+ // controller's own reason is usually "the backend you named is missing".
8979
+ return reason ? <Tooltip content={reason}>{badge}</Tooltip> : badge
8907
8980
  }
8908
8981
  case 'hostnames': {
8909
8982
  const hostnames = getRouteHostnames(resource)