Repository: kubernetes-sigs/kueue

Issue:
Relax number of Resources per ResourceGroup and limit the maximal number of Resources in CQ rather
**What would you like to be added**:

Currently we limit the number of Resources per ResourceGroup to 16 (see [here](https://github.com/kubernetes-sigs/kueue/blob/03e2b04cdebe66fc58a38663e567254d748aa843/apis/kueue/v1beta1/clusterqueue_types.go#L184)).

This is too constraining for production use as discussed in the [thread](https://github.com/kubernetes-sigs/kueue/issues/5877#issuecomment-3080005818) and creates incentive to artificially split the Resources into Groups.

**Why is this needed**:

Instead of validating number of resources per group, we could assert the total number of resources. My proposal, under discussion:
- limit the total number of resources in all groups to 256 (current max as 16*16 is only allowed)
- relax the number of Resources per ResourceGroup to 64 or 128 (optional, but still might be useful to have some control)



Discussion:
@mimowo: cc @gbenhaim @tenzen-y @kannon92 @kannon92: +1 on this. I think if we want to multiple static mig with different configurations on a node this would be essential.@mimowo: /close
This is addressed in https://github.com/kubernetes-sigs/kueue/pull/6906@k8s-ci-robot: @mimowo: Closing this issue.

<details>

In response to [this](https://github.com/kubernetes-sigs/kueue/issues/6007#issuecomment-3401255867):

>/close
>This is addressed in https://github.com/kubernetes-sigs/kueue/pull/6906


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>

Solution patch:
diff --git a/apis/config/v1beta1/configuration_types.go b/apis/config/v1beta1/configuration_types.go
index 2ab541f80..f0f60a2a8 100644
--- a/apis/config/v1beta1/configuration_types.go
+++ b/apis/config/v1beta1/configuration_types.go
@@ -279,6 +279,20 @@ type MultiKueue struct {
 	// - If not specified, the workload will be handled by the default ("kueue.x-k8s.io/multikueue-dispatcher-all-at-once") dispatcher.
 	// +optional
 	DispatcherName *string `json:"dispatcherName,omitempty"`
+
+	// ExternalFrameworks defines a list of external frameworks that should be supported
+	// by the generic MultiKueue adapter. Each entry defines how to handle a specific
+	// GroupVersionKind (GVK) for MultiKueue operations.
+	// +optional
+	ExternalFrameworks []MultiKueueExternalFramework `json:"externalFrameworks,omitempty"`
+}
+
+// MultiKueueExternalFramework defines a framework that is not built-in.
+type MultiKueueExternalFramework struct {
+	// Name is the GVK of the resource that are
+	// managed by external controllers
+	// the expected format is `Kind.version.group.com`.
+	Name string `json:"name"`
 }
 
 const (
diff --git a/apis/config/v1beta1/zz_generated.deepcopy.go b/apis/config/v1beta1/zz_generated.deepcopy.go
index d09086a1b..bf15881ee 100644
--- a/apis/config/v1beta1/zz_generated.deepcopy.go
+++ b/apis/config/v1beta1/zz_generated.deepcopy.go
@@ -417,6 +417,11 @@ func (in *MultiKueue) DeepCopyInto(out *MultiKueue) {
 		*out = new(string)
 		**out = **in
 	}
+	if in.ExternalFrameworks != nil {
+		in, out := &in.ExternalFrameworks, &out.ExternalFrameworks
+		*out = make([]MultiKueueExternalFramework, len(*in))
+		copy(*out, *in)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiKueue.
@@ -429,6 +434,21 @@ func (in *MultiKueue) DeepCopy() *MultiKueue {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MultiKueueExternalFramework) DeepCopyInto(out *MultiKueueExternalFramework) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiKueueExternalFramework.
+func (in *MultiKueueExternalFramework) DeepCopy() *MultiKueueExternalFramework {
+	if in == nil {
+		return nil
+	}
+	out := new(MultiKueueExternalFramework)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ObjectRetentionPolicies) DeepCopyInto(out *ObjectRetentionPolicies) {
 	*out = *in
diff --git a/apis/kueue/v1beta1/clusterqueue_types.go b/apis/kueue/v1beta1/clusterqueue_types.go
index eef40861a..4bac827c9 100644
--- a/apis/kueue/v1beta1/clusterqueue_types.go
+++ b/apis/kueue/v1beta1/clusterqueue_types.go
@@ -59,7 +59,7 @@ type ClusterQueueSpec struct {
 	// Each resource group defines the list of resources and a list of flavors
 	// that provide quotas for these resources.
 	// Each resource and each flavor can only form part of one resource group.
-	// resourceGroups can be up to 16.
+	// resourceGroups can be up to 16, with a max of 256 total flavors across all groups.
 	// +listType=atomic
 	// +kubebuilder:validation:MaxItems=16
 	ResourceGroups []ResourceGroup `json:"resourceGroups,omitempty"`
@@ -179,9 +179,10 @@ type ResourceGroup struct {
 	// coveredResources is the list of resources covered by the flavors in this
 	// group.
 	// Examples: cpu, memory, vendor.com/gpu.
-	// The list cannot be empty and it can contain up to 16 resources.
+	// The list cannot be empty and it can contain up to 64 resources. With a total
+	// of up to 256 covered resources across all resource groups in the ClusterQueue.
 	// +kubebuilder:validation:MinItems=1
-	// +kubebuilder:validation:MaxItems=16
+	// +kubebuilder:validation:MaxItems=64
 	CoveredResources []corev1.ResourceName `json:"coveredResources"`
 
 	// flavors is the list of flavors that provide the resources of this group.
@@ -190,11 +191,12 @@ type ResourceGroup struct {
 	// cpus).
 	// Each flavor MUST list all the resources listed for this group in the same
 	// order as the .resources field.
-	// The list cannot be empty and it can contain up to 16 flavors.
+	// The list cannot be empty and it can contain up to 64 flavors, with a max of
+	// 256 total flavors across all resource groups in the ClusterQueue.
 	// +listType=map
 	// +listMapKey=name
 	// +kubebuilder:validation:MinItems=1
-	// +kubebuilder:validation:MaxItems=16
+	// +kubebuilder:validation:MaxItems=64
 	Flavors []FlavorQuotas `json:"flavors"`
 }
 
diff --git a/apis/kueue/v1beta1/localqueue_types.go b/apis/kueue/v1beta1/localqueue_types.go
index 388dbdb77..fbb3390b3 100644
--- a/apis/kueue/v1beta1/localqueue_types.go
+++ b/apis/kueue/v1beta1/localqueue_types.go
@@ -84,7 +84,7 @@ type LocalQueueFlavorStatus struct {
 
 	// topology is the topology that associated with this ResourceFlavor.
 	//
-	// This is an alpha field and requires enabling the TopologyAwareScheduling
+	// This is a beta field and requires enabling the TopologyAwareScheduling
 	// feature gate.
 	//
 	// +optional
diff --git a/apis/kueue/v1beta1/topology_types.go b/apis/kueue/v1beta1/topology_types.go
index c21c2fd05..09fdbe868 100644
--- a/apis/kueue/v1beta1/topology_types.go
+++ b/apis/kueue/v1beta1/topology_types.go
@@ -74,6 +74,8 @@ const (
 	// PodSet is admitted using TopologyAwareScheduling, and all Pods created
 	// from the Job's PodTemplate also have the label. For the Pod-based
 	// integrations the label is added in webhook during the Pod creation.
+	// Depracted. This label is no longer added by TAS to Pod. The constant is
+	// only kept to support "reading" the label until 0.16.
 	TASLabel = "kueue.x-k8s.io/tas"
 
 	// PodGroupPodIndexLabel is a label set on the Pod's metadata belonging
diff --git a/charts/kueue/README.md b/charts/kueue/README.md
index 175a06265..f6694fa3b 100644
--- a/charts/kueue/README.md
+++ b/charts/kueue/README.md
@@ -125,20 +125,24 @@ The following table lists the configurable parameters of the kueue chart and the
 | enableVisibilityAPF | bool | `false` | Enable API Priority and Fairness configuration for the visibility API |
 | fullnameOverride | string | `""` | Override the resource name |
 | kubernetesClusterDomain | string | `"cluster.local"` | Kubernetes cluster's domain |
+| kueueViz.backend.env | list | `[{"name":"KUEUEVIZ_ALLOWED_ORIGINS","value":"frontend.kueueviz.local"}]` | Environment variables for KueueViz backend deployment |
 | kueueViz.backend.image.pullPolicy | string | `"Always"` | KueueViz dashboard backend image pullPolicy. This should be set to 'IfNotPresent' for released version |
 | kueueViz.backend.image.repository | string | `"us-central1-docker.pkg.dev/k8s-staging-images/kueue/kueueviz-backend"` | KueueViz dashboard backend image repository |
 | kueueViz.backend.image.tag | string | `"main"` | KueueViz dashboard backend image tag |
 | kueueViz.backend.imagePullSecrets | list | `[]` | Sets ImagePullSecrets for KueueViz dashboard backend deployments. This is useful when the images are in a private registry. |
+| kueueViz.backend.ingress.annotations | object | `{"nginx.ingress.kubernetes.io/rewrite-target":"/","nginx.ingress.kubernetes.io/ssl-redirect":"true"}` | KueueViz dashboard backend ingress annotations |
 | kueueViz.backend.ingress.host | string | `"backend.kueueviz.local"` | KueueViz dashboard backend ingress host |
 | kueueViz.backend.ingress.ingressClassName | string | `nil` | KueueViz dashboard backend ingress class name |
 | kueueViz.backend.ingress.tlsSecretName | string | `"kueueviz-backend-tls"` | KueueViz dashboard backend ingress tls secret name |
 | kueueViz.backend.nodeSelector | object | `{}` | KueueViz backend nodeSelector |
 | kueueViz.backend.priorityClassName | string | `nil` | Enable PriorityClass for KueueViz dashboard backend deployments |
 | kueueViz.backend.tolerations | list | `[]` | KueueViz backend tolerations |
+| kueueViz.frontend.env | list | `[{"name":"REACT_APP_WEBSOCKET_URL","value":"wss://backend.kueueviz.local"}]` | Environment variables for KueueViz frontend deployment |
 | kueueViz.frontend.image.pullPolicy | string | `"Always"` | KueueViz dashboard frontend image pullPolicy. This should be set to 'IfNotPresent' for released version |
 | kueueViz.frontend.image.repository | string | `"us-central1-docker.pkg.dev/k8s-staging-images/kueue/kueueviz-frontend"` | KueueViz dashboard frontend image repository |
 | kueueViz.frontend.image.tag | string | `"main"` | KueueViz dashboard frontend image tag |
 | kueueViz.frontend.imagePullSecrets | list | `[]` | Sets ImagePullSecrets for KueueViz dashboard frontend deployments. This is useful when the images are in a private registry. |
+| kueueViz.frontend.ingress.annotations | object | `{"nginx.ingress.kubernetes.io/rewrite-target":"/","nginx.ingress.kubernetes.io/ssl-redirect":"true"}` | KueueViz dashboard frontend ingress annotations |
 | kueueViz.frontend.ingress.host | string | `"frontend.kueueviz.local"` | KueueViz dashboard frontend ingress host |
 | kueueViz.frontend.ingress.ingressClassName | string | `nil` | KueueViz dashboard frontend ingress class name |
 | kueueViz.frontend.ingress.tlsSecretName | string | `"kueueviz-frontend-tls"` | KueueViz dashboard frontend ingress tls secret name |
diff --git a/charts/kueue/templates/crd/kueue.x-k8s.io_clusterqueues.yaml b/charts/kueue/templates/crd/kueue.x-k8s.io_clusterqueues.yaml
index 945231a56..011f0f343 100644
--- a/charts/kueue/templates/crd/kueue.x-k8s.io_clusterqueues.yaml
+++ b/charts/kueue/templates/crd/kueue.x-k8s.io_clusterqueues.yaml
@@ -375,7 +375,7 @@ spec:
                     Each resource group defines the list of resources and a list of flavors
                     that provide quotas for these resources.
                     Each resource and each flavor can only form part of one resource group.
-                    resourceGroups can be up to 16.
+                    resourceGroups can be up to 16, with a max of 256 total flavors across all groups.
                   items:
                     properties:
                       coveredResources:
@@ -383,11 +383,12 @@ spec:
                           coveredResources is the list of resources covered by the flavors in this
                           group.
                           Examples: cpu, memory, vendor.com/gpu.
-                          The list cannot be empty and it can contain up to 16 resources.
+                          The list cannot be empty and it can contain up to 64 resources. With a total
+                          of up to 256 covered resources across all resource groups in the ClusterQueue.
                         items:
                           description: ResourceName is the name identifying various resources in a ResourceList.
                           type: string
-                        maxItems: 16
+                        maxItems: 64
                         minItems: 1
                         type: array
                       flavors:
@@ -398,7 +399,8 @@ spec:
                           cpus).
                           Each flavor MUST list all the resources listed for this group in the same
                           order as the .resources field.
-                          The list cannot be empty and it can contain up to 16 flavors.
+                          The list cannot be empty and it can contain up to 64 flavors, with a max of
+                          256 total flavors across all resource groups in the ClusterQueue.
                         items:
                           properties:
                             name:
@@ -483,7 +485,7 @@ spec:
                             - name
                             - resources
                           type: object
-                        maxItems: 16
+                        maxItems: 64
                         minItems: 1
                         type: array
                         x-kubernetes-list-map-keys:
diff --git a/charts/kueue/templates/crd/kueue.x-k8s.io_cohorts.yaml b/charts/kueue/templates/crd/kueue.x-k8s.io_cohorts.yaml
index e198cfb1b..95b71607c 100644
--- a/charts/kueue/templates/crd/kueue.x-k8s.io_cohorts.yaml
+++ b/charts/kueue/templates/crd/kueue.x-k8s.io_cohorts.yaml
@@ -132,11 +132,12 @@ spec:
                           coveredResources is the list of resources covered by the flavors in this
                           group.
                           Examples: cpu, memory, vendor.com/gpu.
-                          The list cannot be empty and it can contain up to 16 resources.
+                          The list cannot be empty and it can contain up to 64 resources. With a total
+                          of up to 256 covered resources across all resource groups in the ClusterQueue.
                         items:
                           description: ResourceName is the name identifying various resources in a ResourceList.
                           type: string
-                        maxItems: 16
+                        maxItems: 64
                         minItems: 1
                         type: array
                       flavors:
@@ -147,7 +148,8 @@ spec:
                           cpus).
                           Each flavor MUST list all the resources listed for this group in the same
                           order as the .resources field.
-                          The list cannot be empty and it can contain up to 16 flavors.
+                          The list cannot be empty and it can contain up to 64 flavors, with a max of
+                          256 total flavors across all resource groups in the ClusterQueue.
                         items:
                           properties:
                             name:
@@ -232,7 +234,7 @@ spec:
                             - name
                             - resources
                           type: object
-                        maxItems: 16
+                        maxItems: 64
                         minItems: 1
                         type: array
                         x-kubernetes-list-map-keys:
diff --git a/charts/kueue/templates/crd/kueue.x-k8s.io_localqueues.yaml b/charts/kueue/templates/crd/kueue.x-k8s.io_localqueues.yaml
index 15217b08c..ca068aa73 100644
--- a/charts/kueue/templates/crd/kueue.x-k8s.io_localqueues.yaml
+++ b/charts/kueue/templates/crd/kueue.x-k8s.io_localqueues.yaml
@@ -336,7 +336,7 @@ spec:
                         description: |-
                           topology is the topology that associated with this ResourceFlavor.
 
-                          This is an alpha field and requires enabling the TopologyAwareScheduling
+                          This is a beta field and requires enabling the TopologyAwareScheduling
                           feature gate.
                         properties:
                           levels:
diff --git a/charts/kueue/templates/kueueviz/backend-deployment.yaml b/charts/kueue/templates/kueueviz/backend-deployment.yaml
index 296e197d4..60ed4a5db 100644
--- a/charts/kueue/templates/kueueviz/backend-deployment.yaml
+++ b/charts/kueue/templates/kueueviz/backend-deployment.yaml
@@ -33,6 +33,10 @@ spec:
       {{- end }}
       containers:
         - name: backend
+          {{- with .Values.kueueViz.backend.env }}
+          env:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
           image: '{{ .Values.kueueViz.backend.image.repository }}:{{ .Values.kueueViz.backend.image.tag | default .Chart.AppVersion }}'
           imagePullPolicy: '{{ .Values.kueueViz.backend.image.pullPolicy }}'
           ports:
diff --git a/charts/kueue/templates/kueueviz/backend-ingress.yaml b/charts/kueue/templates/kueueviz/backend-ingress.yaml
index ded278a15..2d0638950 100644
--- a/charts/kueue/templates/kueueviz/backend-ingress.yaml
+++ b/charts/kueue/templates/kueueviz/backend-ingress.yaml
@@ -4,19 +4,22 @@
 apiVersion: networking.k8s.io/v1
 kind: Ingress
 metadata:
+  {{- with .Values.kueueViz.backend.ingress.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
   name: '{{ include "kueue.fullname" . }}-kueueviz-backend-ingress'
   namespace: '{{ .Release.Namespace }}'
-  annotations:
-    nginx.ingress.kubernetes.io/rewrite-target: /
-    nginx.ingress.kubernetes.io/ssl-redirect: "true"
 spec:
-  {{- if .Values.kueueViz.backend.ingress.ingressClassName }}
-  ingressClassName: {{ .Values.kueueViz.backend.ingress.ingressClassName }}
-  {{- end }}
+  {{- if .Values.kueueViz.backend.ingress.tlsSecretName }}
   tls:
     - hosts:
         - '{{ .Values.kueueViz.backend.ingress.host | default "backend.kueueviz.local" }}'
-      secretName: '{{ .Values.kueueViz.backend.ingress.tlsSecretName | default "kueueviz-backend-tls" }}'
+      secretName: '{{ .Values.kueueViz.backend.ingress.tlsSecretName }}'
+  {{- end }}
+  {{- if .Values.kueueViz.backend.ingress.ingressClassName }}
+  ingressClassName: {{ .Values.kueueViz.backend.ingress.ingressClassName }}
+  {{- end }}
   rules:
     - host: '{{ .Values.kueueViz.backend.ingress.host | default "backend.kueueviz.local" }}'
       http:
diff --git a/charts/kueue/templates/kueueviz/frontend-deployment.yaml b/charts/kueue/templates/kueueviz/frontend-deployment.yaml
index 39360c787..47d6b6bd5 100644
--- a/charts/kueue/templates/kueueviz/frontend-deployment.yaml
+++ b/charts/kueue/templates/kueueviz/frontend-deployment.yaml
@@ -33,13 +33,14 @@ spec:
       {{- end }}
       containers:
         - name: frontend
+          {{- with .Values.kueueViz.frontend.env }}
+          env:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
           image: '{{ .Values.kueueViz.frontend.image.repository }}:{{ .Values.kueueViz.frontend.image.tag | default .Chart.AppVersion }}'
           imagePullPolicy: '{{ .Values.kueueViz.frontend.image.pullPolicy }}'
           ports:
             - containerPort: 8080
-          env:
-            - name: REACT_APP_WEBSOCKET_URL
-              value: 'wss://{{ .Values.kueueViz.backend.ingress.host | default "backend.kueueviz.local" }}'
           resources:
             limits:
               cpu: 500m
diff --git a/charts/kueue/templates/kueueviz/frontend-ingress.yaml b/charts/kueue/templates/kueueviz/frontend-ingress.yaml
index 92134b352..ca0270402 100644
--- a/charts/kueue/templates/kueueviz/frontend-ingress.yaml
+++ b/charts/kueue/templates/kueueviz/frontend-ingress.yaml
@@ -4,19 +4,22 @@
 apiVersion: networking.k8s.io/v1
 kind: Ingress
 metadata:
+  {{- with .Values.kueueViz.frontend.ingress.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
   name: '{{ include "kueue.fullname" . }}-kueueviz-frontend-ingress'
   namespace: '{{ .Release.Namespace }}'
-  annotations:
-    nginx.ingress.kubernetes.io/rewrite-target: /
-    nginx.ingress.kubernetes.io/ssl-redirect: "true"
 spec:
-  {{- if .Values.kueueViz.frontend.ingress.ingressClassName }}
-  ingressClassName: {{ .Values.kueueViz.frontend.ingress.ingressClassName }}
-  {{- end }}
+  {{- if .Values.kueueViz.frontend.ingress.tlsSecretName }}
   tls:
     - hosts:
         - '{{ .Values.kueueViz.frontend.ingress.host | default "frontend.kueueviz.local" }}'
-      secretName: '{{ .Values.kueueViz.frontend.ingress.tlsSecretName | default "kueueviz-frontend-tls" }}'
+      secretName: '{{ .Values.kueueViz.frontend.ingress.tlsSecretName }}'
+  {{- end }}
+  {{- if .Values.kueueViz.frontend.ingress.ingressClassName }}
+  ingressClassName: {{ .Values.kueueViz.frontend.ingress.ingressClassName }}
+  {{- end }}
   rules:
     - host: '{{ .Values.kueueViz.frontend.ingress.host | default "frontend.kueueviz.local" }}'
       http:
diff --git a/charts/kueue/templates/manager/manager-config.yaml b/charts/kueue/templates/manager/manager-config.yaml
index bd34932ef..39440ea0e 100644
--- a/charts/kueue/templates/manager/manager-config.yaml
+++ b/charts/kueue/templates/manager/manager-config.yaml
@@ -6,4 +6,15 @@ metadata:
   labels:
   {{- include "kueue.labels" . | nindent 4 }}
 data:
-  controller_manager_config.yaml: {{ .Values.managerConfig.controllerManagerConfigYaml | toYaml | indent 1 }}
+  controller_manager_config.yaml: |
+{{- $config := .Values.managerConfig.controllerManagerConfigYaml | fromYaml }}
+{{- if not .Values.enableCertManager }}
+  {{- $defaultInternalCert := dict
+        "enable" true
+        "webhookServiceName" (printf "%s-webhook-service" (include "kueue.fullname" .))
+        "webhookSecretName" (printf "%s-webhook-server-cert" (include "kueue.fullname" .)) }}
+  {{- if not $config.internalCertManagement }}
+    {{- $_ := set $config "internalCertManagement" $defaultInternalCert }}
+  {{- end }}
+{{- end }}
+{{ $config | toYaml | indent 4 }}
diff --git a/charts/kueue/values.yaml b/charts/kueue/values.yaml
index 2596e86f6..e2aa043e2 100644
--- a/charts/kueue/values.yaml
+++ b/charts/kueue/values.yaml
@@ -223,7 +223,15 @@ kueueViz:
     imagePullSecrets: []
     # -- Enable PriorityClass for KueueViz dashboard backend deployments
     priorityClassName:
+    # -- Environment variables for KueueViz backend deployment
+    env:
+      - name: KUEUEVIZ_ALLOWED_ORIGINS
+        value: "frontend.kueueviz.local"
     ingress:
+      # -- KueueViz dashboard backend ingress annotations
+      annotations:
+        nginx.ingress.kubernetes.io/rewrite-target: "/"
+        nginx.ingress.kubernetes.io/ssl-redirect: "true"
       # -- KueueViz dashboard backend ingress class name
       ingressClassName:
       # -- KueueViz dashboard backend ingress host
@@ -248,7 +256,15 @@ kueueViz:
     imagePullSecrets: []
     # -- Enable PriorityClass for KueueViz dashboard frontend deployments
     priorityClassName:
+    # -- Environment variables for KueueViz frontend deployment
+    env:
+      - name: REACT_APP_WEBSOCKET_URL
+        value: "wss://backend.kueueviz.local"
     ingress:
+      # -- KueueViz dashboard frontend ingress annotations
+      annotations:
+        nginx.ingress.kubernetes.io/rewrite-target: "/"
+        nginx.ingress.kubernetes.io/ssl-redirect: "true"
       # -- KueueViz dashboard frontend ingress class name
       ingressClassName:
       # -- KueueViz dashboard frontend ingress host
diff --git a/cmd/kueue/main.go b/cmd/kueue/main.go
index 0b8d187ec..5d43061af 100644
--- a/cmd/kueue/main.go
+++ b/cmd/kueue/main.go
@@ -60,6 +60,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/core"
 	"sigs.k8s.io/kueue/pkg/controller/core/indexer"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
+	"sigs.k8s.io/kueue/pkg/controller/jobs/generic"
 	"sigs.k8s.io/kueue/pkg/controller/tas"
 	tasindexer "sigs.k8s.io/kueue/pkg/controller/tas/indexer"
 	dispatcher "sigs.k8s.io/kueue/pkg/controller/workloaddispatcher"
@@ -350,6 +351,24 @@ func setupControllers(ctx context.Context, mgr ctrl.Manager, cCache *schdcache.C
 		if err != nil {
 			return fmt.Errorf("could not get the enabled multikueue adapters: %w", err)
 		}
+
+		if features.Enabled(features.MultiKueueAdaptersForCustomJobs) && cfg.MultiKueue != nil && len(cfg.MultiKueue.ExternalFrameworks) > 0 {
+			genericConfigManager := generic.NewConfigManager()
+			if err := genericConfigManager.LoadConfigurations(cfg.MultiKueue.ExternalFrameworks); err != nil {
+				return fmt.Errorf("could not load generic adapter configurations: %w", err)
+			}
+
+			// Add generic adapters to the adapters map
+			for _, adapter := range genericConfigManager.GetAllAdapters() {
+				gvkStr := adapter.GVK().String()
+				if _, exists := adapters[gvkStr]; exists {
+					return fmt.Errorf("duplicate adapter for GVK %s: conflicts with built-in adapter", gvkStr)
+				}
+				setupLog.Info("Creating generic MultiKueue adapter", "gvk", gvkStr)
+				adapters[gvkStr] = adapter
+			}
+		}
+
 		if err := multikueue.SetupControllers(mgr, *cfg.Namespace,
 			multikueue.WithGCInterval(cfg.MultiKueue.GCInterval.Duration),
 			multikueue.WithOrigin(ptr.Deref(cfg.MultiKueue.Origin, configapi.DefaultMultiKueueOrigin)),
diff --git a/cmd/kueueviz/backend/go.mod b/cmd/kueueviz/backend/go.mod
index acbf2f050..fc7b3fcb0 100644
--- a/cmd/kueueviz/backend/go.mod
+++ b/cmd/kueueviz/backend/go.mod
@@ -4,7 +4,7 @@ go 1.24.0
 
 require (
 	github.com/gin-contrib/cors v1.7.6
-	github.com/gin-gonic/gin v1.10.1
+	github.com/gin-gonic/gin v1.11.0
 	github.com/gorilla/websocket v1.5.3
 	k8s.io/api v0.32.9
 	k8s.io/apimachinery v0.32.9
@@ -14,19 +14,26 @@ require (
 require (
 	github.com/fsnotify/fsnotify v1.9.0 // indirect
 	github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
+	github.com/goccy/go-yaml v1.18.0 // indirect
+	github.com/quic-go/qpack v0.5.1 // indirect
+	github.com/quic-go/quic-go v0.54.0 // indirect
 	github.com/sagikazarmark/locafero v0.11.0 // indirect
 	github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
 	github.com/spf13/afero v1.15.0 // indirect
 	github.com/spf13/cast v1.10.0 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
+	go.uber.org/mock v0.5.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.2 // indirect
 	go.yaml.in/yaml/v3 v3.0.4 // indirect
+	golang.org/x/mod v0.26.0 // indirect
+	golang.org/x/sync v0.16.0 // indirect
+	golang.org/x/tools v0.35.0 // indirect
 )
 
 require (
-	github.com/bytedance/sonic v1.13.3 // indirect
-	github.com/bytedance/sonic/loader v0.2.4 // indirect
-	github.com/cloudwego/base64x v0.1.5 // indirect
+	github.com/bytedance/sonic v1.14.0 // indirect
+	github.com/bytedance/sonic/loader v0.3.0 // indirect
+	github.com/cloudwego/base64x v0.1.6 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
@@ -38,7 +45,7 @@ require (
 	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.26.0 // indirect
+	github.com/go-playground/validator/v10 v10.27.0 // indirect
 	github.com/goccy/go-json v0.10.5 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
@@ -48,7 +55,7 @@ require (
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
+	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
@@ -62,15 +69,15 @@ require (
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.3.0 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
-	golang.org/x/arch v0.18.0 // indirect
-	golang.org/x/crypto v0.39.0 // indirect
-	golang.org/x/net v0.41.0 // indirect
+	golang.org/x/arch v0.20.0 // indirect
+	golang.org/x/crypto v0.40.0 // indirect
+	golang.org/x/net v0.42.0 // indirect
 	golang.org/x/oauth2 v0.27.0 // indirect
-	golang.org/x/sys v0.33.0 // indirect
-	golang.org/x/term v0.32.0 // indirect
+	golang.org/x/sys v0.35.0 // indirect
+	golang.org/x/term v0.33.0 // indirect
 	golang.org/x/text v0.28.0 // indirect
 	golang.org/x/time v0.8.0 // indirect
-	google.golang.org/protobuf v1.36.6 // indirect
+	google.golang.org/protobuf v1.36.9 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/cmd/kueueviz/backend/go.sum b/cmd/kueueviz/backend/go.sum
index 94e6235ea..e8e3af076 100644
--- a/cmd/kueueviz/backend/go.sum
+++ b/cmd/kueueviz/backend/go.sum
@@ -1,11 +1,9 @@
-github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
-github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
-github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
-github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
-github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
-github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
-github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
-github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
+github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
+github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
+github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
+github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -25,8 +23,8 @@ github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQ
 github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
 github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
 github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
-github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
-github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
+github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
@@ -43,14 +41,16 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
-github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
+github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
+github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
 github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
 github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
 github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -75,10 +75,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
-github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
-github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -110,6 +108,10 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
+github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
+github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
+github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
 github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
 github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
@@ -128,7 +130,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@@ -144,38 +145,44 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
+go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
 go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
 go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
 go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
 go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
-golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
+golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
+golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
-golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
+golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
+golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
-golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
+golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
+golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
-golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
+golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
+golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
@@ -192,8 +199,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
-google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -216,7 +223,6 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJ
 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
diff --git a/config/components/crd/bases/kueue.x-k8s.io_clusterqueues.yaml b/config/components/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
index 34cc00c78..207dade41 100644
--- a/config/components/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
+++ b/config/components/crd/bases/kueue.x-k8s.io_clusterqueues.yaml
@@ -365,7 +365,7 @@ spec:
                   Each resource group defines the list of resources and a list of flavors
                   that provide quotas for these resources.
                   Each resource and each flavor can only form part of one resource group.
-                  resourceGroups can be up to 16.
+                  resourceGroups can be up to 16, with a max of 256 total flavors across all groups.
                 items:
                   properties:
                     coveredResources:
@@ -373,12 +373,13 @@ spec:
                         coveredResources is the list of resources covered by the flavors in this
                         group.
                         Examples: cpu, memory, vendor.com/gpu.
-                        The list cannot be empty and it can contain up to 16 resources.
+                        The list cannot be empty and it can contain up to 64 resources. With a total
+                        of up to 256 covered resources across all resource groups in the ClusterQueue.
                       items:
                         description: ResourceName is the name identifying various
                           resources in a ResourceList.
                         type: string
-                      maxItems: 16
+                      maxItems: 64
                       minItems: 1
                       type: array
                     flavors:
@@ -389,7 +390,8 @@ spec:
                         cpus).
                         Each flavor MUST list all the resources listed for this group in the same
                         order as the .resources field.
-                        The list cannot be empty and it can contain up to 16 flavors.
+                        The list cannot be empty and it can contain up to 64 flavors, with a max of
+                        256 total flavors across all resource groups in the ClusterQueue.
                       items:
                         properties:
                           name:
@@ -474,7 +476,7 @@ spec:
                         - name
                         - resources
                         type: object
-                      maxItems: 16
+                      maxItems: 64
                       minItems: 1
                       type: array
                       x-kubernetes-list-map-keys:
diff --git a/config/components/crd/bases/kueue.x-k8s.io_cohorts.yaml b/config/components/crd/bases/kueue.x-k8s.io_cohorts.yaml
index 878cdd653..f3b65f54b 100644
--- a/config/components/crd/bases/kueue.x-k8s.io_cohorts.yaml
+++ b/config/components/crd/bases/kueue.x-k8s.io_cohorts.yaml
@@ -116,12 +116,13 @@ spec:
                         coveredResources is the list of resources covered by the flavors in this
                         group.
                         Examples: cpu, memory, vendor.com/gpu.
-                        The list cannot be empty and it can contain up to 16 resources.
+                        The list cannot be empty and it can contain up to 64 resources. With a total
+                        of up to 256 covered resources across all resource groups in the ClusterQueue.
                       items:
                         description: ResourceName is the name identifying various
                           resources in a ResourceList.
                         type: string
-                      maxItems: 16
+                      maxItems: 64
                       minItems: 1
                       type: array
                     flavors:
@@ -132,7 +133,8 @@ spec:
                         cpus).
                         Each flavor MUST list all the resources listed for this group in the same
                         order as the .resources field.
-                        The list cannot be empty and it can contain up to 16 flavors.
+                        The list cannot be empty and it can contain up to 64 flavors, with a max of
+                        256 total flavors across all resource groups in the ClusterQueue.
                       items:
                         properties:
                           name:
@@ -217,7 +219,7 @@ spec:
                         - name
                         - resources
                         type: object
-                      maxItems: 16
+                      maxItems: 64
                       minItems: 1
                       type: array
                       x-kubernetes-list-map-keys:
diff --git a/config/components/crd/bases/kueue.x-k8s.io_localqueues.yaml b/config/components/crd/bases/kueue.x-k8s.io_localqueues.yaml
index faf48cf75..f693ef634 100644
--- a/config/components/crd/bases/kueue.x-k8s.io_localqueues.yaml
+++ b/config/components/crd/bases/kueue.x-k8s.io_localqueues.yaml
@@ -331,7 +331,7 @@ spec:
                       description: |-
                         topology is the topology that associated with this ResourceFlavor.
 
-                        This is an alpha field and requires enabling the TopologyAwareScheduling
+                        This is a beta field and requires enabling the TopologyAwareScheduling
                         feature gate.
                       properties:
                         levels:
diff --git a/config/components/kueueviz/backend-ingress.yaml b/config/components/kueueviz/backend-ingress.yaml
index 0a714b4da..895d28f46 100644
--- a/config/components/kueueviz/backend-ingress.yaml
+++ b/config/components/kueueviz/backend-ingress.yaml
@@ -4,14 +4,7 @@ kind: Ingress
 metadata:
   name: kueueviz-backend-ingress
   namespace: system
-  annotations:
-    nginx.ingress.kubernetes.io/rewrite-target: /
-    nginx.ingress.kubernetes.io/ssl-redirect: "true"
 spec:
-  tls:
-    - hosts:
-        - backend.kueueviz.local
-      secretName: kueueviz-tls-secret
   rules:
     - host: backend.kueueviz.local
       http:
diff --git a/config/components/kueueviz/frontend-deployment.yaml b/config/components/kueueviz/frontend-deployment.yaml
index e4dc461f6..b43082004 100644
--- a/config/components/kueueviz/frontend-deployment.yaml
+++ b/config/components/kueueviz/frontend-deployment.yaml
@@ -20,9 +20,6 @@ spec:
           imagePullPolicy: IfNotPresent
           ports:
             - containerPort: 8080
-          env:
-            - name: REACT_APP_WEBSOCKET_URL
-              value: 'wss://backend.kueueviz.local'
           resources:
             limits:
               cpu: 500m
diff --git a/config/components/kueueviz/frontend-ingress.yaml b/config/components/kueueviz/frontend-ingress.yaml
index 42b0c7375..e06dc9c6c 100644
--- a/config/components/kueueviz/frontend-ingress.yaml
+++ b/config/components/kueueviz/frontend-ingress.yaml
@@ -4,14 +4,7 @@ kind: Ingress
 metadata:
   name: kueueviz-frontend-ingress
   namespace: system
-  annotations:
-    nginx.ingress.kubernetes.io/rewrite-target: /
-    nginx.ingress.kubernetes.io/ssl-redirect: "true"
 spec:
-  tls:
-    - hosts:
-        - frontend.kueueviz.local
-      secretName: kueueviz-tls-secret
   rules:
     - host: frontend.kueueviz.local
       http:
diff --git a/config/kueueviz/backend_ingress_patch.yaml b/config/kueueviz/backend_ingress_patch.yaml
new file mode 100644
index 000000000..a8d4360f5
--- /dev/null
+++ b/config/kueueviz/backend_ingress_patch.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: kueueviz-backend-ingress
+  namespace: system
+  annotations:
+    nginx.ingress.kubernetes.io/rewrite-target: /
+    nginx.ingress.kubernetes.io/ssl-redirect: "true"
+spec:
+  tls:
+    - hosts:
+        - backend.kueueviz.local
+      secretName: kueueviz-tls-secret
diff --git a/config/kueueviz/frontend_deployment_patch.yaml b/config/kueueviz/frontend_deployment_patch.yaml
new file mode 100644
index 000000000..88e85e3c7
--- /dev/null
+++ b/config/kueueviz/frontend_deployment_patch.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: kueueviz-frontend
+  namespace: system
+spec:
+  template:
+    spec:
+      containers:
+        - name: frontend
+          env:
+            - name: REACT_APP_WEBSOCKET_URL
+              value: 'wss://backend.kueueviz.local'
diff --git a/config/kueueviz/frontend_ingress_patch.yaml b/config/kueueviz/frontend_ingress_patch.yaml
new file mode 100644
index 000000000..9d04cb004
--- /dev/null
+++ b/config/kueueviz/frontend_ingress_patch.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: kueueviz-frontend-ingress
+  namespace: system
+  annotations:
+    nginx.ingress.kubernetes.io/rewrite-target: /
+    nginx.ingress.kubernetes.io/ssl-redirect: "true"
+spec:
+  tls:
+    - hosts:
+        - frontend.kueueviz.local
+      secretName: kueueviz-tls-secret
diff --git a/config/kueueviz/kustomization.yaml b/config/kueueviz/kustomization.yaml
index 54e57350f..ae2aed000 100644
--- a/config/kueueviz/kustomization.yaml
+++ b/config/kueueviz/kustomization.yaml
@@ -11,3 +11,11 @@ labels:
 
 resources:
   - ../components/kueueviz/
+
+patches:
+# Add ingress annotations and tls config for nginx backend ingress controller
+- path: backend_ingress_patch.yaml
+# Add ingress annotations and tls config for nginx frontend ingress controller
+- path: frontend_ingress_patch.yaml
+# Add environment variables for frontend deployment
+- path: frontend_deployment_patch.yaml
diff --git a/hack/e2e-common.sh b/hack/e2e-common.sh
index f011c764d..314e06a38 100644
--- a/hack/e2e-common.sh
+++ b/hack/e2e-common.sh
@@ -313,7 +313,7 @@ function install_kubeflow_trainer {
         kubectl apply --kubeconfig="$2" --server-side -k "$manifests_temp_dir/overlays/manager"
     )
     # In order to install the training runtimes we need to wait for the ClusterTrainingRuntime webhook to be ready
-    kubectl wait --kubeconfig="$2" deploy/kubeflow-trainer-controller-manager -n kubeflow-system --for=condition=available --timeout=1m
+    kubectl wait --kubeconfig="$2" deploy/kubeflow-trainer-controller-manager -n kubeflow-system --for=condition=available --timeout=5m
     kubectl apply --kubeconfig="$2" --server-side -k "${KUBEFLOW_TRAINER_MANIFEST}/overlays/runtimes"
 }
 
diff --git a/hack/processing-plan.yaml b/hack/processing-plan.yaml
index 380057a2d..cd07a9ed8 100644
--- a/hack/processing-plan.yaml
+++ b/hack/processing-plan.yaml
@@ -591,26 +591,6 @@ files:
       - kustomization.yaml
     continueOnError: true
     operations:
-      - type: UPDATE
-        key: .spec.tls.[].secretName
-        value: '"{{ .Values.kueueViz.backend.ingress.tlsSecretName | default \"kueueviz-backend-tls\" }}"'
-        onFileCondition: '.kind == "Ingress" and .metadata.name | contains("kueueviz-backend-ingress")'
-        onItemCondition: '.spec.tls.[].secretName == "kueueviz-tls-secret"'
-      - type: UPDATE
-        key: .spec.tls.[].secretName
-        value: '"{{ .Values.kueueViz.frontend.ingress.tlsSecretName | default \"kueueviz-frontend-tls\" }}"'
-        onFileCondition: '.kind == "Ingress" and .metadata.name | contains("kueueviz-frontend-ingress")'
-        onItemCondition: '.spec.tls.[].secretName == "kueueviz-tls-secret"'
-      - type: UPDATE
-        key: .spec.tls.[].hosts
-        value: '["{{ .Values.kueueViz.backend.ingress.host | default \"backend.kueueviz.local\" }}"]'
-        onFileCondition: '.kind == "Ingress" and .metadata.name | contains("kueueviz-backend-ingress")'
-        onItemCondition: '.spec.tls.[].hosts.[] == "backend.kueueviz.local"'
-      - type: UPDATE
-        key: .spec.tls.[].hosts
-        value: '["{{ .Values.kueueViz.frontend.ingress.host | default \"frontend.kueueviz.local\" }}"]'
-        onFileCondition: '.kind == "Ingress" and .metadata.name | contains("kueueviz-frontend-ingress")'
-        onItemCondition: '.spec.tls.[].hosts.[] == "frontend.kueueviz.local"'
       - type: UPDATE
         key: .spec.rules[0].host
         value: '"{{ .Values.kueueViz.backend.ingress.host | default \"backend.kueueviz.local\" }}"'
@@ -694,6 +674,64 @@ files:
           {{- end }}
         indentation: 2
         onFileCondition: '.kind == "Deployment" and  .metadata.name | contains("frontend")'
+      - type: INSERT_TEXT
+        key: .spec
+        value: |
+          {{- if .Values.kueueViz.backend.ingress.tlsSecretName }}
+          tls:
+            - hosts:
+                - '{{ .Values.kueueViz.backend.ingress.host | default "backend.kueueviz.local" }}'
+              secretName: '{{ .Values.kueueViz.backend.ingress.tlsSecretName }}'
+          {{- end }}
+        indentation: 2
+        onFileCondition: '.kind == "Ingress" and  .metadata.name | contains("backend")'
+      - type: INSERT_TEXT
+        key: .spec
+        value: |
+          {{- if .Values.kueueViz.frontend.ingress.tlsSecretName }}
+          tls:
+            - hosts:
+                - '{{ .Values.kueueViz.frontend.ingress.host | default "frontend.kueueviz.local" }}'
+              secretName: '{{ .Values.kueueViz.frontend.ingress.tlsSecretName }}'
+          {{- end }}
+        indentation: 2
+        onFileCondition: '.kind == "Ingress" and  .metadata.name | contains("frontend")'
+      - type: INSERT_TEXT
+        key: .metadata
+        value: |
+          {{- with .Values.kueueViz.backend.ingress.annotations }}
+          annotations:
+            {{- toYaml . | nindent 4 }}
+          {{- end }}
+        indentation: 2
+        onFileCondition: '.kind == "Ingress" and  .metadata.name | contains("backend")'
+      - type: INSERT_TEXT
+        key: .metadata
+        value: |
+          {{- with .Values.kueueViz.frontend.ingress.annotations }}
+          annotations:
+            {{- toYaml . | nindent 4 }}
+          {{- end }}
+        indentation: 2
+        onFileCondition: '.kind == "Ingress" and  .metadata.name | contains("frontend")'
+      - type: INSERT_TEXT
+        key: .spec.template.spec.containers.[].name
+        value: |
+          {{- with .Values.kueueViz.frontend.env }}
+          env:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+        indentation: 2
+        onFileCondition: '.kind == "Deployment" and  .metadata.name | contains("frontend")'
+      - type: INSERT_TEXT
+        key: .spec.template.spec.containers.[].name
+        value: |
+          {{- with .Values.kueueViz.backend.env }}
+          env:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+        indentation: 2
+        onFileCondition: '.kind == "Deployment" and  .metadata.name | contains("backend")'
       - type: INSERT_TEXT
         key: .spec.template.spec
         value: |
diff --git a/hack/releasing/sync-notes.sh b/hack/releasing/sync-notes.sh
index 6a79db1af..22c360803 100755
--- a/hack/releasing/sync-notes.sh
+++ b/hack/releasing/sync-notes.sh
@@ -57,6 +57,10 @@ git fetch "${UPSTREAM_REMOTE}" --tags
 find_previous_version() {
   local release_version="$1"
   IFS='.' read -r major minor patch <<< "${release_version#v}"
+  if [ "$patch" -eq 0 ]; then
+    echo "${release_version}-devel"
+    return 0
+  fi
   for tag in $(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V -r); do
     IFS='.' read -r t_major t_minor t_patch <<< "${tag#v}"
     if [ "$t_major" -lt "$major" ] || \
@@ -88,9 +92,11 @@ function find_head_branch() {
 
 HEAD_BRANCH=$(find_head_branch "$RELEASE_VERSION")
 declare HEAD_BRANCH
+echo "+++ HEAD_BRANCH=$HEAD_BRANCH"
 
 PREVIOUS_VERSION=$(find_previous_version "$RELEASE_VERSION")
 declare PREVIOUS_VERSION
+echo "+++ PREVIOUS_VERSION=$PREVIOUS_VERSION"
 
 START_SHA=$(git rev-parse "${PREVIOUS_VERSION}^{commit}" 2>/dev/null)
 declare START_SHA
diff --git a/keps/2941-DRA/README.md b/keps/2941-DRA/README.md
index 682d0da7a..b380b0c45 100644
--- a/keps/2941-DRA/README.md
+++ b/keps/2941-DRA/README.md
@@ -31,13 +31,19 @@ tags, and then generate with `hack/update-toc.sh`.
   - [User Stories (Optional)](#user-stories-optional)
     - [Story 1](#story-1)
     - [Story 2](#story-2)
+    - [Story 3](#story-3)
   - [Notes/Constraints/Caveats (Optional)](#notesconstraintscaveats-optional)
   - [Risks and Mitigations](#risks-and-mitigations)
 - [Design Details](#design-details)
-  - [DynamicResourceAllocationConfig API](#dynamicresourceallocationconfig-api)
-  - [Device Class Resolution and Ambiguity Handling](#device-class-resolution-and-ambiguity-handling)
-    - [Device Class Mapping Ambiguity](#device-class-mapping-ambiguity)
+  - [Configuration API Extension for DRA](#configuration-api-extension-for-dra)
+  - [Device Class Resolution and Conflict Prevention](#device-class-resolution-and-conflict-prevention)
+    - [Device Class Mapping Uniqueness](#device-class-mapping-uniqueness)
+  - [RBAC Requirements](#rbac-requirements)
   - [Workloads](#workloads)
+    - [DRA-Specific Workload Processing](#dra-specific-workload-processing)
+    - [Workload Processing Flow](#workload-processing-flow)
+  - [Architecture Details](#architecture-details)
+    - [Queue Manager Extensions](#queue-manager-extensions)
   - [Test Plan](#test-plan)
       - [Prerequisite testing updates](#prerequisite-testing-updates)
     - [Unit Tests](#unit-tests)
@@ -55,7 +61,7 @@ tags, and then generate with `hack/update-toc.sh`.
   - [Using a CEL expression](#using-a-cel-expression)
   - [Defining DeviceClass mapping in ClusterQuota](#defining-deviceclass-mapping-in-clusterquota)
   - [Using ResourceFlavor for DeviceClass Mapping](#using-resourceflavor-for-deviceclass-mapping)
-  - [Using ConfigMap for DeviceClass Mapping](#using-configmap-for-deviceclass-mapping)
+  - [Creating a new CRD for device class mapping](#creating-a-new-crd-for-device-class-mapping)
 <!-- /toc -->
 
 ## Summary
@@ -170,13 +176,17 @@ a simple device class named `gpu.example.com`. This will be the way to enforce q
 
 ## Proposal
 
-This proposal is to extend the APIs for allowing workloads using DRA APIs to be tallied against quota management,
-borrowing and preemptable scheduling. This includes:
-1. Introducing a new API to allow creating a mapping of resource name -> device class names list
-2. Allowing admins to refer to resource name defined in DynamicResourceAllocationConfig in ClusterQueue to define
-   nominalQuota. The nominalQuota here will be applied to workloads requesting devices from any DeviceClasses mentioned
-   in the deviceClassNames list for the specific resource name. More details are documented in
-   [Design Details](#design-details)
+This proposal extends Kueue to support workloads using DRA APIs for quota management, borrowing and preemptable
+scheduling. This includes:
+
+1. Extending the existing Kueue Configuration API with `DeviceClassMappings` to map device classes to logical resource
+   names
+2. Supporting workloads that use ResourceClaimTemplates (ResourceClaims are not supported in alpha)
+3. Allowing admins to define quota for DRA resources in ClusterQueues using the logical resource names from device class
+   mappings
+4. Implementing validation to prevent device class conflicts and ensure predictable quota behavior
+
+More details are documented in [Design Details](#design-details)
 
 ### User Stories (Optional)
 
@@ -189,13 +199,20 @@ As a Kueue user, I want to use DRA devices for batch workloads in Kubernetes usi
 As an administrator of Kueue with ClusterQueue, I have a DRA driver installed in the cluster. I would like to enforce
 queuing, quota management and preemptable workloads for cluster users.
 
+#### Story 3
+
+As a cluster administrator, I want clear validation feedback when I misconfigure device class mappings so I can quickly
+identify and fix configuration conflicts before they affect workload scheduling.
+
 ### Notes/Constraints/Caveats (Optional)
 
 - The `ResourceClaims` and `ResourceClaimTemplates` APIs for DRA in k8s are immutable.
-- Device classes mapping to multiple resource names can create ambiguity during quota enforcement and requires careful
-  handling.
+- ResourceClaims are not supported in alpha - workloads must use ResourceClaimTemplates.
+  Direct ResourceClaim references will result in inadmissible workloads.
+- Device class uniqueness is enforced - each device class can only map to one resource name to prevent quota ambiguity.
+- Configuration-based approach - device class mappings are configured through the Kueue Configuration API
 - This design does not work with Kueue's Topology Aware Scheduling feature and will be addressed in future work.
-- This implementation focuses on structured parameters (beta in 1.32 and 1.33) and does not support alpha DRA features
+- This implementation focuses on structured parameters (GA in 1.34) and does not support alpha DRA features
   like DRADeviceTaints, DRAAdminAccess, DRAPrioritizedLists, and DRAPartitionableDevices.
 
 ### Risks and Mitigations
@@ -219,64 +236,68 @@ In order to mitigate this risk, Kueue can take the following approach:
 A new feature gate DynamicResourceAllocation will be introduced, allowing users to test it in dev environments while
 making the changes dormant for production users. The following sections will explain the design in detail.
 
-### DynamicResourceAllocationConfig API
+### Configuration API Extension for DRA
 
-```golang
-// +genclient
-// +kubebuilder:object:root=true
-// +kubebuilder:storageversion
-// DynamicResourceAllocationConfig is a singleton CRD that maps resource names to device classes
-// used in ClusterQueue resource quotas. It is singleton as "default" is the only allowed named for the CRD instance in
-// Kueue namespace.
-type DynamicResourceAllocationConfig struct {
-    metav1.TypeMeta   `json:",inline"`
-    metav1.ObjectMeta `json:"metadata,omitempty"`
-
-    // Spec defines the desired state of DynamicResourceAllocationConfig
-    Spec DynamicResourceAllocationConfigSpec `json:"spec"`
-}
+DRA device class mappings are configured through the existing Kueue Configuration API rather than a standalone CRD.
+This approach provides a centralized configuration mechanism and avoids the complexity of managing additional CRDs.
 
-// DynamicResourceAllocationConfigSpec defines the mappings between resource names and device classes
-type DynamicResourceAllocationConfigSpec struct {
-    // Resources is a list of mappings from resource name to device classes
-    // +listType=map
-    // +listMapKey=name
-    // +kubebuilder:validation:MaxItems=16
-    Resources []DynamicResource `json:"resources"`
+```golang
+// Resources struct in the Configuration API
+type Resources struct {
+    // DeviceClassMappings defines mappings from device classes to logical resources
+    // for Dynamic Resource Allocation support.
+    // +optional
+    DeviceClassMappings []DeviceClassMapping `json:"deviceClassMappings,omitempty"`
 }
 
-// DynamicResource defines a mapping from a resource name to a list of device classes
-type DynamicResource struct {
-    // Name is the resource name that will be referred to in ClusterQueue and Workload admission status.
+// DeviceClassMapping holds device class to logical resource mappings
+// for Dynamic Resource Allocation support.
+type DeviceClassMapping struct {
+    // Name is referenced in ClusterQueue.nominalQuota and Workload status.
+    // Must be a valid fully qualified name consisting of an optional DNS subdomain prefix
+    // followed by a slash and a DNS label, or just a DNS label.
+    // DNS labels consist of lower-case alphanumeric characters or hyphens,
+    // and must start and end with an alphanumeric character.
+    // DNS subdomain prefixes follow the same rules as DNS labels but can contain periods.
+    // The total length must not exceed 253 characters.
     Name corev1.ResourceName `json:"name"`
 
-    // DeviceClassNames lists the names of all the device classes that will count against
-    // the quota defined for this resource name
-    // +listType=set
-    // +kubebuilder:validation:MaxItems=8
+    // DeviceClassNames enumerates the DeviceClasses represented by this resource name.
+    // Each device class name must be a valid qualified name consisting of an optional DNS subdomain prefix
+    // followed by a slash and a DNS label, or just a DNS label.
+    // DNS labels consist of lower-case alphanumeric characters or hyphens,
+    // and must start and end with an alphanumeric character.
+    // DNS subdomain prefixes follow the same rules as DNS labels but can contain periods.
+    // The total length of each name must not exceed 253 characters.
     DeviceClassNames []corev1.ResourceName `json:"deviceClassNames"`
 }
 ```
 
-The ResourceFlavor spec will have a field called dynamicResource. The cluster admin has to define the list of
-deviceClasses that will be represented by a resource name and can be used to define quotas in cluster queue. With this
-a ClusterQueue with nominalQuota can be defined as follows:
+The cluster admin defines the mappings from device classes to logical resource names, which can then be used to
+define quotas in ClusterQueues.
 
+**Configuration Example:**
 ```yaml
-apiVersion: kueue.x-k8s.io/v1alpha1
-kind: DynamicResourceAllocationConfig
+apiVersion: v1
+kind: ConfigMap
 metadata:
-  name: "default"  # Fixed name - singleton CR, only one instance allowed
-  namespace: "kueue-system"
-spec:
-  resources:
-  - name: whole-gpus
-    deviceClassNames:
-    - gpu.example.com
-  - name: shared-gpus
-    deviceClassNames:
-    - ts-shard-gpus.example.com
-    - sp-shared-gpus.example.com
+  name: kueue-controller-manager-config
+  namespace: kueue-system
+data:
+  config.yaml: |
+    apiVersion: config.kueue.x-k8s.io/v1beta1
+    kind: Configuration
+    namespace: kueue-system
+    manageJobsWithoutQueueName: false
+    resources:
+      deviceClassMappings:
+      - name: whole-gpus
+        deviceClassNames:
+        - gpu.example.com
+      - name: shared-gpus
+        deviceClassNames:
+        - ts-shard-gpus.example.com
+        - sp-shared-gpus.example.com
 ---
 apiVersion: kueue.x-k8s.io/v1beta1
 kind: ResourceFlavor
@@ -310,98 +331,183 @@ example
 GPUs, and half quota configured for GPUs that are shared by workloads. Similarly, when DRAPartitionableDevices feature
 is supported in kubernetes, GPUs partitions can be represented by a single device class.
 
-### Device Class Resolution and Ambiguity Handling
+### Device Class Resolution and Conflict Prevention
 
-#### Device Class Mapping Ambiguity
+#### Device Class Mapping Uniqueness
 
-Ambiguity occurs when a device class appears in multiple mappings within the DynamicResourceAllocationConfig CR. This
-creates uncertainty at admission time about which resource name and corresponding quota should be used.
+To ensure predictable and deterministic quota enforcement, Kueue enforces strict uniqueness constraints on device class
+mappings. Each device class can only map to one resource name across all device class mappings in the configuration.
+
+Kueue prevents ambiguous configurations through validation at configuration load time. The following configuration
+would be rejected:
 
-Example of ambiguous configuration:
 ```yaml
-# Configuration allowing device class to map to multiple resource names
-apiVersion: kueue.x-k8s.io/v1alpha1
-kind: DynamicResourceAllocationConfig
+# INVALID - This configuration will be rejected during validation
+apiVersion: v1
+kind: ConfigMap
 metadata:
-  name: "default"
-  namespace: "kueue-system"
-spec:
-  resources:
-  - name: whole-gpus
-    deviceClassNames:
-    - gpus.example.com          # Appears here
-  - name: fast-gpus
-    deviceClassNames:
-    - gpus.example.com          # And also here - creates ambiguity
----
-# ClusterQueue using resource names from the DynamicResourceAllocationConfig
-apiVersion: kueue.x-k8s.io/v1beta1
-kind: ClusterQueue
+  name: kueue-controller-manager-config
+  namespace: kueue-system
+data:
+  config.yaml: |
+    apiVersion: config.kueue.x-k8s.io/v1beta1
+    kind: Configuration
+    resources:
+      deviceClassMappings:
+      - name: whole-gpus
+        deviceClassNames:
+        - gpus.example.com          # Appears here
+      - name: fast-gpus
+        deviceClassNames:
+        - gpus.example.com          # ERROR: Duplicate device class name
+``
+
+Example of valid configuration:
+```yaml
+apiVersion: v1
+kind: ConfigMap
 metadata:
-  name: multi-zone-queue
-spec:
-  resourceGroups:
-  - coveredResources: ["cpu", "memory", "whole-gpus", "fast-gpus"]
-    flavors:
-    - name: zone-a-flavor
-      resources:
+  name: kueue-controller-manager-config
+  namespace: kueue-system
+data:
+  config.yaml: |
+    apiVersion: config.kueue.x-k8s.io/v1beta1
+    kind: Configuration
+    resources:
+      deviceClassMappings:
       - name: whole-gpus
-        nominalQuota: 2
-    - name: zone-b-flavor
-      resources:
+        deviceClassNames:
+        - whole-gpus.example.com     # Unique device class
       - name: fast-gpus
-        nominalQuota: 3
+        deviceClassNames:
+        - fast-gpus.example.com      # Different device class
 ```
 
-When a device class like `gpus.example.com` maps to multiple resource names (`whole-gpus` and `fast-gpus`), Kueue must
-determine which quota to use for admission.
+This validation approach eliminates ambiguity at configuration time rather than requiring complex runtime resolution logic, ensuring predictable and efficient workload admission.
 
-Kueue resolves this ambiguity using a top-down flavor matching process:
+### RBAC Requirements
 
-1. Examine flavors in the order they are listed in the ClusterQueue spec
-2. For each flavor, check if any of its resources match the resource names that correspond to the requested device
-   class
-3. Select the first flavor that contains a resource mapping to the device class and has sufficient quota available
+DRA support requires additional RBAC permissions for the Kueue controller to access DRA resources:
 
-For example, when a workload requests `gpus.example.com`:
-- First check `zone-a-flavor` for available `whole-gpus` quota
-- If `zone-a-flavor` is exhausted, check `zone-b-flavor` for available `fast-gpus` quota
-- If no flavors have available quota, the workload waits in queue
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kueue-controller-role
+rules:
+# ... existing permissions ...
+
+# DRA-specific permissions
+- apiGroups: ["resource.k8s.io"]
+  resources: ["resourceclaims"]
+  verbs: ["get", "list", "watch"]
+- apiGroups: ["resource.k8s.io"]
+  resources: ["resourceclaimtemplates"]
+  verbs: ["get", "list", "watch"]
+```
 
-This approach ensures predictable behavior where the ClusterQueue flavor ordering determines quota evaluation priority.
+**Required Permissions:**
+- `resourceclaims`: Read access to validate ResourceClaim references (though not supported for quota)
+- `resourceclaimtemplates`: Read access to process ResourceClaimTemplates and extract device class information
+
+**Security Considerations:**
+- Kueue only requires read permissions - no create, update, or delete access to DRA resources
+- Permissions are cluster-scoped to allow processing workloads across all namespaces
+- No elevated privileges required beyond standard Kueue controller permissions
 
 ### Workloads
 
-When a user submits a workload and DynamicResourceAllocation feature gate is on, Kueue will do the following:
-
-1. Claims will be read from podSpec.resourceClaims in the PodTemplateSpec.
-2. ResourceClaimSpec will be looked up either by using:
-    1. the name of the ResourceClaimTemplate or
-    2. the name of the ResourceClaim
-
-   Both ResourceClaimTemplate or ResourceClaim will be in the same namespace as the workload.
-3. From the ResourceClaimSpec, the deviceClassName will be read.
-4. Every claim, deviceClassName for each request will be looked at
-    1. For the workload a deviceClassMap will be created by:
-        1. Retrieving the singleton DynamicResourceAllocationConfig resource (named "default")
-        2. Creating a map from DeviceClass names to resource names using the
-           DynamicResourceAllocationConfig.spec.resources entries
-        3. For resource claims that are already allocated, Kueue will maintain a mapping called `admittedResourceClaims`
-           where the key follows the format `{clusterQueueName}/{resourceClaimNamespace}/{resourceClaimName}`
-        4. If a workload is using an already allocated resource claim and the key
-           `{targetClusterQueueName}/{resourceClaimNamespace}/{resourceClaimName}` exists in the
-           `admittedResourceClaims`
-           map, Kueue will skip counting that claim against quota and automatically admit the workload
-        5. This approach ensures that within a single cluster queue, reusing allocated resource claims doesn't consume
-           additional quota, while still enforcing quota boundaries between different cluster queues
-    2. For each device class the resource name will be looked up from the deviceClassMap and resource will be
-       counted against it.
-5. Once the Kueue counts and admits the workloads, it saves the count in workload status. This does not require any API
-   change.
-
-All the step above are reflect in the YAMLs below:
+#### DRA-Specific Workload Processing
+
+DRA workloads require special handling to ensure proper resource validation and quota enforcement. Unlike standard
+workloads that are processed immediately in event handlers, DRA workloads are processed in the controller's Reconcile
+loop to enable proper error handling and retry logic.
+
+
+1. Event Handler Behavior: When a DRA workload is created or updated, the event handlers detect the presence of
+   ResourceClaimTemplates or ResourceClaims and if feature gate is enabled, skip normal queue operations, deferring
+   processing to the Reconcile loop.
+2. Reconcile Loop Processing: The Reconcile method handles all DRA-specific logic including:
+   - Feature gate validation
+   - ResourceClaim vs ResourceClaimTemplate support validation
+   - Device class mapping resolution
+   - Resource preprocessing and queue admission
+3. Error Handling: DRA processing errors are properly handled with exponential backoff retry logic.
+
+#### Workload Processing Flow
+
+When a user submits a workload and DynamicResourceAllocation feature gate is enabled, Kueue processes it as follows:
+1. DRA Detection: Kueue detects DRA workloads by checking for ResourceClaimTemplates or ResourceClaims in
+   podSpec.resourceClaims.
+2. Feature Gate Validation: Verify that the DynamicResourceAllocation feature gate is enabled. If disabled, continue
+   with legacy behavior.
+3. ResourceClaim Support Validation: Check if the workload uses ResourceClaims (not supported in alpha) or
+   ResourceClaimTemplates (supported):
+   - ResourceClaims: Mark workload as inadmissible with an error message
+   - ResourceClaimTemplates: Continue processing
+4. Device Class Resolution: For each ResourceClaimTemplate:
+   - Read the ResourceClaimTemplate from the same namespace as the workload
+   - Extract deviceClassName from each request in the template spec
+   - Look up the corresponding resource name using the device class mappings from the Configuration API
+5. Resource Preprocessing:
+   - Calculate total device count per device class across all containers and init containers
+   - Map device classes to resource names using the configuration
+   - Generate preprocessed resource requests for queue admission
+6. Queue Admission: Add the workload to the queue with preprocessed DRA resources.
+7. Status Update: Once admitted, the workload status reflects the assigned flavors and resource usage including DRA resources.
+
+The steps above are reflected in the complete configuration and workload example below:
+
 ```yaml
+# Step 1: Configure device class mappings in Kueue Configuration
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: kueue-controller-manager-config
+  namespace: kueue-system
+data:
+  config.yaml: |
+    apiVersion: config.kueue.x-k8s.io/v1beta1
+    kind: Configuration
+    namespace: kueue-system
+    resources:
+      deviceClassMappings:
+      - name: whole-gpus
+        deviceClassNames:
+        - gpu.example.com # Maps gpu.example.com -> whole-gpus
+---
+# Step 2: Define ClusterQueue with DRA resource quotas
+apiVersion: kueue.x-k8s.io/v1beta1
+kind: ClusterQueue
+metadata:
+  name: "gpus-cluster-queue"
+spec:
+  resourceGroups:
+  - coveredResources: ["cpu", "memory", "whole-gpus"]
+    flavors:
+    - name: "default-gpu-flavor"
+      resources:
+      - name: "cpu"
+        nominalQuota: 9
+      - name: "memory"
+        nominalQuota: "1200Mi"
+      - name: 'whole-gpus' # References the resource name from Configuration
+        nominalQuota: 2
+---
+# Step 3: Create ResourceClaimTemplate (only templates are supported)
+apiVersion: resource.k8s.io/v1alpha3
+kind: ResourceClaimTemplate
+metadata:
+  namespace: gpu-test1
+  name: single-gpu
+spec:
+  spec:
+    devices:
+      requests:
+      - name: gpu
+        deviceClassName: gpu.example.com # Device class from the mapping
 ---
+# Step 4: Submit workload using ResourceClaimTemplate
 apiVersion: batch/v1
 kind: Job
 metadata:
@@ -421,84 +527,60 @@ spec:
         args: ["export; sleep 9999"]
         resources:
           claims:
-          - name: gpu # 1) read the claim from resources.claims
+          - name: gpu # Reference to the resource claim
           requests:
             cpu: 1
             memory: "200Mi"
       resourceClaims:
-      - name: gpu # use the name read from #1
-        resourceClaimTemplateName: single-gpu # 2.1) the name for resource claim template
----
-apiVersion: resource.k8s.io/v1alpha3
-kind: ResourceClaimTemplate
-metadata:
-  namespace: gpu-test1
-  name: single-gpu
-spec:
-  spec:
-    devices:
-      requests:
       - name: gpu
-        deviceClassName: gpu.example.com # 3) the name of the device class
----
-apiVersion: kueue.x-k8s.io/v1alpha1
-kind: DynamicResourceAllocationConfig # 4.1.1) Singleton CR that defines all device class mappings
-metadata:
-  name: "default"
-  namespace: "kueue-system"
-spec:
-  resources:
-  - name: whole-gpus
-    deviceClassNames:
-    - gpu.example.com # 4.1.2) Creating map{"gpu.example.com" -> "whole-gpus"}
----
-apiVersion: kueue.x-k8s.io/v1beta1
-kind: ClusterQueue
-metadata:
-  name: "gpus-cluster-queue"
-spec:
-  resourceGroups:
-  - coveredResources: ["cpu", "memory", "whole-gpus"]
-    flavors:
-    - name: "default-gpu-flavor"
-      resources:
-      - name: "cpu"
-        nominalQuota: 9
-      - name: "memory"
-        nominalQuota: "1200Mi"
-      - name: 'whole-gpus' # 4.2) The resource name from DynamicResourceAllocationConfig
-        nominalQuota: 2
----
-apiVersion: kueue.x-k8s.io/v1beta1
-kind: ResourceFlavor
-metadata:
-  name: "default-gpu-flavor"
-spec:
-  # No resources field - mapping is now in DynamicResourceAllocationConfig CR
+        resourceClaimTemplateName: single-gpu # Must use template, not direct claim
 ---
+# Step 5: Resulting Workload status after admission
 apiVersion: kueue.x-k8s.io/v1beta1
 kind: Workload
 metadata:
   name: job-job0-6f46e
   namespace: gpu-test1
-...
-...
+# ... spec omitted for brevity ...
 status:
   admission:
-    clusterQueue: single-gpus-cluster-queue
+    clusterQueue: gpus-cluster-queue
     podSetAssignments:
-    - count: 2
+    - count: 1
       flavors:
-        cpu: whole-gpu-flavor
-        memory: whole-gpu-flavor
-        whole-gpus: default-gpu-flavor # 5) selected flavor is reflected here in workload status
+        cpu: default-gpu-flavor
+        memory: default-gpu-flavor
+        whole-gpus: default-gpu-flavor # DRA resource assigned to flavor
       name: main
       resourceUsage:
         cpu: "1"
-        memory: 400Mi
-        whole-gpus: "1" # 5) selected device count is reflected here in workload status
+        memory: "200Mi"
+        whole-gpus: "1" # DRA device count reflected in status
+```
+
+### Architecture Details
+
+#### Queue Manager Extensions
+
+The queue manager has been extended to support DRA resource preprocessing through the InfoOption pattern:
+
+```golang
+// Extended method signatures
+func (m *Manager) AddOrUpdateWorkload(wl *kueue.Workload, opts ...workload.InfoOption) error
+func (m *Manager) UpdateWorkload(oldWl, newWl *kueue.Workload, opts ...workload.InfoOption) error
+
+// DRA-specific InfoOption
+func WithPreprocessedDRAResources(resources map[kueue.PodSetReference]corev1.ResourceList) workload.InfoOption
 ```
 
+Processing Flow:
+1. DRA Preprocessing: Controller processes ResourceClaimTemplates and calculates resource requirements
+2. InfoOption Creation: Preprocessed resources are wrapped in `WithPreprocessedDRAResources` option
+3. Queue Integration: Queue manager receives workload with preprocessed DRA data
+4. Scheduler Access: Scheduler gets workload with DRA resources already calculated and validated
+
+This architecture separates concerns between DRA processing (controller) and queue management (scheduler), enabling robust error handling and retry logic for DRA-specific operations.
+
 ### Test Plan
 
 [x] I/we understand the owners of the involved components may require updates to
@@ -530,9 +612,11 @@ in the form of:
 This can inform certain test coverage improvements that we want to do before
 extending the production code to implement this enhancement.
 -->
-
-TBD
-- `<package>`: `<date>` - `<test coverage>`
+- pkg/cache/queue/manager.go: 09/17/2025 - 61.5%
+- pkg/config/validation.go: 09/17/2025 - 97.3%
+- pkg/controller/core/workload_controller.go: 09/17/2025 - 55.8%
+- pkg/dra/claims.go: 09/17/2025 - 83.3%
+- pkg/workload/workload.go: 09/17/2025 - 72.3%
 
 #### Integration tests
 
@@ -540,13 +624,20 @@ Integration tests in Kueue use controller-runtime's envtest framework, which pro
 without requiring kubelet or other cluster components. While DRA device allocation requires kubelet plugins, the core
 DRA integration functionality for Kueue can be tested at the integration level by:
 
-- Testing DynamicResourceAllocationConfig CRD creation and validation
-- Verifying workload admission logic with DRA resource claims
+- Testing Configuration API validation for device class mappings
+- Verifying workload admission logic with DRA ResourceClaimTemplates
 - Testing quota enforcement against device class mappings
 - Validating resource counting and flavor assignment for DRA resources
+- Testing error scenarios (feature gate disabled, unsupported ResourceClaims, unmapped device classes)
+- Verifying DRA workload processing in Reconcile loop vs event handlers
+
+The integration tests focus on Kueue's quota management and admission logic rather than actual device allocation,
+using mock ResourceClaimTemplates and DeviceClasses to simulate DRA workloads. Key test scenarios include:
 
-The integration tests would focus on Kueue's quota management and admission logic rather than actual device allocation,
-using mock ResourceClaims and DeviceClasses to simulate DRA workloads.
+- Configuration validation: Testing device class conflict detection
+- Workload inadmissibility: Testing various error conditions and proper WorkloadInadmissible condition setting
+- Resource preprocessing: Verifying correct device count calculation from ResourceClaimTemplates
+- Queue integration: Testing workload admission with preprocessed DRA resources
 
 #### E2E Test
 
@@ -577,12 +668,24 @@ Use existing dra-example-driver or Kubernetes test driver for e2e testing.
 ## Implementation History
 
 - Initial draft on September 16th 2024 by @kannon92
-- Polished on April 25th 2025 by @alaypatel07
+- Implementation development: September-December 2024
+- Design evolution from standalone CRD to Configuration API approach: October 2024
+- Alpha implementation completed: December 2024
+- KEP updated to reflect actual implementation: September 2025 by @alaypatel07
+
+**Key Design Evolution:**
+- **Original Design**: Standalone DynamicResourceAllocationConfig CRD with runtime ambiguity resolution
+- **Final Implementation**: Configuration API extension with strict validation and conflict prevention
+- **Architecture Decision**: DRA processing moved to Reconcile loop for proper error handling
+- **Scope Refinement**: ResourceClaims support removed, focus on ResourceClaimTemplates only
 
 ## Drawbacks
 
-NA. Kueue should be able to schedule devices following what upstream is proposing.
-The only drawbacks are that workloads will have to fetch the resource claim if they are specifying resource claims.
+**Configuration Restart Requirement**: Changes to device class mappings require controller restart, which may cause brief service interruption. This is acceptable for alpha feature but should be addressed in future versions.
+
+**ResourceClaims Not Supported**: Users with existing ResourceClaim-based workloads cannot use Kueue quota management and must migrate to ResourceClaimTemplates.
+
+**Limited Dynamic Reconfiguration**: Unlike some other Kueue features, DRA configuration cannot be changed dynamically and requires controller restart.
 
 ## Alternatives
 
@@ -750,11 +853,38 @@ administrators because the same resource name would have different meanings depe
 referenced. The singleton DynamicResourceAllocationConfig CRD approach addresses this by providing a single source of
 truth for all device class mappings in the cluster.
 
-### Using ConfigMap for DeviceClass Mapping
+### Creating a new CRD for device class mapping
 
-The existing configmap which holds the configuration for `kueue-controller-manager` could be used to define the resource
-name to device class mappings. This would be a single source of truth for all the device class mappings in the cluster.
-
-However, this approach is not ideal because:
-1. The global configuration prevents dynamic Kueue configuration changes without needing to restart kueue-controller-manager
-2. The configmap is already in beta, adding changes for alpha level feature to it is not preferred
+```golang
+// +genclient
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// DynamicResourceAllocationConfig is a singleton CRD that maps resource names to device classes
+// used in ClusterQueue resource quotas. It is singleton as "default" is the only allowed named for the CRD instance in
+// Kueue namespace.
+type DynamicResourceAllocationConfig struct {
+    metav1.TypeMeta   `json:",inline"`
+    metav1.ObjectMeta `json:"metadata,omitempty"`
+    // Spec defines the desired state of DynamicResourceAllocationConfig
+    Spec DynamicResourceAllocationConfigSpec `json:"spec"`
+}
+// DynamicResourceAllocationConfigSpec defines the mappings between resource names and device classes
+type DynamicResourceAllocationConfigSpec struct {
+    // Resources is a list of mappings from resource name to device classes
+    // +listType=map
+    // +listMapKey=name
+    // +kubebuilder:validation:MaxItems=16
+    Resources []DynamicResource `json:"resources"`
+}
+// DynamicResource defines a mapping from a resource name to a list of device classes
+type DynamicResource struct {
+    // Name is the resource name that will be referred to in ClusterQueue and Workload admission status.
+    Name corev1.ResourceName `json:"name"`
+    // DeviceClassNames lists the names of all the device classes that will count against
+    // the quota defined for this resource name
+    // +listType=set
+    // +kubebuilder:validation:MaxItems=8 
+    DeviceClassNames []corev1.ResourceName `json:"deviceClassNames"`
+}
+```
+However, this approach was introducing a significant amount of complexity in implementing the feature so it was rejected.
diff --git a/pkg/cache/scheduler/cache.go b/pkg/cache/scheduler/cache.go
index 81cd4ed5a..ac2a06cab 100644
--- a/pkg/cache/scheduler/cache.go
+++ b/pkg/cache/scheduler/cache.go
@@ -707,10 +707,10 @@ func (c *Cache) Usage(cqObj *kueue.ClusterQueue) (*ClusterQueueUsageStats, error
 	}
 
 	if c.fairSharingEnabled {
-		weightedShare, _ := dominantResourceShare(cq, nil)
-		stats.WeightedShare = int64(weightedShare)
+		drs := dominantResourceShare(cq, nil)
+		weightedShare, _ := drs.roundedWeightedShare()
+		stats.WeightedShare = weightedShare
 	}
-
 	return stats, nil
 }
 
@@ -729,8 +729,9 @@ func (c *Cache) CohortStats(cohortObj *kueue.Cohort) (*CohortUsageStats, error)
 
 	stats := &CohortUsageStats{}
 	if c.fairSharingEnabled {
-		weightedShare, _ := dominantResourceShare(cohort, nil)
-		stats.WeightedShare = int64(weightedShare)
+		drs := dominantResourceShare(cohort, nil)
+		weightedShare, _ := drs.roundedWeightedShare()
+		stats.WeightedShare = weightedShare
 	}
 
 	return stats, nil
diff --git a/pkg/cache/scheduler/clusterqueue_snapshot.go b/pkg/cache/scheduler/clusterqueue_snapshot.go
index 1fe225478..a9595950b 100644
--- a/pkg/cache/scheduler/clusterqueue_snapshot.go
+++ b/pkg/cache/scheduler/clusterqueue_snapshot.go
@@ -200,9 +200,8 @@ func (c *ClusterQueueSnapshot) parentHRN() hierarchicalResourceNode {
 	return c.Parent()
 }
 
-func (c *ClusterQueueSnapshot) DominantResourceShare() int {
-	share, _ := dominantResourceShare(c, nil)
-	return share
+func (c *ClusterQueueSnapshot) DominantResourceShare() DRS {
+	return dominantResourceShare(c, nil)
 }
 
 type WorkloadTASRequests map[kueue.ResourceFlavorReference]FlavorTASRequests
diff --git a/pkg/cache/scheduler/cohort_snapshot.go b/pkg/cache/scheduler/cohort_snapshot.go
index 2a7129854..86897359e 100644
--- a/pkg/cache/scheduler/cohort_snapshot.go
+++ b/pkg/cache/scheduler/cohort_snapshot.go
@@ -69,9 +69,8 @@ func (c *CohortSnapshot) subtreeClusterQueueCount() int {
 	return count
 }
 
-func (c *CohortSnapshot) DominantResourceShare() int {
-	share, _ := dominantResourceShare(c, nil)
-	return share
+func (c *CohortSnapshot) DominantResourceShare() DRS {
+	return dominantResourceShare(c, nil)
 }
 
 // implement flatResourceNode/hierarchicalResourceNode interfaces
diff --git a/pkg/cache/scheduler/fair_sharing.go b/pkg/cache/scheduler/fair_sharing.go
index e842ced0b..11601cdfe 100644
--- a/pkg/cache/scheduler/fair_sharing.go
+++ b/pkg/cache/scheduler/fair_sharing.go
@@ -17,6 +17,7 @@ limitations under the License.
 package scheduler
 
 import (
+	"cmp"
 	"math"
 
 	corev1 "k8s.io/api/core/v1"
@@ -38,7 +39,56 @@ type dominantResourceShareNode interface {
 	hierarchicalResourceNode
 }
 
-// dominantResourceShare returns a value ranging from 0 to math.MaxInt,
+// DRS contains the DominantResourceShare for some
+// node, with convenence methods for precise comparison.
+type DRS struct {
+	fairWeight       *resource.Quantity
+	unweightedRatio  float64
+	dominantResource corev1.ResourceName
+}
+
+// NegativeDRS is used as a starting point for comparisons.
+func NegativeDRS() DRS {
+	return DRS{unweightedRatio: -1, dominantResource: "", fairWeight: &oneQuantity}
+}
+
+// IsZero returns whether the DRS is 0. In other words,
+// the node for which this function was called is not
+// borrowing any resources.
+func (d DRS) IsZero() bool {
+	return d.unweightedRatio == 0
+}
+
+func (d DRS) PreciseWeightedShare() float64 {
+	if d.IsZero() {
+		return 0.0
+	}
+	if d.fairWeight.IsZero() {
+		// This branch is used only for logging; functional
+		// branches never reach here.
+		return math.Inf(1)
+	}
+	return d.unweightedRatio / d.fairWeight.AsFloat64Slow()
+}
+
+// CompareDRS compares two DRS values. A lower value
+// indicates that the ClusterQueue/Cohort with this
+// value should be preferred for scheduling, while
+// a higher value preferred for preemption.
+func CompareDRS(a, b DRS) int {
+	switch {
+	case a.zeroWeightBorrows() && b.zeroWeightBorrows():
+		return cmp.Compare(a.unweightedRatio, b.unweightedRatio)
+	case a.zeroWeightBorrows():
+		return 1
+	case b.zeroWeightBorrows():
+		return -1
+	default:
+		return cmp.Compare(a.PreciseWeightedShare(), b.PreciseWeightedShare())
+	}
+}
+
+// roundedWeightedShare returns a value ranging from 0 to math.MaxInt,
 // representing the maximum of the ratios of usage above nominal quota
 // to the lendable resources in the cohort, among all the resources
 // provided by the ClusterQueue, and divided by the weight.  If zero,
@@ -46,9 +96,26 @@ type dominantResourceShareNode interface {
 // quota.  The function also returns the resource name that yielded
 // this value.  When the FairSharing weight is 0, and the ClusterQueue
 // or Cohort is borrowing, we return math.MaxInt.
-func dominantResourceShare(node dominantResourceShareNode, wlReq resources.FlavorResourceQuantities) (int, corev1.ResourceName) {
+func (d DRS) roundedWeightedShare() (int64, corev1.ResourceName) {
+	var weightedShare int64
+	if d.zeroWeightBorrows() {
+		weightedShare = math.MaxInt64
+	} else {
+		weightedShare = int64(math.Ceil(d.PreciseWeightedShare()))
+	}
+	return weightedShare, d.dominantResource
+}
+
+// zeroWeightBorrows returns whether this DRS represents a
+// borrowing state for a ClusterQueue/Cohort with a zero weight.
+func (d DRS) zeroWeightBorrows() bool {
+	return d.fairWeight.IsZero() && !d.IsZero()
+}
+
+func dominantResourceShare(node dominantResourceShareNode, wlReq resources.FlavorResourceQuantities) DRS {
+	drs := DRS{fairWeight: node.fairWeight(), unweightedRatio: 0, dominantResource: ""}
 	if !node.HasParent() {
-		return 0, ""
+		return drs
 	}
 
 	borrowing := make(map[corev1.ResourceName]int64, len(node.getResourceNode().SubtreeQuota))
@@ -59,30 +126,21 @@ func dominantResourceShare(node dominantResourceShareNode, wlReq resources.Flavo
 		}
 	}
 	if len(borrowing) == 0 {
-		return 0, ""
+		return drs
 	}
 
-	var drs int64 = -1
-	var dRes corev1.ResourceName
-
 	lendable := calculateLendable(node.parentHRN())
 	for rName, b := range borrowing {
 		if lr := lendable[rName]; lr > 0 {
-			ratio := b * 1000 / lr
+			ratio := float64(b) * 1000.0 / float64(lr)
 			// Use alphabetical order to get a deterministic resource name.
-			if ratio > drs || (ratio == drs && rName < dRes) {
-				drs = ratio
-				dRes = rName
+			if ratio > drs.unweightedRatio || (ratio == drs.unweightedRatio && rName < drs.dominantResource) {
+				drs.unweightedRatio = ratio
+				drs.dominantResource = rName
 			}
 		}
 	}
-
-	if node.fairWeight().IsZero() {
-		return math.MaxInt, dRes
-	}
-
-	dws := drs * 1000 / node.fairWeight().MilliValue()
-	return max(1, int(dws)), dRes
+	return drs
 }
 
 // calculateLendable aggregates capacity for resources across all
diff --git a/pkg/cache/scheduler/tas_flavor.go b/pkg/cache/scheduler/tas_flavor.go
index 5f84ffe79..425800fa8 100644
--- a/pkg/cache/scheduler/tas_flavor.go
+++ b/pkg/cache/scheduler/tas_flavor.go
@@ -25,8 +25,7 @@ import (
 
 	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/apimachinery/pkg/selection"
+	"k8s.io/apimachinery/pkg/fields"
 	resourcehelpers "k8s.io/component-helpers/resource"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -115,13 +114,8 @@ func (c *TASFlavorCache) snapshot(ctx context.Context) (*TASFlavorSnapshot, erro
 	if err != nil {
 		return nil, fmt.Errorf("failed to list nodes for TAS: %w", err)
 	}
-	r, err := labels.NewRequirement(kueue.TASLabel, selection.DoesNotExist, nil)
-	if err != nil {
-		return nil, fmt.Errorf("failed to build requirement for non-TAS pods: %w", err)
-	}
 	podListOpts := &client.ListOptions{}
-	podListOpts.LabelSelector = labels.NewSelector()
-	podListOpts.LabelSelector = podListOpts.LabelSelector.Add(*r)
+	podListOpts.FieldSelector = fields.OneTermEqualSelector(indexer.TASKey, "false")
 	pods := corev1.PodList{}
 	err = c.client.List(ctx, &pods, podListOpts)
 	if err != nil {
diff --git a/pkg/cache/scheduler/tas_flavor_snapshot.go b/pkg/cache/scheduler/tas_flavor_snapshot.go
index 683e4f1cb..74c26efd3 100644
--- a/pkg/cache/scheduler/tas_flavor_snapshot.go
+++ b/pkg/cache/scheduler/tas_flavor_snapshot.go
@@ -518,6 +518,9 @@ func (s *TASFlavorSnapshot) findReplacementAssignment(tr *TASPodSetRequests, exi
 	if reason != "" {
 		return nil, nil, reason
 	}
+	if replacementAssignment == nil || len(replacementAssignment[tr.PodSet.Name].Domains) == 0 {
+		return nil, nil, fmt.Sprintf("cannot find replacement assignment for unhealthy node: %v", wl.Status.UnhealthyNodes[0].Name)
+	}
 	newAssignment := s.mergeTopologyAssignments(replacementAssignment[tr.PodSet.Name], existingAssignment)
 	return newAssignment, replacementAssignment[tr.PodSet.Name], ""
 }
diff --git a/pkg/controller/admissionchecks/provisioning/controller.go b/pkg/controller/admissionchecks/provisioning/controller.go
index aaa618672..8d4ca818d 100644
--- a/pkg/controller/admissionchecks/provisioning/controller.go
+++ b/pkg/controller/admissionchecks/provisioning/controller.go
@@ -359,7 +359,7 @@ func (c *Controller) createPodTemplate(ctx context.Context, wl *kueue.Workload,
 	}
 
 	// apply the admission node selectors to the Template
-	psi, err := podset.FromAssignment(ctx, c.client, psa, ptr.Deref(psa.Count, ps.Count))
+	psi, err := podset.FromAssignment(ctx, c.client, psa, ps)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/controller/jobframework/reconciler.go b/pkg/controller/jobframework/reconciler.go
index 10c376ac5..3b60c71b3 100644
--- a/pkg/controller/jobframework/reconciler.go
+++ b/pkg/controller/jobframework/reconciler.go
@@ -1287,7 +1287,7 @@ func getPodSetsInfoFromStatus(ctx context.Context, c client.Client, w *kueue.Wor
 	podSetsInfo := make([]podset.PodSetInfo, len(w.Status.Admission.PodSetAssignments))
 
 	for i, psAssignment := range w.Status.Admission.PodSetAssignments {
-		info, err := podset.FromAssignment(ctx, c, &psAssignment, w.Spec.PodSets[i].Count)
+		info, err := podset.FromAssignment(ctx, c, &psAssignment, &w.Spec.PodSets[i])
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/controller/jobs/generic/config.go b/pkg/controller/jobs/generic/config.go
new file mode 100644
index 000000000..d86c9c0a4
--- /dev/null
+++ b/pkg/controller/jobs/generic/config.go
@@ -0,0 +1,97 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package generic
+
+import (
+	"errors"
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	k8serrors "k8s.io/apimachinery/pkg/util/errors"
+
+	configapi "sigs.k8s.io/kueue/apis/config/v1beta1"
+)
+
+// ConfigManager manages external framework configurations for generic adapters
+type ConfigManager struct {
+	configs map[schema.GroupVersionKind]configapi.MultiKueueExternalFramework
+}
+
+// NewConfigManager creates a new configuration manager
+func NewConfigManager() *ConfigManager {
+	return &ConfigManager{
+		configs: make(map[schema.GroupVersionKind]configapi.MultiKueueExternalFramework),
+	}
+}
+
+// LoadConfigurations loads and validates external framework configurations
+func (cm *ConfigManager) LoadConfigurations(configs []configapi.MultiKueueExternalFramework) error {
+	cm.configs = make(map[schema.GroupVersionKind]configapi.MultiKueueExternalFramework)
+	var errs []error
+
+	for _, config := range configs {
+		gvk, err := parseGVK(config.Name)
+		if err != nil {
+			errs = append(errs, fmt.Errorf("invalid external framework configuration for %q: %w", config.Name, err))
+			continue
+		}
+
+		if _, exists := cm.configs[*gvk]; exists {
+			errs = append(errs, fmt.Errorf("duplicate configuration for GVK %s", gvk))
+			continue
+		}
+
+		cm.configs[*gvk] = config
+	}
+
+	return k8serrors.NewAggregate(errs)
+}
+
+// GetAdapter returns a generic adapter for the given GVK if configured
+func (cm *ConfigManager) GetAdapter(gvk schema.GroupVersionKind) *genericAdapter {
+	if _, exists := cm.configs[gvk]; !exists {
+		return nil
+	}
+
+	return &genericAdapter{
+		gvk: gvk,
+	}
+}
+
+// GetAllAdapters returns all configured generic adapters
+func (cm *ConfigManager) GetAllAdapters() []*genericAdapter {
+	adapters := make([]*genericAdapter, 0, len(cm.configs))
+	for gvk := range cm.configs {
+		adapters = append(adapters, &genericAdapter{
+			gvk: gvk,
+		})
+	}
+	return adapters
+}
+
+// parseGVK parses a string to a GVK
+func parseGVK(name string) (*schema.GroupVersionKind, error) {
+	if name == "" {
+		return nil, errors.New("name is required")
+	}
+
+	gvk, _ := schema.ParseKindArg(name)
+	if gvk == nil {
+		return nil, fmt.Errorf("invalid GVK format '%s'", name)
+	}
+	return gvk, nil
+}
diff --git a/pkg/controller/jobs/generic/generic_multikueue_adapter.go b/pkg/controller/jobs/generic/generic_multikueue_adapter.go
new file mode 100644
index 000000000..8727ced53
--- /dev/null
+++ b/pkg/controller/jobs/generic/generic_multikueue_adapter.go
@@ -0,0 +1,221 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package generic
+
+import (
+	"context"
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/api/equality"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/klog/v2"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
+	"sigs.k8s.io/kueue/pkg/controller/constants"
+	"sigs.k8s.io/kueue/pkg/controller/jobframework"
+	"sigs.k8s.io/kueue/pkg/features"
+)
+
+// genericAdapter implements the MultiKueueAdapter interface for external frameworks
+// with hardcoded default behavior as specified in the KEP.
+type genericAdapter struct {
+	gvk schema.GroupVersionKind
+}
+
+var (
+	_ jobframework.MultiKueueAdapter = (*genericAdapter)(nil)
+	_ jobframework.MultiKueueWatcher = (*genericAdapter)(nil)
+)
+
+// NewGenericAdapter creates a new generic adapter for the given GVK.
+func NewGenericAdapter(gvk schema.GroupVersionKind) jobframework.MultiKueueAdapter {
+	return &genericAdapter{
+		gvk: gvk,
+	}
+}
+
+func (a *genericAdapter) SyncJob(ctx context.Context, localClient client.Client, remoteClient client.Client, key types.NamespacedName, workloadName, origin string) error {
+	// Get the local object
+	localObj := &unstructured.Unstructured{}
+	localObj.SetGroupVersionKind(a.gvk)
+	err := localClient.Get(ctx, key, localObj)
+	if err != nil {
+		return err
+	}
+
+	// Check if remote object already exists
+	remoteObj := &unstructured.Unstructured{}
+	remoteObj.SetGroupVersionKind(a.gvk)
+	err = remoteClient.Get(ctx, key, remoteObj)
+	if err != nil && !apierrors.IsNotFound(err) {
+		return err
+	}
+
+	if apierrors.IsNotFound(err) {
+		// Create new remote object
+		return a.createRemoteObject(ctx, remoteClient, localObj, workloadName, origin)
+	}
+
+	// Update existing remote object status
+	return a.syncStatus(ctx, localClient, remoteClient, localObj, remoteObj)
+}
+
+func (a *genericAdapter) createRemoteObject(ctx context.Context, remoteClient client.Client, localObj *unstructured.Unstructured, workloadName, origin string) error {
+	log := ctrl.LoggerFrom(ctx)
+
+	// Create a copy of the local object for the remote cluster
+	remoteObj := localObj.DeepCopy()
+	remoteObj.SetResourceVersion("")
+
+	// Apply default transformation: remove the managedBy field
+	a.removeManagedByField(remoteObj)
+
+	// Add MultiKueue labels
+	labels := remoteObj.GetLabels()
+	if labels == nil {
+		labels = make(map[string]string)
+	}
+	labels[constants.PrebuiltWorkloadLabel] = workloadName
+	labels[kueue.MultiKueueOriginLabel] = origin
+	remoteObj.SetLabels(labels)
+
+	// Create the object in the remote cluster
+	log.V(2).Info("Creating remote object", "gvk", a.gvk, "name", remoteObj.GetName(), "namespace", remoteObj.GetNamespace())
+	return remoteClient.Create(ctx, remoteObj)
+}
+
+func (a *genericAdapter) syncStatus(ctx context.Context, localClient client.Client, remoteClient client.Client, localObj, remoteObj *unstructured.Unstructured) error {
+	log := ctrl.LoggerFrom(ctx)
+
+	// Create a deep copy of the original object to calculate the patch against.
+	originalObj := localObj.DeepCopy()
+
+	// Apply default sync: copy entire status from remote to local
+	a.copyStatusFromRemote(localObj, remoteObj)
+
+	// If there are no changes, do nothing.
+	if equality.Semantic.DeepEqual(localObj, originalObj) {
+		return nil
+	}
+
+	// Create the patch and apply it to the local object's status subresource.
+	patch := client.MergeFrom(originalObj)
+	log.V(2).Info("Syncing status from remote object", "gvk", a.gvk, "name", localObj.GetName())
+	return localClient.Status().Patch(ctx, localObj, patch)
+}
+
+// removeManagedByField removes the .spec.managedBy field from the object
+func (a *genericAdapter) removeManagedByField(obj *unstructured.Unstructured) {
+	spec, exists, err := unstructured.NestedMap(obj.Object, "spec")
+	if !exists || err != nil {
+		return
+	}
+
+	delete(spec, "managedBy")
+	obj.Object["spec"] = spec
+}
+
+// copyStatusFromRemote copies the entire status from remote object to local object
+func (a *genericAdapter) copyStatusFromRemote(localObj, remoteObj *unstructured.Unstructured) {
+	remoteStatus, exists, err := unstructured.NestedMap(remoteObj.Object, "status")
+	if !exists || err != nil {
+		return
+	}
+
+	// Set the status in the local object
+	localObj.Object["status"] = remoteStatus
+}
+
+func (a *genericAdapter) DeleteRemoteObject(ctx context.Context, remoteClient client.Client, key types.NamespacedName) error {
+	obj := &unstructured.Unstructured{}
+	obj.SetGroupVersionKind(a.gvk)
+	err := remoteClient.Get(ctx, key, obj)
+	if err != nil {
+		return client.IgnoreNotFound(err)
+	}
+	return client.IgnoreNotFound(remoteClient.Delete(ctx, obj, client.PropagationPolicy(metav1.DeletePropagationBackground)))
+}
+
+func (a *genericAdapter) KeepAdmissionCheckPending() bool {
+	return false
+}
+
+func (a *genericAdapter) IsJobManagedByKueue(ctx context.Context, c client.Client, key types.NamespacedName) (bool, string, error) {
+	if !features.Enabled(features.MultiKueueAdaptersForCustomJobs) {
+		return false, "MultiKueueAdaptersForCustomJobs feature gate is disabled", nil
+	}
+
+	obj := &unstructured.Unstructured{}
+	obj.SetGroupVersionKind(a.gvk)
+	err := c.Get(ctx, key, obj)
+	if err != nil {
+		return false, "", err
+	}
+
+	// Use the default managedBy path: .spec.managedBy
+	managedByValue, _, err := unstructured.NestedString(obj.Object, "spec", "managedBy")
+	if err != nil {
+		return false, "", fmt.Errorf("failed to read .spec.managedBy: %w", err)
+	}
+
+	if managedByValue != kueue.MultiKueueControllerName {
+		return false, fmt.Sprintf("Expecting .spec.managedBy to be %q not %q", kueue.MultiKueueControllerName, managedByValue), nil
+	}
+
+	return true, "", nil
+}
+
+func (a *genericAdapter) GVK() schema.GroupVersionKind {
+	return a.gvk
+}
+
+func (a *genericAdapter) GetEmptyList() client.ObjectList {
+	list := &unstructured.UnstructuredList{}
+	list.SetGroupVersionKind(schema.GroupVersionKind{
+		Group:   a.gvk.Group,
+		Version: a.gvk.Version,
+		Kind:    a.gvk.Kind + "List",
+	})
+	return list
+}
+
+func (a *genericAdapter) WorkloadKeyFor(o runtime.Object) (types.NamespacedName, error) {
+	unstructuredObj, isUnstructured := o.(*unstructured.Unstructured)
+	if !isUnstructured {
+		return types.NamespacedName{}, fmt.Errorf("not an unstructured object, got type: %T", o)
+	}
+
+	objGVK := unstructuredObj.GroupVersionKind()
+	if objGVK.Group != a.gvk.Group || objGVK.Version != a.gvk.Version || objGVK.Kind != a.gvk.Kind {
+		return types.NamespacedName{}, fmt.Errorf("unexpected GVK: expected %s, got %s for object %s", a.gvk, objGVK, klog.KObj(unstructuredObj))
+	}
+
+	labels := unstructuredObj.GetLabels()
+	prebuiltWl, hasPrebuiltWorkload := labels[constants.PrebuiltWorkloadLabel]
+	if !hasPrebuiltWorkload {
+		return types.NamespacedName{}, fmt.Errorf("no prebuilt workload found for %s: %s", a.gvk.Kind, klog.KObj(unstructuredObj))
+	}
+
+	return types.NamespacedName{Name: prebuiltWl, Namespace: unstructuredObj.GetNamespace()}, nil
+}
diff --git a/pkg/controller/tas/indexer/indexer.go b/pkg/controller/tas/indexer/indexer.go
index b1979f9f0..d3eca5f6e 100644
--- a/pkg/controller/tas/indexer/indexer.go
+++ b/pkg/controller/tas/indexer/indexer.go
@@ -19,6 +19,7 @@ package indexer
 import (
 	"context"
 	"fmt"
+	"strconv"
 
 	corev1 "k8s.io/api/core/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -28,12 +29,21 @@ import (
 )
 
 const (
+	TASKey                        = "metadata.tas"
 	WorkloadNameKey               = "metadata.workload"
 	ReadyNode                     = "metadata.ready"
 	SchedulableNode               = "spec.schedulable"
 	ResourceFlavorTopologyNameKey = "spec.topologyName"
 )
 
+func indexPodTAS(o client.Object) []string {
+	pod, ok := o.(*corev1.Pod)
+	if !ok {
+		return nil
+	}
+	return []string{strconv.FormatBool(utiltas.IsTAS(pod))}
+}
+
 func indexPodWorkload(o client.Object) []string {
 	pod, ok := o.(*corev1.Pod)
 	if !ok {
@@ -76,6 +86,10 @@ func indexResourceFlavorTopologyName(o client.Object) []string {
 }
 
 func SetupIndexes(ctx context.Context, indexer client.FieldIndexer) error {
+	if err := indexer.IndexField(ctx, &corev1.Pod{}, TASKey, indexPodTAS); err != nil {
+		return fmt.Errorf("setting index pod TAS: %w", err)
+	}
+
 	if err := indexer.IndexField(ctx, &corev1.Pod{}, WorkloadNameKey, indexPodWorkload); err != nil {
 		return fmt.Errorf("setting index pod workload: %w", err)
 	}
diff --git a/pkg/controller/tas/topology_ungater.go b/pkg/controller/tas/topology_ungater.go
index 02e17b45e..70ef8a1a0 100644
--- a/pkg/controller/tas/topology_ungater.go
+++ b/pkg/controller/tas/topology_ungater.go
@@ -134,7 +134,7 @@ func (h *podHandler) queueReconcileForPod(ctx context.Context, object client.Obj
 	if !isPod {
 		return
 	}
-	if _, found := pod.Labels[kueue.TASLabel]; !found {
+	if !utiltas.IsTAS(pod) {
 		// skip non-TAS pods
 		return
 	}
diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go
index ff1e929ec..9acbbc42a 100644
--- a/pkg/features/kube_features.go
+++ b/pkg/features/kube_features.go
@@ -187,6 +187,12 @@ const (
 	//
 	// Enable quota accounting for Dynamic Resource Allocation (DRA) devies in workloads
 	DynamicResourceAllocation featuregate.Feature = "DynamicResourceAllocation"
+
+	// owner: @khrm
+	// kep: https://github.com/kubernetes-sigs/kueue/tree/main/keps/2349-multikueue-external-custom-job-support
+	//
+	// Enable MultiKueue support for external custom Jobs via configurable adapters.
+	MultiKueueAdaptersForCustomJobs featuregate.Feature = "MultiKueueAdaptersForCustomJobs"
 )
 
 func init() {
@@ -273,15 +279,18 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned
 	},
 	TASFailedNodeReplacement: {
 		{Version: version.MustParse("0.12"), Default: false, PreRelease: featuregate.Alpha},
+		{Version: version.MustParse("0.14"), Default: true, PreRelease: featuregate.Beta},
 	},
 	ElasticJobsViaWorkloadSlices: {
 		{Version: version.MustParse("0.13"), Default: false, PreRelease: featuregate.Alpha},
 	},
 	TASFailedNodeReplacementFailFast: {
 		{Version: version.MustParse("0.13"), Default: false, PreRelease: featuregate.Alpha},
+		{Version: version.MustParse("0.14"), Default: true, PreRelease: featuregate.Beta},
 	},
 	TASReplaceNodeOnPodTermination: {
 		{Version: version.MustParse("0.13"), Default: false, PreRelease: featuregate.Alpha},
+		{Version: version.MustParse("0.14"), Default: true, PreRelease: featuregate.Beta},
 	},
 	ManagedJobsNamespaceSelectorAlwaysRespected: {
 		{Version: version.MustParse("0.13"), Default: false, PreRelease: featuregate.Alpha},
@@ -292,6 +301,9 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned
 	DynamicResourceAllocation: {
 		{Version: version.MustParse("0.14"), Default: false, PreRelease: featuregate.Alpha},
 	},
+	MultiKueueAdaptersForCustomJobs: {
+		{Version: version.MustParse("0.14"), Default: false, PreRelease: featuregate.Alpha},
+	},
 }
 
 func SetFeatureGateDuringTest(tb testing.TB, f featuregate.Feature, value bool) {
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
index 024b020f5..9d4c6c931 100644
--- a/pkg/metrics/metrics.go
+++ b/pkg/metrics/metrics.go
@@ -145,13 +145,13 @@ The label 'result' can have the following values:
 		}, []string{"name", "namespace"},
 	)
 
-	quotaReservedWaitTime = prometheus.NewHistogramVec(
+	QuotaReservedWaitTime = prometheus.NewHistogramVec(
 		prometheus.HistogramOpts{
 			Subsystem: constants.KueueName,
 			Name:      "quota_reserved_wait_time_seconds",
 			Help:      "The time between a workload was created or requeued until it got quota reservation, per 'cluster_queue'",
 			Buckets:   generateExponentialBuckets(14),
-		}, []string{"cluster_queue"},
+		}, []string{"cluster_queue", "workload_priority_class"},
 	)
 
 	PodsReadyToEvictedTimeSeconds = prometheus.NewHistogramVec(
@@ -512,7 +512,7 @@ func AdmissionAttempt(result AdmissionResult, duration time.Duration) {
 
 func QuotaReservedWorkload(cqName kueue.ClusterQueueReference, workloadPriorityClass string, waitTime time.Duration) {
 	QuotaReservedWorkloadsTotal.WithLabelValues(string(cqName), workloadPriorityClass).Inc()
-	quotaReservedWaitTime.WithLabelValues(string(cqName)).Observe(waitTime.Seconds())
+	QuotaReservedWaitTime.WithLabelValues(string(cqName), workloadPriorityClass).Observe(waitTime.Seconds())
 }
 
 func LocalQueueQuotaReservedWorkload(lq LocalQueueReference, waitTime time.Duration) {
@@ -596,7 +596,7 @@ func ClearClusterQueueMetrics(cqName string) {
 	PendingWorkloads.DeleteLabelValues(cqName, PendingStatusActive)
 	PendingWorkloads.DeleteLabelValues(cqName, PendingStatusInadmissible)
 	QuotaReservedWorkloadsTotal.DeletePartialMatch(prometheus.Labels{"cluster_queue": cqName})
-	quotaReservedWaitTime.DeleteLabelValues(cqName)
+	QuotaReservedWaitTime.DeletePartialMatch(prometheus.Labels{"cluster_queue": cqName})
 	PodsReadyToEvictedTimeSeconds.DeleteLabelValues(cqName)
 	AdmittedWorkloadsTotal.DeletePartialMatch(prometheus.Labels{"cluster_queue": cqName})
 	AdmissionWaitTime.DeletePartialMatch(prometheus.Labels{"cluster_queue": cqName})
@@ -768,7 +768,7 @@ func Register() {
 		ReservingActiveWorkloads,
 		AdmittedActiveWorkloads,
 		QuotaReservedWorkloadsTotal,
-		quotaReservedWaitTime,
+		QuotaReservedWaitTime,
 		PodsReadyToEvictedTimeSeconds,
 		AdmittedWorkloadsTotal,
 		EvictedWorkloadsTotal,
diff --git a/pkg/podset/podset.go b/pkg/podset/podset.go
index f0847d520..3ec3c5169 100644
--- a/pkg/podset/podset.go
+++ b/pkg/podset/podset.go
@@ -52,17 +52,22 @@ type PodSetInfo struct {
 
 // FromAssignment returns a PodSetInfo based on the provided assignment and an error if unable
 // to get any of the referenced flavors.
-func FromAssignment(ctx context.Context, client client.Client, assignment *kueue.PodSetAssignment, defaultCount int32) (PodSetInfo, error) {
+func FromAssignment(ctx context.Context, client client.Client, assignment *kueue.PodSetAssignment, podSet *kueue.PodSet) (PodSetInfo, error) {
 	processedFlvs := sets.New[kueue.ResourceFlavorReference]()
 	info := PodSetInfo{
 		Name:         assignment.Name,
 		NodeSelector: make(map[string]string),
-		Count:        ptr.Deref(assignment.Count, defaultCount),
+		Count:        ptr.Deref(assignment.Count, podSet.Count),
 		Labels:       make(map[string]string),
 		Annotations:  make(map[string]string),
 	}
 	if features.Enabled(features.TopologyAwareScheduling) && assignment.TopologyAssignment != nil {
-		info.Labels[kueue.TASLabel] = "true"
+		// For implicit TAS we inject the "unconstrained" topology by default, even if unspecified.
+		if podSet.TopologyRequest == nil || (podSet.TopologyRequest.Preferred == nil &&
+			podSet.TopologyRequest.Required == nil &&
+			podSet.TopologyRequest.Unconstrained == nil) {
+			info.Annotations[kueue.PodSetUnconstrainedTopologyAnnotation] = "true"
+		}
 		info.SchedulingGates = append(info.SchedulingGates, corev1.PodSchedulingGate{
 			Name: kueue.TopologySchedulingGate,
 		})
diff --git a/pkg/scheduler/fair_sharing_iterator.go b/pkg/scheduler/fair_sharing_iterator.go
index 11394d1e1..951fded24 100644
--- a/pkg/scheduler/fair_sharing_iterator.go
+++ b/pkg/scheduler/fair_sharing_iterator.go
@@ -160,16 +160,17 @@ type drsKey struct {
 }
 
 type entryComparer struct {
-	drsValues        map[drsKey]int
+	drsValues        map[drsKey]schdcache.DRS
 	workloadOrdering workload.Ordering
 }
 
 func (e *entryComparer) less(a, b *entry, parentCohort kueue.CohortReference) bool {
 	aDrs := e.drsValues[drsKey{parentCohort: parentCohort, workloadKey: workload.Key(a.Obj)}]
 	bDrs := e.drsValues[drsKey{parentCohort: parentCohort, workloadKey: workload.Key(b.Obj)}]
+
 	// 1: DRF
-	if aDrs != bDrs {
-		return aDrs < bDrs
+	if cmp := schdcache.CompareDRS(aDrs, bDrs); cmp != 0 {
+		return cmp == -1
 	}
 
 	// 2: Priority
@@ -193,7 +194,7 @@ func (e *entryComparer) less(a, b *entry, parentCohort kueue.CohortReference) bo
 // all children the parentCohort, to select the child with the lowest
 // DRS after admission of its nominated workload.
 func (e *entryComparer) computeDRS(rootCohort *schdcache.CohortSnapshot, cqToEntry map[*schdcache.ClusterQueueSnapshot]*entry) {
-	e.drsValues = make(map[drsKey]int)
+	e.drsValues = make(map[drsKey]schdcache.DRS)
 	for _, cq := range rootCohort.SubtreeClusterQueues() {
 		entry, ok := cqToEntry[cq]
 		if !ok {
@@ -221,7 +222,7 @@ func (e *entryComparer) logDrsValuesWhenVerbose(log logr.Logger) {
 	if logV := log.V(5); logV.Enabled() {
 		serializableDrs := make([]string, 0, len(e.drsValues))
 		for k, v := range e.drsValues {
-			serializableDrs = append(serializableDrs, fmt.Sprintf("{parentCohort: %s, workload %s, drs: %d}", k.parentCohort, k.workloadKey, v))
+			serializableDrs = append(serializableDrs, fmt.Sprintf("{parentCohort: %s, workload %s, drs: %.3f}", k.parentCohort, k.workloadKey, v.PreciseWeightedShare()))
 		}
 		logV.Info("DominantResourceShare values used during tournament", "drsValues", serializableDrs)
 	}
diff --git a/pkg/scheduler/preemption/fairsharing/least_common_ancestor.go b/pkg/scheduler/preemption/fairsharing/least_common_ancestor.go
index bd03bbde9..1e107d426 100644
--- a/pkg/scheduler/preemption/fairsharing/least_common_ancestor.go
+++ b/pkg/scheduler/preemption/fairsharing/least_common_ancestor.go
@@ -20,7 +20,7 @@ import schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
 // the lowest shared node - the LeastCommonAncestor (LCA). While LCA
 // is always a Cohort, almostLCA may be a ClusterQueue or a Cohort.
 type almostLCA interface {
-	DominantResourceShare() int
+	DominantResourceShare() schdcache.DRS
 }
 
 // getAlmostLCAs returns almostLCAs of (preemptor, target).
diff --git a/pkg/scheduler/preemption/fairsharing/ordering.go b/pkg/scheduler/preemption/fairsharing/ordering.go
index decc9dafa..0612f2312 100644
--- a/pkg/scheduler/preemption/fairsharing/ordering.go
+++ b/pkg/scheduler/preemption/fairsharing/ordering.go
@@ -136,8 +136,8 @@ func (t *TargetClusterQueueOrdering) hasWorkload(cq *schdcache.ClusterQueueSnaps
 // are no more candidate ClusterQueues; an iteration may have only
 // pruned nodes from the tree.
 func (t *TargetClusterQueueOrdering) nextTarget(cohort *schdcache.CohortSnapshot) *TargetClusterQueue {
-	var highestCq *schdcache.ClusterQueueSnapshot = nil
-	highestCqDrs := -1
+	var highestCq *schdcache.ClusterQueueSnapshot
+	highestCqDrs := schdcache.NegativeDRS()
 	for _, cq := range cohort.ChildCQs() {
 		if t.prunedClusterQueues.Has(cq) {
 			continue
@@ -147,22 +147,22 @@ func (t *TargetClusterQueueOrdering) nextTarget(cohort *schdcache.CohortSnapshot
 		// we can't prune the preemptor ClusterQueue itself,
 		// until it runs out of candidates.
 		switch {
-		case (drs == 0 && cq != t.preemptorCq) || !t.hasWorkload(cq):
+		case (drs.IsZero() && cq != t.preemptorCq) || !t.hasWorkload(cq):
 			t.prunedClusterQueues.Insert(cq)
-		case drs == highestCqDrs:
+		case schdcache.CompareDRS(drs, highestCqDrs) == 0:
 			newCandWl := t.clusterQueueToTarget[cq.GetName()][0]
 			currentCandWl := t.clusterQueueToTarget[highestCq.GetName()][0]
 			if preemptioncommon.CandidatesOrdering(t.log, false, newCandWl, currentCandWl, t.preemptorCq.Name, time.Now()) < 0 {
 				highestCq = cq
 			}
-		case drs > highestCqDrs:
+		case schdcache.CompareDRS(drs, highestCqDrs) == 1:
 			highestCqDrs = drs
 			highestCq = cq
 		}
 	}
 
 	var highestCohort *schdcache.CohortSnapshot = nil
-	highestCohortDrs := -1
+	highestCohortDrs := schdcache.NegativeDRS()
 	for _, cohort := range cohort.ChildCohorts() {
 		if t.prunedCohorts.Has(cohort) {
 			continue
@@ -177,9 +177,9 @@ func (t *TargetClusterQueueOrdering) nextTarget(cohort *schdcache.CohortSnapshot
 		// subtree, or a possible preemption within Preemptor
 		// CQ itself.  We will only prune such a Cohort if all
 		// of its children have been pruned.
-		if drs == 0 && !t.onPathFromRootToPreemptorCQ(cohort) {
+		if drs.IsZero() && !t.onPathFromRootToPreemptorCQ(cohort) {
 			t.prunedCohorts.Insert(cohort)
-		} else if drs >= highestCohortDrs {
+		} else if schdcache.CompareDRS(drs, highestCohortDrs) >= 0 {
 			highestCohortDrs = drs
 			highestCohort = cohort
 		}
@@ -195,7 +195,7 @@ func (t *TargetClusterQueueOrdering) nextTarget(cohort *schdcache.CohortSnapshot
 	// we use >= because, as a tiebreak, choosing the Cohort seems
 	// slightly more fair, as we can choose the most unfair node
 	// within that Cohort.
-	if highestCohortDrs >= highestCqDrs {
+	if schdcache.CompareDRS(highestCohortDrs, highestCqDrs) >= 0 {
 		return t.nextTarget(highestCohort)
 	}
 	return &TargetClusterQueue{
diff --git a/pkg/scheduler/preemption/fairsharing/strategy.go b/pkg/scheduler/preemption/fairsharing/strategy.go
index df413792c..34c3b2efe 100644
--- a/pkg/scheduler/preemption/fairsharing/strategy.go
+++ b/pkg/scheduler/preemption/fairsharing/strategy.go
@@ -16,28 +16,30 @@ limitations under the License.
 
 package fairsharing
 
+import schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
+
 // PreemptorNewShare is the DominantResourceShare of the Preemptor
 // after the incoming workload's usage has been added. It is used for
 // both rules S2-a and S2-b
-type PreemptorNewShare int
+type PreemptorNewShare schdcache.DRS
 
 // TargetNewShare is the DominantResourceShare of the Preemptee after
 // its preempted workload's usage has been removed. It is used for
 // rule S2-a.
-type TargetNewShare int
+type TargetNewShare schdcache.DRS
 
 // TargetOldShare is the DominantResourceShare of the Preemptee before
 // its workload has been removed. It is used for rule S2-b.
-type TargetOldShare int
+type TargetOldShare schdcache.DRS
 
 type Strategy func(PreemptorNewShare, TargetOldShare, TargetNewShare) bool
 
 // LessThanOrEqualToFinalShare implements Rule S2-a in https://sigs.k8s.io/kueue/keps/1714-fair-sharing#choosing-workloads-from-clusterqueues-for-preemption
 func LessThanOrEqualToFinalShare(preemptorNewShare PreemptorNewShare, _ TargetOldShare, targetNewShare TargetNewShare) bool {
-	return int(preemptorNewShare) <= int(targetNewShare)
+	return schdcache.CompareDRS(schdcache.DRS(preemptorNewShare), schdcache.DRS(targetNewShare)) <= 0
 }
 
 // LessThanInitialShare implements rule S2-b in https://sigs.k8s.io/kueue/keps/1714-fair-sharing#choosing-workloads-from-clusterqueues-for-preemption
 func LessThanInitialShare(preemptorNewShare PreemptorNewShare, targetOldShare TargetOldShare, _ TargetNewShare) bool {
-	return int(preemptorNewShare) < int(targetOldShare)
+	return schdcache.CompareDRS(schdcache.DRS(preemptorNewShare), schdcache.DRS(targetOldShare)) < 0
 }
diff --git a/pkg/scheduler/preemption/preemption.go b/pkg/scheduler/preemption/preemption.go
index 352e150aa..4694c5b6c 100644
--- a/pkg/scheduler/preemption/preemption.go
+++ b/pkg/scheduler/preemption/preemption.go
@@ -362,7 +362,7 @@ func runSecondFsStrategy(retryCandidates []*workload.Info, preemptionCtx *preemp
 		preemptorNewShare, targetOldShare := candCQ.ComputeShares()
 		// Due to API validation, we can only reach here if the second strategy is LessThanInitialShare,
 		// in which case the last parameter for the strategy function is irrelevant.
-		if fairsharing.LessThanInitialShare(preemptorNewShare, targetOldShare, 0) {
+		if fairsharing.LessThanInitialShare(preemptorNewShare, targetOldShare, fairsharing.TargetNewShare{}) {
 			// The criteria doesn't depend on the preempted workload, so just preempt the first candidate.
 			candWl := candCQ.PopWorkload()
 			preemptionCtx.snapshot.RemoveWorkload(candWl)
diff --git a/pkg/util/cert/cert.go b/pkg/util/cert/cert.go
index cf5986434..502496d82 100644
--- a/pkg/util/cert/cert.go
+++ b/pkg/util/cert/cert.go
@@ -18,6 +18,7 @@ package cert
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/go-logr/logr"
 	cert "github.com/open-policy-agent/cert-controller/pkg/rotator"
@@ -28,10 +29,9 @@ import (
 )
 
 const (
-	vwcName        = "kueue-validating-webhook-configuration"
-	mwcName        = "kueue-mutating-webhook-configuration"
-	caName         = "kueue-ca"
-	caOrganization = "kueue"
+	caName               = "kueue-ca"
+	caOrganization       = "kueue"
+	webhookServiceSuffix = "-webhook-service"
 )
 
 // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;update
@@ -43,6 +43,11 @@ func ManageCerts(mgr ctrl.Manager, cfg config.Configuration, setupFinished chan
 	// DNSName is <service name>.<namespace>.svc
 	var dnsName = fmt.Sprintf("%s.%s.svc", *cfg.InternalCertManagement.WebhookServiceName, *cfg.Namespace)
 
+	// Derive webhook configuration names from the webhook service name
+	webhookBaseName := deriveWebhookBaseName(*cfg.InternalCertManagement.WebhookServiceName)
+	mutatingWebhookName := buildWebhookConfigurationName(webhookBaseName, "mutating")
+	validatingWebhookName := buildWebhookConfigurationName(webhookBaseName, "validating")
+
 	return cert.AddRotator(mgr, &cert.CertRotator{
 		SecretKey: types.NamespacedName{
 			Namespace: *cfg.Namespace,
@@ -55,10 +60,10 @@ func ManageCerts(mgr ctrl.Manager, cfg config.Configuration, setupFinished chan
 		IsReady:        setupFinished,
 		Webhooks: []cert.WebhookInfo{{
 			Type: cert.Validating,
-			Name: vwcName,
+			Name: validatingWebhookName,
 		}, {
 			Type: cert.Mutating,
-			Name: mwcName,
+			Name: mutatingWebhookName,
 		}},
 		// When kueue is running in the leader election mode,
 		// we expect webhook server will run in primary and secondary instance
@@ -71,3 +76,14 @@ func WaitForCertsReady(log logr.Logger, certsReady chan struct{}) {
 	<-certsReady
 	log.Info("Certs ready")
 }
+
+// deriveWebhookBaseName extracts the base name from a webhook service name
+func deriveWebhookBaseName(webhookServiceName string) string {
+	return strings.TrimSuffix(webhookServiceName, webhookServiceSuffix)
+}
+
+// buildWebhookConfigurationName constructs a webhook configuration name
+// from a base name and webhook type suffix.
+func buildWebhookConfigurationName(baseName, webhookType string) string {
+	return fmt.Sprintf("%s-%s-webhook-configuration", baseName, webhookType)
+}
diff --git a/pkg/util/tas/tas.go b/pkg/util/tas/tas.go
index 9f35d0138..2289d0c37 100644
--- a/pkg/util/tas/tas.go
+++ b/pkg/util/tas/tas.go
@@ -21,7 +21,7 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 
-	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1beta1"
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 )
 
 type TopologyDomainID string
@@ -30,6 +30,30 @@ func DomainID(levelValues []string) TopologyDomainID {
 	return TopologyDomainID(strings.Join(levelValues, ","))
 }
 
+func IsTAS(pod *corev1.Pod) bool {
+	if IsExplicitTAS(pod.Annotations) {
+		return true
+	}
+	if _, ok := pod.Annotations[kueue.PodSetUnconstrainedTopologyAnnotation]; ok {
+		return true
+	}
+	// TODO: remove after 0.16
+	if _, ok := pod.Labels[kueue.TASLabel]; ok {
+		return true
+	}
+	return false
+}
+
+func IsExplicitTAS(annots map[string]string) bool {
+	if _, ok := annots[kueue.PodSetPreferredTopologyAnnotation]; ok {
+		return true
+	}
+	if _, ok := annots[kueue.PodSetRequiredTopologyAnnotation]; ok {
+		return true
+	}
+	return false
+}
+
 func NodeLabelsFromKeysAndValues(keys, values []string) map[string]string {
 	result := make(map[string]string, len(keys))
 	for i := range keys {
@@ -46,7 +70,7 @@ func LevelValues(levelKeys []string, objectLabels map[string]string) []string {
 	return levelValues
 }
 
-func Levels(topology *kueuealpha.Topology) []string {
+func Levels(topology *kueue.Topology) []string {
 	result := make([]string, len(topology.Spec.Levels))
 	for i, level := range topology.Spec.Levels {
 		result[i] = level.NodeLabel
diff --git a/pkg/webhooks/clusterqueue_webhook.go b/pkg/webhooks/clusterqueue_webhook.go
index 6205b7548..d412bd9b3 100644
--- a/pkg/webhooks/clusterqueue_webhook.go
+++ b/pkg/webhooks/clusterqueue_webhook.go
@@ -110,6 +110,9 @@ func ValidateClusterQueue(cq *kueue.ClusterQueue) field.ErrorList {
 		allErrs = append(allErrs, validatePreemption(cq.Spec.Preemption, path.Child("preemption"))...)
 	}
 	allErrs = append(allErrs, validateFairSharing(cq.Spec.FairSharing, path.Child("fairSharing"))...)
+	allErrs = append(allErrs, validateTotalFlavors(cq.Spec.ResourceGroups, path.Child("resourceGroups"))...)
+	allErrs = append(allErrs, validateTotalCoveredResources(cq.Spec.ResourceGroups, path.Child("resourceGroups"))...)
+	allErrs = append(allErrs, validateFlavorResourceCombinations(cq.Spec.ResourceGroups, path.Child("resourceGroups"))...)
 	return allErrs
 }
 
@@ -117,6 +120,50 @@ func ValidateClusterQueueUpdate(newObj *kueue.ClusterQueue) field.ErrorList {
 	return ValidateClusterQueue(newObj)
 }
 
+func validateTotalFlavors(resourceGroups []kueue.ResourceGroup, path *field.Path) field.ErrorList {
+	var allErrs field.ErrorList
+	total := 0
+	for _, rg := range resourceGroups {
+		total += len(rg.Flavors)
+	}
+	if total > 256 {
+		allErrs = append(allErrs, field.Invalid(path, total,
+			fmt.Sprintf("total number of flavors across all resourceGroups must be ≤ 256, got %d", total)))
+	}
+	return allErrs
+}
+
+func validateTotalCoveredResources(resourceGroups []kueue.ResourceGroup, path *field.Path) field.ErrorList {
+	var allErrs field.ErrorList
+	total := 0
+	for _, rg := range resourceGroups {
+		total += len(rg.CoveredResources)
+	}
+	if total > 256 {
+		allErrs = append(allErrs, field.Invalid(path, total,
+			fmt.Sprintf("total number of covered resources across all resourceGroups must be ≤ 256, got %d", total)))
+	}
+	return allErrs
+}
+
+func validateFlavorResourceCombinations(resourceGroups []kueue.ResourceGroup, path *field.Path) field.ErrorList {
+	var allErrs field.ErrorList
+	for i, rg := range resourceGroups {
+		total := 0
+		for _, fqs := range rg.Flavors {
+			total += len(fqs.Resources)
+		}
+		if total > 512 {
+			allErrs = append(allErrs, field.Invalid(
+				path.Index(i),
+				total,
+				fmt.Sprintf("number of flavor-resource combinations in a resourceGroup must be ≤ 512, got %d", total),
+			))
+		}
+	}
+	return allErrs
+}
+
 func validatePreemption(preemption *kueue.ClusterQueuePreemption, path *field.Path) field.ErrorList {
 	var allErrs field.ErrorList
 	if preemption.ReclaimWithinCohort == kueue.PreemptionPolicyNever &&
diff --git a/pkg/workload/workload.go b/pkg/workload/workload.go
index bc25cd0e7..ff3e1d199 100644
--- a/pkg/workload/workload.go
+++ b/pkg/workload/workload.go
@@ -698,42 +698,49 @@ func BaseSSAWorkload(w *kueue.Workload, strict bool) *kueue.Workload {
 	return wlCopy
 }
 
-// SetQuotaReservation applies the provided admission to the workload.
-// The WorkloadAdmitted and WorkloadEvicted are added or updated if necessary.
+// SetQuotaReservation records that quota has been reserved for the given Workload
+// in the specified ClusterQueue and updates the Workload status accordingly.
+//
+// Effects:
+//   - Sets w.Status.Admission to the provided admission.
+//   - Adds or updates a Condition of type kueue.WorkloadQuotaReserved with
+//     Status=True, Reason="QuotaReserved", Message="Quota reserved in ClusterQueue <name>",
+//     and ObservedGeneration set to w.Generation. The message is truncated via
+//     api.TruncateConditionMessage.
+//   - Resets any active "evicted" and "preempted" conditions by invoking
+//     resetActiveCondition for kueue.WorkloadEvicted and kueue.WorkloadPreempted.
 func SetQuotaReservation(w *kueue.Workload, admission *kueue.Admission, clock clock.Clock) {
 	w.Status.Admission = admission
-	message := fmt.Sprintf("Quota reserved in ClusterQueue %s", w.Status.Admission.ClusterQueue)
-	admittedCond := metav1.Condition{
+
+	reason := "QuotaReserved"
+
+	apimeta.SetStatusCondition(&w.Status.Conditions, metav1.Condition{
 		Type:               kueue.WorkloadQuotaReserved,
 		Status:             metav1.ConditionTrue,
-		Reason:             "QuotaReserved",
-		Message:            api.TruncateConditionMessage(message),
+		Reason:             reason,
+		Message:            api.TruncateConditionMessage(fmt.Sprintf("Quota reserved in ClusterQueue %s", admission.ClusterQueue)),
 		ObservedGeneration: w.Generation,
-	}
-	apimeta.SetStatusCondition(&w.Status.Conditions, admittedCond)
+		LastTransitionTime: metav1.NewTime(clock.Now()),
+	})
 
-	// reset Evicted condition if present.
-	if evictedCond := apimeta.FindStatusCondition(w.Status.Conditions, kueue.WorkloadEvicted); evictedCond != nil {
-		evictedCond.Status = metav1.ConditionFalse
-		evictedCond.Reason = "QuotaReserved"
-		evictedCond.Message = preservePreviousMessage(evictedCond.Message)
-		evictedCond.LastTransitionTime = metav1.NewTime(clock.Now())
-	}
-	// reset Preempted condition if present.
-	if preemptedCond := apimeta.FindStatusCondition(w.Status.Conditions, kueue.WorkloadPreempted); preemptedCond != nil {
-		preemptedCond.Status = metav1.ConditionFalse
-		preemptedCond.Reason = "QuotaReserved"
-		preemptedCond.Message = preservePreviousMessage(preemptedCond.Message)
-		preemptedCond.LastTransitionTime = metav1.NewTime(clock.Now())
-	}
+	resetActiveCondition(&w.Status.Conditions, w.Generation, kueue.WorkloadEvicted, reason, clock)
+	resetActiveCondition(&w.Status.Conditions, w.Generation, kueue.WorkloadPreempted, reason, clock)
 }
 
-func preservePreviousMessage(message string) string {
-	if !strings.HasPrefix(message, "Previously: ") {
-		return api.TruncateConditionMessage("Previously: " + message)
+func resetActiveCondition(conds *[]metav1.Condition, gen int64, condType, reason string, clock clock.Clock) {
+	prev := apimeta.FindStatusCondition(*conds, condType)
+	// Ignore not found or inactive condition.
+	if prev == nil || prev.Status != metav1.ConditionTrue {
+		return
 	}
-
-	return message
+	apimeta.SetStatusCondition(conds, metav1.Condition{
+		Type:               condType,
+		Status:             metav1.ConditionFalse,
+		Reason:             reason,
+		Message:            api.TruncateConditionMessage("Previously: " + prev.Message),
+		ObservedGeneration: gen,
+		LastTransitionTime: metav1.NewTime(clock.Now()),
+	})
 }
 
 // NeedsSecondPass checks if the second pass of scheduling is needed for the
diff --git a/site/content/en/docs/concepts/topology_aware_scheduling.md b/site/content/en/docs/concepts/topology_aware_scheduling.md
index c08521449..3a9232a42 100644
--- a/site/content/en/docs/concepts/topology_aware_scheduling.md
+++ b/site/content/en/docs/concepts/topology_aware_scheduling.md
@@ -6,7 +6,14 @@ description: >
   Allows scheduling of Pods based on the topology of nodes in a data center.
 ---
 
-{{< feature-state state="alpha" for_version="v0.9" >}}
+{{< feature-state state="beta" for_version="v0.14" >}}
+{{% alert title="Note" color="primary" %}}
+`TopologyAwareScheduling` is currently a beta feature and is enabled by default.
+
+You can disable it by editing the `TopologyAwareScheduling` feature gate. Refer to the
+[Installation guide](/docs/installation/#change-the-feature-gates-configuration)
+for instructions on configuring feature gates.
+{{% /alert %}}
 
 It is common that AI/ML workloads require a significant amount of pod-to-pod
 communication. Therefore the network bandwidth between the running Pods
@@ -64,15 +71,17 @@ domain (like a given rack) by:
 ### Admin-facing APIs
 
 As an admin, in order to enable the feature you need to:
-1. ensure the `TopologyAwareScheduling` feature gate is enabled
-2. create at least one instance of the `Topology` API
-3. reference the `Topology` API from a dedicated ResourceFlavor by the
+1. create at least one instance of the `Topology` API
+2. reference the `Topology` API from a dedicated ResourceFlavor by the
    `.spec.topologyName` field
 
 #### Example
 
 {{< include "examples/tas/sample-queues.yaml" "yaml" >}}
 
+An example for managing GPUs:
+{{< include "examples/tas/sample-gpu-queues.yaml" "yaml" >}}
+
 ### User-facing APIs
 
 Once TAS is configured and ready to be used, you can create Jobs with the
@@ -123,10 +132,13 @@ to see how you can configure Kueue if you want to restrict scheduling to the
 newly provisioned nodes (assuming the provisioning class supports it).
 
 ### Hot swap support
+{{< feature-state state="beta" for_version="v0.14" >}}
 {{% alert title="Note" color="primary" %}}
-To enable the feature, you have to set the [feature gate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/)
-`TASFailedNodeReplacement` to `true` and the lowest topological label has to be
-`kubernetes.io/hostname`. This feature was introduced to Kueue in version 0.12.
+`TASFailedNodeReplacement` is currently a beta feature and is enabled by default.
+
+You can disable it by editing the `TASFailedNodeReplacement` feature gate. Refer to the
+[Installation guide](/docs/installation/#change-the-feature-gates-configuration)
+for instructions on configuring feature gates.
 {{% /alert %}}
 
 When the lowest level of Topology is set to node, TAS finds a fixed assignment
@@ -141,11 +153,23 @@ all the affected workloads, without changing the rest of the topology assignment
 Currently this works only for a single node failure at the time and in case of multiple failures,
 the workload gets evicted.
 
+#### Replace Node on Pod termination 
+
+{{< feature-state state="beta" for_version="v0.14" >}}
+{{% alert title="Note" color="primary" %}}
+`TASReplaceNodeOnPodTermination` is currently a beta feature and is enabled by default.
+
+You can disable it by editing the `TASReplaceNodeOnPodTermination` feature gate. Refer to the
+[Installation guide](/docs/installation/#change-the-feature-gates-configuration)
+for instructions on configuring feature gates.
+{{% /alert %}}
+
 By default, the node is assumed to have failed if its `conditions.Status.Ready`
 is not `True` for at least 30 seconds or if the node is missing (removed from the cluster).
-Since Kueue v0.13, you can enable the `TASReplaceNodeOnPodTermination` feature gate, which adds an additional heuristic:
+Since Kueue v0.13, the `TASReplaceNodeOnPodTermination` feature, introduced an additional heuristic:
 a node is also considered failed if it is `NotReady` and the workload's Pods scheduled on that node are either terminated or terminating.
 If this happens Kueue will immediately look for replacement without waiting 30 seconds.
+
 Note that those two heuristics are mutually exclusive and depend on the value of the `TASReplaceNodeOnPodTermination` feature gate.
 
 Note that finding a replacement node that meets all the requirements (e.g. the same type of machine placed in the rack that Kueue had previously assigned to the workload) may not always be possible.
@@ -155,9 +179,13 @@ and configuring `waitForPodsReady.recoveryTimeout`, to prevent the workloads fro
 waiting for the replacement indefinitely.
 
 #### Fast Hot swap
+{{< feature-state state="beta" for_version="v0.14" >}}
 {{% alert title="Note" color="primary" %}}
-To enable the feature, you have to set the [feature gate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/)
-`TASFailedNodeReplacementFailFast` to `true`. This feature was introduced to Kueue in version 0.13.
+`TASFailedNodeReplacementFailFast` is currently a beta feature and is enabled by default.
+
+You can disable it by editing the `TASFailedNodeReplacementFailFast` feature gate. Refer to the
+[Installation guide](/docs/installation/#change-the-feature-gates-configuration)
+for instructions on configuring feature gates.
 {{% /alert %}}
 
 By default, Kueue tries to find a replacement for a failed node until it succeeds or until the workload is evicted (for example, by `waitForPodsReady.recoveryTimeout`). To prevent Kueue from retrying indefinitely, you can enable the `TASFailedNodeReplacementFailFast` feature gate. When enabled, Kueue will only attempt to find a replacement node once. If it fails, it will not try again, and the workload will get evicted and requeued.
@@ -192,11 +220,6 @@ Here are a few scenarios that can happen when both `TASReplaceNodeOnPodTerminati
 
 ##### Feature Gate Interaction Matrix
 
-{{% alert title="Note" color="primary" %}}
-`TASFailedNodeReplacement` feature gate is available since Kueue v0.12.
-`TASReplaceNodeOnPodTermination` and `TASFailedNodeReplacementFailFast` are available since Kueue v0.13.
-{{% /alert %}}
-
 The following table summarizes the behavior based on the combination of the feature gates. If `TASFailedNodeReplacement` is `false`, the other two gates have no effect.
 
 | `TASFailedNodeReplacement` | `TASReplaceNodeOnPodTermination` | `TASFailedNodeReplacementFailFast` | End Behavior                                                                                                                                                                                                                                                                                        |
@@ -209,24 +232,7 @@ The following table summarizes the behavior based on the combination of the feat
 
 **Recommended configuration**
 
-We recommend to set all three feature gates to true to have the fastest feedback loop on workload that encountered node failure.
-
-The recommended configuration looks is express in Kueue's manager deployment as following:
-```diff
-kind: Deployment
-...
-spec:
-  ...
-  template:
-    ...
-    spec:
-      containers:
-      - name: manager
-        args:
-        - --config=/controller_manager_config.yaml
-        - --zap-log-level=2
-+       - --feature-gates=TopologyAwareScheduling=true,TASFailedNodeReplacement=true,TASFailedNodeReplacementFailFast=true,TASReplaceNodeOnPodTermination=true
-```
+We recommend keeping all three feature gates enabled to ensure the fastest feedback loop for workloads affected by node failures.
 
 ### Limitations
 
@@ -237,6 +243,7 @@ features, including:
   ClusterAutoscaler cannot provision nodes that satisfy the domain constraint,
 - a ClusterQueue for [MultiKueue](multikueue.md) referencing a ResourceFlavor
 with Topology name (`.spec.topologyName`) is marked as inactive.
+- The taints on the nodes are not respected unless `kubernetes.io/hostname` is on the lowest topology level.
 
 These usage scenarios are considered to be supported in the future releases
 of Kueue.
diff --git a/site/content/en/docs/installation/_index.md b/site/content/en/docs/installation/_index.md
index f440f87dd..a7f0bd717 100644
--- a/site/content/en/docs/installation/_index.md
+++ b/site/content/en/docs/installation/_index.md
@@ -284,10 +284,13 @@ spec:
 | `HierarchicalCohort`                          | `true`  | Beta  | 0.11  |       |
 | `ObjectRetentionPolicies`                     | `false` | Alpha | 0.12  | 0.12  |
 | `ObjectRetentionPolicies`                     | `true`  | Beta  | 0.13  |       |
-| `TASFailedNodeReplacement`                    | `false` | Alpha | 0.12  |       |
+| `TASFailedNodeReplacement`                    | `false` | Alpha | 0.12  | 0.13  |
+| `TASFailedNodeReplacement`                    | `true`  | Beta  | 0.14  |       |
 | `AdmissionFairSharing`                        | `false` | Alpha | 0.12  |       |
-| `TASFailedNodeReplacementFailFast`            | `false` | Alpha | 0.12  |       |
-| `TASReplaceNodeOnPodTermination`              | `false` | Alpha | 0.13  |       |
+| `TASFailedNodeReplacementFailFast`            | `false` | Alpha | 0.12  | 0.13  |
+| `TASFailedNodeReplacementFailFast`            | `true`  | Beta  | 0.14  |       |
+| `TASReplaceNodeOnPodTermination`              | `false` | Alpha | 0.13  | 0.13  |
+| `TASReplaceNodeOnPodTermination`              | `true`  | Beta  | 0.14  |       |
 | `ElasticJobsViaWorkloadSlices`                | `false` | Alpha | 0.13  |       |
 | `ManagedJobsNamespaceSelectorAlwaysRespected` | `false` | Alpha | 0.13  |       |
 | `FlavorFungibilityImplicitPreferenceDefault`  | `false` | Alpha | 0.13  |       |
diff --git a/site/content/en/docs/reference/kueue-config.v1beta1.md b/site/content/en/docs/reference/kueue-config.v1beta1.md
index 8d436fcfb..a8a5ff264 100644
--- a/site/content/en/docs/reference/kueue-config.v1beta1.md
+++ b/site/content/en/docs/reference/kueue-config.v1beta1.md
@@ -767,6 +767,43 @@ if the connection with its reserving worker cluster is lost.</p>
 </ul>
 </td>
 </tr>
+<tr><td><code>externalFrameworks</code><br/>
+<a href="#MultiKueueExternalFramework"><code>[]MultiKueueExternalFramework</code></a>
+</td>
+<td>
+   <p>ExternalFrameworks defines a list of external frameworks that should be supported
+by the generic MultiKueue adapter. Each entry defines how to handle a specific
+GroupVersionKind (GVK) for MultiKueue operations.</p>
+</td>
+</tr>
+</tbody>
+</table>
+
+## `MultiKueueExternalFramework`     {#MultiKueueExternalFramework}
+    
+
+**Appears in:**
+
+- [MultiKueue](#MultiKueue)
+
+
+<p>MultiKueueExternalFramework defines a framework that is not built-in.</p>
+
+
+<table class="table">
+<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
+<tbody>
+    
+  
+<tr><td><code>name</code> <B>[Required]</B><br/>
+<code>string</code>
+</td>
+<td>
+   <p>Name is the GVK of the resource that are
+managed by external controllers
+the expected format is <code>Kind.version.group.com</code>.</p>
+</td>
+</tr>
 </tbody>
 </table>
 
diff --git a/site/content/en/docs/reference/kueue.v1beta1.md b/site/content/en/docs/reference/kueue.v1beta1.md
index 5dfee78a6..84ce81ddf 100644
--- a/site/content/en/docs/reference/kueue.v1beta1.md
+++ b/site/content/en/docs/reference/kueue.v1beta1.md
@@ -975,7 +975,7 @@ It must be a DNS (RFC 1123) and has the maximum length of 253 characters.</p>
 Each resource group defines the list of resources and a list of flavors
 that provide quotas for these resources.
 Each resource and each flavor can only form part of one resource group.
-resourceGroups can be up to 16.</p>
+resourceGroups can be up to 16, with a max of 256 total flavors across all groups.</p>
 </td>
 </tr>
 <tr><td><code>cohort</code> <B>[Required]</B><br/>
@@ -1595,7 +1595,7 @@ have.</p>
 </td>
 <td>
    <p>topology is the topology that associated with this ResourceFlavor.</p>
-<p>This is an alpha field and requires enabling the TopologyAwareScheduling
+<p>This is a beta field and requires enabling the TopologyAwareScheduling
 feature gate.</p>
 </td>
 </tr>
@@ -2700,7 +2700,8 @@ nodes matching to the Resource Flavor node labels.</p>
    <p>coveredResources is the list of resources covered by the flavors in this
 group.
 Examples: cpu, memory, vendor.com/gpu.
-The list cannot be empty and it can contain up to 16 resources.</p>
+The list cannot be empty and it can contain up to 64 resources. With a total
+of up to 256 covered resources across all resource groups in the ClusterQueue.</p>
 </td>
 </tr>
 <tr><td><code>flavors</code> <B>[Required]</B><br/>
@@ -2713,7 +2714,8 @@ Typically, different flavors represent different hardware models
 cpus).
 Each flavor MUST list all the resources listed for this group in the same
 order as the .resources field.
-The list cannot be empty and it can contain up to 16 flavors.</p>
+The list cannot be empty and it can contain up to 64 flavors, with a max of
+256 total flavors across all resource groups in the ClusterQueue.</p>
 </td>
 </tr>
 </tbody>
diff --git a/site/content/en/docs/reference/metrics.md b/site/content/en/docs/reference/metrics.md
index c8fdcbea9..5bdb7a5a9 100644
--- a/site/content/en/docs/reference/metrics.md
+++ b/site/content/en/docs/reference/metrics.md
@@ -28,7 +28,7 @@ Use the following metrics to monitor the status of your ClusterQueues:
 | -------------------------------------------- | ----------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | `kueue_pending_workloads`                  | Gauge     | The number of pending workloads.                                                    | `cluster_queue`: the name of the ClusterQueue<br> `status`: possible values are `active` or `inadmissible`                                                                                             |
 | `kueue_quota_reserved_workloads_total`     | Counter   | The total number of quota reserved workloads.                                       | `cluster_queue`: the name of the ClusterQueue<br> workload_priority_class: the workload priority class name                                                                                            |
-| `kueue_quota_reserved_wait_time_seconds`   | Histogram | The time between a workload was created or requeued until it got quota reservation. | `cluster_queue`: the name of the ClusterQueue                                                                                                                                                          |
+| `kueue_quota_reserved_wait_time_seconds`   | Histogram | The time between a workload was created or requeued until it got quota reservation. | `cluster_queue`: the name of the ClusterQueue<br> workload_priority_class: the workload priority class name                                                                                            |
 | `kueue_admitted_workloads_total`           | Counter   | The total number of admitted workloads.                                             | `cluster_queue`: the name of the ClusterQueue<br> workload_priority_class: the workload priority class name                                                                                               |
 | `kueue_evicted_workloads_total`            | Counter   | The total number of evicted workloads.                                              | `cluster_queue`: the name of the ClusterQueue<br> `reason`: Possible values are `Preempted`, `PodsReadyTimeout`, `AdmissionCheck`, `ClusterQueueStopped` or `Deactivated`<br> workload_priority_class: the workload priority class name |
 | `kueue_evicted_workloads_once_total`       | Counter   | The number of unique workload evictions per 'cluster_queue'                         | `cluster_queue`: the name of the ClusterQueue<br> `reason`: Possible values are `Preempted`, `PodsReadyTimeout`, `AdmissionCheck`, `ClusterQueueStopped` or `Deactivated`<br> `detailedReason`: specifies a finer-grained explanation that complements the eviction cause<br> workload_priority_class: the workload priority class name |
diff --git a/site/content/en/docs/tasks/run/multikueue/external-frameworks.md b/site/content/en/docs/tasks/run/multikueue/external-frameworks.md
new file mode 100644
index 000000000..a603ee44b
--- /dev/null
+++ b/site/content/en/docs/tasks/run/multikueue/external-frameworks.md
@@ -0,0 +1,80 @@
+---
+title: "Run External Framework Jobs in Multi-Cluster"
+linkTitle: "External Framework Jobs"
+weight: 5
+date: 2025-09-19
+description: >
+  Run a MultiKueue scheduled External Framework Job.
+---
+
+{{< feature-state state="alpha" for_version="v0.14" >}}
+
+## Before you begin
+
+1. Check the [MultiKueue installation guide](/docs/tasks/manage/setup_multikueue) on how to properly setup MultiKueue clusters.
+
+2. Enable the `MultiKueueAdaptersForCustomJobs` feature gate. This feature is in Alpha and is disabled by default.
+
+   To enable the feature, add the `--feature-gates=MultiKueueAdaptersForCustomJobs=true` flag to the Kueue controller manager's arguments. For example, you can apply it to your deployment with the following command:
+
+   ```bash
+   kubectl patch deployment kueue-controller-manager -n kueue-system \
+     --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--feature-gates=MultiKueueAdaptersForCustomJobs=true"}]'
+   ```
+
+3. Ensure the Kueue controller has permissions for your external GVK. Example for Tekton PipelineRuns:
+
+   ```yaml
+   apiVersion: rbac.authorization.k8s.io/v1
+   kind: ClusterRole
+   metadata:
+     name: kueue-external-frameworks
+   rules:
+   - apiGroups: ["tekton.dev"]
+     resources: ["pipelineruns", "pipelineruns/status"]
+     verbs: ["get", "list", "watch", "patch", "update"]
+   ```
+
+## MultiKueue integration
+
+MultiKueue External Frameworks Support allows you to configure MultiKueue to work with any Custom Resource (CR) that follows a Job-like pattern. This is particularly useful for integrating CRDs such as:
+
+- Tekton `PipelineRun`
+- Argo `Workflow`
+- Other custom job types
+
+To be managed by the generic MultiKueue adapter, a Custom Resource must have a `.spec.managedBy` field. When a CR is intended to be managed by MultiKueue, this field must be set to `"kueue.x-k8s.io/multikueue"`. The adapter uses this field to identify which objects to manage.
+
+External frameworks are configured in the Kueue `Configuration` object. The settings are located under `multikueue.externalFrameworks`. This field holds a list of frameworks to be enabled.
+
+Each entry in the `externalFrameworks` list is an object with the following field:
+
+| Field      | Type   | Required | Description                                       |
+|------------|--------|----------|---------------------------------------------------|
+| `name`     | string | Yes      | GVK of the resource in the format `Kind.version.group`. |
+
+## Example: Tekton PipelineRun
+
+To demonstrate how to configure the adapter, let's use Tekton `PipelineRun` as an example.
+
+First, update your Kueue configuration to include `PipelineRun` in the `externalFrameworks` list:
+
+```yaml
+apiVersion: config.kueue.x-k8s.io/v1beta1
+kind: Configuration
+data:
+  multikueue:
+    externalFrameworks:
+      - name: "PipelineRun.v1.tekton.dev"
+```
+
+Once configured, the generic MultiKueue adapter will watch for `PipelineRun` resources that have `.spec.managedBy` set to `"kueue.x-k8s.io/multikueue"` and manage them as it does with other supported job types. This allows Kueue to handle resource management for `PipelineRun` objects across multiple clusters.
+
+{{% alert title="Note" color="primary" %}}
+Note: Kueue defaults the `spec.managedBy` field to `kueue.x-k8s.io/multikueue` on the management cluster for External Framework Jobs.
+
+This allows the External Framework controller to ignore the Jobs managed by MultiKueue on the management cluster, and in particular skip creation of the wrapped resources.
+
+The resources are created and the actual computation will happen on the mirror copy of the External Framework Job on the selected worker cluster.
+The mirror copy of the External Framework Job does not have the field set.
+{{% /alert %}}
diff --git a/site/content/zh-CN/docs/reference/metrics.md b/site/content/zh-CN/docs/reference/metrics.md
index 69b129eb2..6ef2e62d9 100644
--- a/site/content/zh-CN/docs/reference/metrics.md
+++ b/site/content/zh-CN/docs/reference/metrics.md
@@ -28,7 +28,7 @@ Use the following metrics to monitor the status of your ClusterQueues:
 | -------------------------------------------- | ----------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | `kueue_pending_workloads`                  | Gauge     | The number of pending workloads.                                                    | `cluster_queue`: the name of the ClusterQueue<br> `status`: possible values are `active` or `inadmissible`                                                                                             |
 | `kueue_quota_reserved_workloads_total`     | Counter   | The total number of quota reserved workloads.                                       | `cluster_queue`: the name of the ClusterQueue<br> workload_priority_class: the workload priority class name                                                                                            |
-| `kueue_quota_reserved_wait_time_seconds`   | Histogram | The time between a workload was created or requeued until it got quota reservation. | `cluster_queue`: the name of the ClusterQueue                                                                                                                                                          |
+| `kueue_quota_reserved_wait_time_seconds`   | Histogram | The time between a workload was created or requeued until it got quota reservation. | `cluster_queue`: the name of the ClusterQueue<br> workload_priority_class: the workload priority class name                                                                                            |
 | `kueue_admitted_workloads_total`           | Counter   | The total number of admitted workloads.                                             | `cluster_queue`: the name of the ClusterQueue<br> workload_priority_class: the workload priority class name                                                                                            |
 | `kueue_evicted_workloads_total`            | Counter   | The total number of evicted workloads.                                              | `cluster_queue`: the name of the ClusterQueue<br> `reason`: Possible values are `Preempted`, `PodsReadyTimeout`, `AdmissionCheck`, `ClusterQueueStopped` or `Deactivated`<br> workload_priority_class: the workload priority class name |
 | `kueue_evicted_workloads_once_total`       | Counter   | The number of unique workload evictions per 'cluster_queue'                         | `cluster_queue`: the name of the ClusterQueue<br> `reason`: Possible values are `Preempted`, `PodsReadyTimeout`, `AdmissionCheck`, `ClusterQueueStopped` or `Deactivated`<br> `detailedReason`: specifies a finer-grained explanation that complements the eviction cause<br> workload_priority_class: the workload priority class name |
diff --git a/site/static/examples/tas/sample-gpu-queues.yaml b/site/static/examples/tas/sample-gpu-queues.yaml
new file mode 100644
index 000000000..e69efc128
--- /dev/null
+++ b/site/static/examples/tas/sample-gpu-queues.yaml
@@ -0,0 +1,48 @@
+apiVersion: kueue.x-k8s.io/v1alpha1
+kind: Topology
+metadata:
+  name: "default"
+spec:
+  levels:
+  - nodeLabel: "cloud.provider.com/topology-block"
+  - nodeLabel: "cloud.provider.com/topology-rack"
+  - nodeLabel: "kubernetes.io/hostname" # Taints on nodes are only respected if this label is the lowest topology level.
+---
+kind: ResourceFlavor
+apiVersion: kueue.x-k8s.io/v1beta1
+metadata:
+  name: "tas-flavor"
+spec:
+  nodeLabels:
+    cloud.provider.com/node-group: "tas-group"
+  topologyName: "default"
+  tolerations:
+  - key: "nvidia.com/gpu" # Most cloud providers auto inject the toleration to the pods based on the nodeSelector via a webhook. However, TAS isn't aware of the webhook, so you need to manually add the toleration here.
+    operator: "Exists"
+    effect: "NoSchedule"
+---
+apiVersion: kueue.x-k8s.io/v1beta1
+kind: ClusterQueue
+metadata:
+  name: "tas-cluster-queue"
+spec:
+  namespaceSelector: {} # match all.
+  resourceGroups:
+  - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
+    flavors:
+    - name: "tas-flavor"
+      resources:
+      - name: "cpu"
+        nominalQuota: 100
+      - name: "memory"
+        nominalQuota: 100Gi
+      - name: "nvidia.com/gpu"
+        nominalQuota: 16
+---
+apiVersion: kueue.x-k8s.io/v1beta1
+kind: LocalQueue
+metadata:
+  namespace: "default"
+  name: "tas-user-queue"
+spec:
+  clusterQueue: "tas-cluster-queue"



Test patch:
diff --git a/charts/kueue/tests/kueue_test.yaml b/charts/kueue/tests/kueue_test.yaml
index 5270148a8..2bace36bc 100644
--- a/charts/kueue/tests/kueue_test.yaml
+++ b/charts/kueue/tests/kueue_test.yaml
@@ -57,10 +57,25 @@ tests:
             ingressClassName: "ingress-external"
             host: "backend.kueueviz.fr"
             tlsSecretName: "kueueviz-frontend-tls"
+        frontend:
+          env:
+            - name: REACT_APP_WEBSOCKET_URL
+              value: "wss://backend.kueueviz.fr"
     asserts:
       - equal:
           path: spec.template.spec.containers[0].env[0].value
           value: "wss://backend.kueueviz.fr"
+  - it: should not render the tls if not set for backend ingress
+    template: kueueviz/backend-ingress.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        backend:
+          ingress:
+            tlsSecretName: ""
+    asserts:
+      - notExists:
+          path: spec.tls[0]
   - it: Should Render Ingress for Backend Component
     template: kueueviz/backend-ingress.yaml
     set:
@@ -68,6 +83,9 @@ tests:
       kueueViz:  
         backend:
           ingress:
+            annotations: 
+              nginx.ingress.kubernetes.io/rewrite-target: "/"
+              nginx.ingress.kubernetes.io/ssl-redirect: "true"
             ingressClassName: "nginx-external-ingress"
             host: "backend.kueueviz.in"
             tlsSecretName: "kueueviz-backend-tls-secret"
@@ -81,7 +99,34 @@ tests:
       - equal:
           path: spec.tls[0].secretName
           value: "kueueviz-backend-tls-secret"
-
+      - equal:
+          path: metadata.annotations["nginx.ingress.kubernetes.io/rewrite-target"]
+          value: "/"
+      - equal:
+          path: metadata.annotations["nginx.ingress.kubernetes.io/ssl-redirect"]
+          value: "true"
+  - it: should not render annotations if not set for backend ingress
+    template: kueueviz/backend-ingress.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        backend:
+          ingress:
+            annotations: ""
+    asserts:
+      - notExists:
+          path: metadata.annotations
+  - it: should not render the tls if not set for frontend ingress
+    template: kueueviz/frontend-ingress.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        frontend:
+          ingress:
+            tlsSecretName: ""
+    asserts:
+      - notExists:
+          path: spec.tls[0]
   - it: Should Render Ingress for Frontend Component
     template: kueueviz/frontend-ingress.yaml
     set:
@@ -90,6 +135,9 @@ tests:
         ingressClassName: "nginx-external"
         frontend:
           ingress:
+            annotations: 
+              nginx.ingress.kubernetes.io/rewrite-target: "/"
+              nginx.ingress.kubernetes.io/ssl-redirect: "true"
             ingressClassName: "nginx-external"
             host: "frontend.kueueviz.in"
             tlsSecretName: "frontend-tls"
@@ -103,6 +151,23 @@ tests:
       - equal:
           path: spec.tls[0].secretName
           value: "frontend-tls"
+      - equal:
+          path: metadata.annotations["nginx.ingress.kubernetes.io/rewrite-target"]
+          value: "/"
+      - equal:
+          path: metadata.annotations["nginx.ingress.kubernetes.io/ssl-redirect"]
+          value: "true"
+  - it: should not render annotations if not set for frontend ingress
+    template: kueueviz/frontend-ingress.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        frontend:
+          ingress:
+            annotations: ""
+    asserts:
+      - notExists:
+          path: metadata.annotations
   - it: should set the imagePullSecrets for backend deployment
     template: kueueviz/backend-deployment.yaml
     set:
@@ -126,4 +191,64 @@ tests:
     asserts:
       - equal:
           path: spec.template.spec.imagePullSecrets[0].name
-          value: "my-secret"
\ No newline at end of file
+          value: "my-secret"
+  - it: should set the environment variables for backend deployment
+    template: kueueviz/backend-deployment.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        backend:
+          env:
+            - name: KUEUEVIZ_ALLOWED_ORIGINS
+              value: "frontend.kueueviz.local"
+            - name: KUEUEVIZ_PORT
+              value: "8080"
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].env[0].name
+          value: KUEUEVIZ_ALLOWED_ORIGINS
+      - equal:
+          path: spec.template.spec.containers[0].env[0].value
+          value: "frontend.kueueviz.local"
+      - equal:
+          path: spec.template.spec.containers[0].env[1].name
+          value: KUEUEVIZ_PORT
+      - equal:
+          path: spec.template.spec.containers[0].env[1].value
+          value: "8080"
+  - it: should not set the environment variables if not set for backend deployment
+    template: kueueviz/backend-deployment.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        backend:
+          env: []
+    asserts:
+      - notExists:
+          path: spec.template.spec.containers[0].env[0]
+  - it: should set the environment variables for frontend deployment
+    template: kueueviz/frontend-deployment.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        frontend:
+          env:
+            - name: REACT_APP_WEBSOCKET_URL
+              value: "wss://backend.kueueviz.local"
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].env[0].name
+          value: REACT_APP_WEBSOCKET_URL
+      - equal:
+          path: spec.template.spec.containers[0].env[0].value
+          value: "wss://backend.kueueviz.local"
+  - it: should not set the environment variables if not set for frontend deployment
+    template: kueueviz/frontend-deployment.yaml
+    set:
+      enableKueueViz: true
+      kueueViz:
+        frontend:
+          env: []
+    asserts:
+      - notExists:
+          path: spec.template.spec.containers[0].env[0]
diff --git a/charts/kueue/tests/manager_config_test.yaml b/charts/kueue/tests/manager_config_test.yaml
new file mode 100644
index 000000000..7b519fe08
--- /dev/null
+++ b/charts/kueue/tests/manager_config_test.yaml
@@ -0,0 +1,105 @@
+suite: test manager-config configmap
+templates:
+  - manager/manager-config.yaml
+tests:
+  - it: should not inject internalCertManagement config when enableCertManager is true
+    set:
+      enableCertManager: true
+    asserts:
+      - notMatchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "internalCertManagement:\n  enable: true"
+
+  - it: should properly template the webhook using the release name and defaults
+    release:
+      name: custom-kueue-name
+    asserts:
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "webhookServiceName: custom-kueue-name-webhook-service"
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "webhookSecretName: custom-kueue-name-webhook-server-cert"
+
+  - it: should properly template the webhook using the fullnameOverride and defaults
+    set:
+      fullnameOverride: "full-override"
+    asserts:
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "webhookServiceName: full-override-webhook-service"
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "webhookSecretName: full-override-webhook-server-cert"
+
+  - it: should parse controllerManagerConfigYaml and preserve custom config
+    set:
+      enableCertManager: false
+      managerConfig:
+        controllerManagerConfigYaml: |
+          leaderElection:
+            leaderElect: true
+          metrics:
+            bindAddress: "0.0.0.0:8080"
+    asserts:
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "leaderElection:\n  leaderElect: true"
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "metrics:\n  bindAddress: 0.0.0.0:8080"
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "internalCertManagement:\n  enable: true"
+
+  - it: should allow controllerManagerConfigYaml to supersede defaults
+    set:
+      enableCertManager: false
+      managerConfig:
+        controllerManagerConfigYaml: |
+          internalCertManagement:
+            enable: false
+            webhookServiceName: foo
+            webhookSecretName: bar
+          leaderElection:
+            leaderElect: false
+    asserts:
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "leaderElection:\n  leaderElect: false"
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "internalCertManagement:\n  enable: false\n  webhookSecretName: bar\n  webhookServiceName: foo"
+
+  - it: should not add internalCertManagement when enableCertManager is true and controllerManagerConfigYaml is provided
+    set:
+      enableCertManager: true
+      managerConfig:
+        controllerManagerConfigYaml: |
+          leaderElection:
+            leaderElect: true
+          metrics:
+            bindAddress: "0.0.0.0:8080"
+    asserts:
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "leaderElection:\n  leaderElect: true"
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "metrics:\n  bindAddress: 0.0.0.0:8080"
+      - notMatchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "internalCertManagement:"
+
+  - it: should handle an empty controllerManagerConfigYaml
+    release:
+      name: testing
+    set:
+      enableCertManager: false
+      managerConfig:
+        controllerManagerConfigYaml: "{}"
+    asserts:
+      - matchRegex:
+          path: data["controller_manager_config.yaml"]
+          pattern: "internalCertManagement:\n  enable: true\n  webhookSecretName: testing-kueue-webhook-server-cert\n  webhookServiceName: testing-kueue-webhook-service"
+
diff --git a/pkg/cache/scheduler/fair_sharing_test.go b/pkg/cache/scheduler/fair_sharing_test.go
index b125fb7ef..3ef7454ee 100644
--- a/pkg/cache/scheduler/fair_sharing_test.go
+++ b/pkg/cache/scheduler/fair_sharing_test.go
@@ -42,7 +42,7 @@ func TestDominantResourceShare(t *testing.T) {
 	type fairSharingResult struct {
 		Name     string
 		NodeType nodeType
-		DrValue  int
+		DrValue  int64
 		DrName   corev1.ResourceName
 	}
 
@@ -728,7 +728,8 @@ func TestDominantResourceShare(t *testing.T) {
 			cacheCohortsMap := cache.hm.Cohorts()
 			gotCache := make([]fairSharingResult, 0, len(cacheClusterQueuesMap)+len(cacheCohortsMap))
 			for _, cq := range cacheClusterQueuesMap {
-				drVal, drName := dominantResourceShare(cq, tc.flvResQ)
+				drs := dominantResourceShare(cq, tc.flvResQ)
+				drVal, drName := drs.roundedWeightedShare()
 				gotCache = append(gotCache, fairSharingResult{
 					Name:     string(cq.Name),
 					NodeType: nodeTypeCq,
@@ -737,7 +738,8 @@ func TestDominantResourceShare(t *testing.T) {
 				})
 			}
 			for _, cohort := range cacheCohortsMap {
-				drVal, drName := dominantResourceShare(cohort, tc.flvResQ)
+				drs := dominantResourceShare(cohort, tc.flvResQ)
+				drVal, drName := drs.roundedWeightedShare()
 				gotCache = append(gotCache, fairSharingResult{
 					Name:     string(cohort.Name),
 					NodeType: nodeTypeCohort,
@@ -753,7 +755,8 @@ func TestDominantResourceShare(t *testing.T) {
 			snapshotCohortsMap := snapshot.Cohorts()
 			gotSnapshot := make([]fairSharingResult, 0, len(snapshotClusterQueuesMap)+len(snapshotCohortsMap))
 			for _, cq := range snapshotClusterQueuesMap {
-				drVal, drName := dominantResourceShare(cq, tc.flvResQ)
+				drs := dominantResourceShare(cq, tc.flvResQ)
+				drVal, drName := drs.roundedWeightedShare()
 				gotSnapshot = append(gotSnapshot, fairSharingResult{
 					Name:     string(cq.Name),
 					NodeType: nodeTypeCq,
@@ -762,7 +765,8 @@ func TestDominantResourceShare(t *testing.T) {
 				})
 			}
 			for _, cohort := range snapshotCohortsMap {
-				drVal, drName := dominantResourceShare(cohort, tc.flvResQ)
+				drs := dominantResourceShare(cohort, tc.flvResQ)
+				drVal, drName := drs.roundedWeightedShare()
 				gotSnapshot = append(gotSnapshot, fairSharingResult{
 					Name:     string(cohort.Name),
 					NodeType: nodeTypeCohort,
diff --git a/pkg/config/validation_test.go b/pkg/config/validation_test.go
index 9dc023ba6..8b02fdeec 100644
--- a/pkg/config/validation_test.go
+++ b/pkg/config/validation_test.go
@@ -868,9 +868,8 @@ func TestValidateFeatureGates(t *testing.T) {
 			errorStr:        "Cannot use more than one TAS profiles",
 		},
 		"cannot set TAS profile with TAS disabled": {
-			featureGateMap: map[string]bool{"TASProfileLeastFreeCapacity": true,
-				"TopologyAwareScheduling": true},
-			errorStr: "Cannot use a TAS profile with TAS disabled",
+			featureGateMap: map[string]bool{"TASProfileLeastFreeCapacity": true},
+			errorStr:       "Cannot use a TAS profile with TAS disabled",
 		},
 	}
 	for name, tc := range cases {
diff --git a/pkg/controller/jobs/generic/config_test.go b/pkg/controller/jobs/generic/config_test.go
new file mode 100644
index 000000000..133287017
--- /dev/null
+++ b/pkg/controller/jobs/generic/config_test.go
@@ -0,0 +1,157 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package generic
+
+import (
+	"testing"
+
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	configapi "sigs.k8s.io/kueue/apis/config/v1beta1"
+)
+
+func TestConfigManager_LoadConfigurations(t *testing.T) {
+	cm := NewConfigManager()
+
+	tests := []struct {
+		name    string
+		configs []configapi.MultiKueueExternalFramework
+		wantErr bool
+	}{
+		{
+			name: "valid configurations",
+			configs: []configapi.MultiKueueExternalFramework{
+				{Name: "PipelineRun.v1.tekton.dev"},
+				{Name: "CustomJob.v1alpha1.custom.example.com"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "invalid GVK format",
+			configs: []configapi.MultiKueueExternalFramework{
+				{Name: "invalid-format"},
+			},
+			wantErr: true,
+		},
+		{
+			name: "empty name",
+			configs: []configapi.MultiKueueExternalFramework{
+				{Name: ""},
+			},
+			wantErr: true,
+		},
+		{
+			name: "duplicate GVK",
+			configs: []configapi.MultiKueueExternalFramework{
+				{Name: "PipelineRun.v1.tekton.dev"},
+				{Name: "PipelineRun.v1.tekton.dev"},
+			},
+			wantErr: true,
+		},
+		{
+			name: "mixed valid and invalid",
+			configs: []configapi.MultiKueueExternalFramework{
+				{Name: "PipelineRun.v1.tekton.dev"},
+				{Name: "invalid-format"},
+				{Name: "Workflow.v1alpha1.argoproj.io"},
+			},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := cm.LoadConfigurations(tt.configs)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ConfigManager.LoadConfigurations() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestConfigManager_GetAdapter(t *testing.T) {
+	cm := NewConfigManager()
+
+	// Load a valid configuration
+	configs := []configapi.MultiKueueExternalFramework{
+		{Name: "PipelineRun.v1.tekton.dev"},
+	}
+	if err := cm.LoadConfigurations(configs); err != nil {
+		t.Fatalf("Failed to load configurations: %v", err)
+	}
+
+	// Test getting adapter for configured GVK
+	gvk := schema.GroupVersionKind{
+		Group:   "tekton.dev",
+		Version: "v1",
+		Kind:    "PipelineRun",
+	}
+
+	adapter := cm.GetAdapter(gvk)
+	if adapter == nil {
+		t.Error("Expected adapter to be returned for configured GVK")
+	}
+	if adapter.GVK() != gvk {
+		t.Errorf("Expected GVK %v, got %v", gvk, adapter.GVK())
+	}
+
+	// Test getting adapter for unconfigured GVK
+	unconfiguredGVK := schema.GroupVersionKind{
+		Group:   "example.com",
+		Version: "v1",
+		Kind:    "Example",
+	}
+
+	unconfiguredAdapter := cm.GetAdapter(unconfiguredGVK)
+	if unconfiguredAdapter != nil {
+		t.Error("Expected nil adapter for unconfigured GVK")
+	}
+}
+
+func TestConfigManager_GetAllAdapters(t *testing.T) {
+	cm := NewConfigManager()
+
+	// Load multiple valid configurations
+	configs := []configapi.MultiKueueExternalFramework{
+		{Name: "PipelineRun.v1.tekton.dev"},
+		{Name: "Workflow.v1alpha1.argoproj.io"},
+		{Name: "CustomJob.v1alpha1.custom.example.com"},
+	}
+
+	if err := cm.LoadConfigurations(configs); err != nil {
+		t.Fatalf("Failed to load configurations: %v", err)
+	}
+
+	adapters := cm.GetAllAdapters()
+	if len(adapters) != 3 {
+		t.Errorf("Expected 3 adapters, got %d", len(adapters))
+	}
+
+	// Verify all adapters have valid GVKs
+	expectedGVKs := map[schema.GroupVersionKind]bool{
+		{Group: "tekton.dev", Version: "v1", Kind: "PipelineRun"}:             true,
+		{Group: "argoproj.io", Version: "v1alpha1", Kind: "Workflow"}:         true,
+		{Group: "custom.example.com", Version: "v1alpha1", Kind: "CustomJob"}: true,
+	}
+
+	for _, adapter := range adapters {
+		gvk := adapter.GVK()
+		if !expectedGVKs[gvk] {
+			t.Errorf("Unexpected GVK in adapter: %v", gvk)
+		}
+	}
+}
diff --git a/pkg/controller/jobs/generic/generic_multikueue_adapter_test.go b/pkg/controller/jobs/generic/generic_multikueue_adapter_test.go
new file mode 100644
index 000000000..31935b37b
--- /dev/null
+++ b/pkg/controller/jobs/generic/generic_multikueue_adapter_test.go
@@ -0,0 +1,378 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package generic
+
+import (
+	"context"
+	"strings"
+	"testing"
+
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
+	"sigs.k8s.io/kueue/pkg/controller/constants"
+	"sigs.k8s.io/kueue/pkg/features"
+)
+
+func TestGenericAdapter_IsJobManagedByKueue(t *testing.T) {
+	tests := []struct {
+		name           string
+		object         *unstructured.Unstructured
+		featureEnabled bool
+		want           bool
+		wantReason     string
+		wantErr        bool
+	}{
+		{
+			name: "feature gate disabled",
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"spec": map[string]any{
+						"managedBy": kueue.MultiKueueControllerName,
+					},
+				},
+			},
+			featureEnabled: false,
+			want:           false,
+			wantReason:     "MultiKueueAdaptersForCustomJobs feature gate is disabled",
+			wantErr:        false,
+		},
+		{
+			name: "managed by kueue with default path",
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"spec": map[string]any{
+						"managedBy": kueue.MultiKueueControllerName,
+					},
+				},
+			},
+			featureEnabled: true,
+			want:           true,
+			wantReason:     "",
+			wantErr:        false,
+		},
+		{
+			name: "not managed by kueue",
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"spec": map[string]any{
+						"managedBy": "other-controller",
+					},
+				},
+			},
+			featureEnabled: true,
+			want:           false,
+			wantReason:     "Expecting .spec.managedBy to be \"kueue.x-k8s.io/multikueue\" not \"other-controller\"",
+			wantErr:        false,
+		},
+		{
+			name: "managedBy field not found",
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"spec": map[string]any{
+						"otherField": "value",
+					},
+				},
+			},
+			featureEnabled: true,
+			want:           false,
+			wantReason:     "Expecting .spec.managedBy to be \"kueue.x-k8s.io/multikueue\" not \"\"",
+			wantErr:        false,
+		},
+		{
+			name: "managedBy value is not a string",
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"spec": map[string]any{
+						"managedBy": "not-a-string",
+					},
+				},
+			},
+			featureEnabled: true,
+			want:           false,
+			wantReason:     "Expecting .spec.managedBy to be \"kueue.x-k8s.io/multikueue\" not \"not-a-string\"",
+			wantErr:        false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := features.SetEnable(features.MultiKueueAdaptersForCustomJobs, tt.featureEnabled)
+			if err != nil {
+				t.Fatalf("failed to set feature gate %s", err.Error())
+			}
+
+			adapter := &genericAdapter{
+				gvk: schema.GroupVersionKind{
+					Group:   "test.example.com",
+					Version: "v1",
+					Kind:    "TestJob",
+				},
+			}
+
+			// Set GVK on test object
+			tt.object.SetGroupVersionKind(adapter.gvk)
+			tt.object.SetName("test-job")
+			tt.object.SetNamespace("default")
+
+			client := fake.NewClientBuilder().WithObjects(tt.object).Build()
+			key := types.NamespacedName{Name: "test-job", Namespace: "default"}
+
+			got, gotReason, err := adapter.IsJobManagedByKueue(context.Background(), client, key)
+
+			if (err != nil) != tt.wantErr {
+				t.Errorf("genericAdapter.IsJobManagedByKueue() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("genericAdapter.IsJobManagedByKueue() got = %v, want %v", got, tt.want)
+			}
+			if gotReason != tt.wantReason {
+				t.Errorf("genericAdapter.IsJobManagedByKueue() gotReason = %v, want %v", gotReason, tt.wantReason)
+			}
+		})
+	}
+}
+
+func TestGenericAdapter_RemoveManagedByField(t *testing.T) {
+	adapter := &genericAdapter{
+		gvk: schema.GroupVersionKind{
+			Group:   "test.example.com",
+			Version: "v1",
+			Kind:    "TestJob",
+		},
+	}
+
+	obj := &unstructured.Unstructured{
+		Object: map[string]any{
+			"spec": map[string]any{
+				"managedBy":  "kueue.x-k8s.io/multikueue",
+				"otherField": "value",
+			},
+		},
+	}
+
+	adapter.removeManagedByField(obj)
+
+	// Check that managedBy field is removed
+	if _, exists := obj.Object["spec"].(map[string]any)["managedBy"]; exists {
+		t.Error("managedBy field should be removed")
+	}
+
+	// Check that other fields are preserved
+	if obj.Object["spec"].(map[string]any)["otherField"] != "value" {
+		t.Error("otherField should be preserved")
+	}
+}
+
+func TestGenericAdapter_CopyStatusFromRemote(t *testing.T) {
+	adapter := &genericAdapter{
+		gvk: schema.GroupVersionKind{
+			Group:   "test.example.com",
+			Version: "v1",
+			Kind:    "TestJob",
+		},
+	}
+
+	localObj := &unstructured.Unstructured{
+		Object: map[string]any{
+			"spec": map[string]any{
+				"field": "value",
+			},
+		},
+	}
+
+	remoteObj := &unstructured.Unstructured{
+		Object: map[string]any{
+			"spec": map[string]any{
+				"field": "value",
+			},
+			"status": map[string]any{
+				"phase": "Running",
+				"conditions": []any{
+					map[string]any{
+						"type":   "Ready",
+						"status": "True",
+					},
+				},
+			},
+		},
+	}
+
+	adapter.copyStatusFromRemote(localObj, remoteObj)
+
+	// Check that status is copied
+	localStatus, exists := localObj.Object["status"]
+	if !exists {
+		t.Error("status should be copied to local object")
+	}
+
+	// Check that status content matches
+	if localStatus.(map[string]any)["phase"] != "Running" {
+		t.Error("status phase should match")
+	}
+}
+
+func TestGenericAdapter_GetEmptyList(t *testing.T) {
+	tests := []struct {
+		name string
+		gvk  schema.GroupVersionKind
+		want schema.GroupVersionKind
+	}{
+		{
+			name: "standard GVK",
+			gvk: schema.GroupVersionKind{
+				Group:   "test.example.com",
+				Version: "v1",
+				Kind:    "TestJob",
+			},
+			want: schema.GroupVersionKind{
+				Group:   "test.example.com",
+				Version: "v1",
+				Kind:    "TestJobList",
+			},
+		},
+		{
+			name: "empty GVK",
+			gvk:  schema.GroupVersionKind{},
+			want: schema.GroupVersionKind{
+				Group:   "",
+				Version: "",
+				Kind:    "List",
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			adapter := NewGenericAdapter(tt.gvk)
+			result := adapter.(*genericAdapter).GetEmptyList()
+
+			// Verify the result is an UnstructuredList
+			unstructuredList, ok := result.(*unstructured.UnstructuredList)
+			if !ok {
+				t.Errorf("Expected *unstructured.UnstructuredList, got %T", result)
+				return
+			}
+
+			// Verify the GVK is correct
+			gvk := unstructuredList.GroupVersionKind()
+			if gvk != tt.want {
+				t.Errorf("Expected GVK %v, got %v", tt.want, gvk)
+			}
+
+			// Verify the Kind ends with "List"
+			if !strings.HasSuffix(gvk.Kind, "List") {
+				t.Errorf("Expected Kind to end with 'List', got %s", gvk.Kind)
+			}
+		})
+	}
+}
+
+func TestGenericAdapter_WorkloadKeyFor(t *testing.T) {
+	tests := []struct {
+		name        string
+		gvk         schema.GroupVersionKind
+		object      *unstructured.Unstructured
+		want        types.NamespacedName
+		wantErr     bool
+		expectedErr string
+	}{
+		{
+			name: "valid object with prebuilt workload label",
+			gvk: schema.GroupVersionKind{
+				Group:   "test.example.com",
+				Version: "v1",
+				Kind:    "TestJob",
+			},
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"metadata": map[string]any{
+						"name":      "test-job",
+						"namespace": "test-ns",
+						"labels": map[string]any{
+							constants.PrebuiltWorkloadLabel: "test-workload",
+						},
+					},
+				},
+			},
+			want: types.NamespacedName{
+				Name:      "test-workload",
+				Namespace: "test-ns",
+			},
+			wantErr: false,
+		},
+		{
+			name: "object without prebuilt workload label",
+			gvk: schema.GroupVersionKind{
+				Group:   "test.example.com",
+				Version: "v1",
+				Kind:    "TestJob",
+			},
+			object: &unstructured.Unstructured{
+				Object: map[string]any{
+					"metadata": map[string]any{
+						"name":      "test-job",
+						"namespace": "test-ns",
+						"labels": map[string]any{
+							"other-label": "value",
+						},
+					},
+				},
+			},
+			want:        types.NamespacedName{},
+			wantErr:     true,
+			expectedErr: "no prebuilt workload found for TestJob: test-ns/test-job",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			adapter := NewGenericAdapter(tt.gvk)
+			tt.object.SetGroupVersionKind(tt.gvk)
+			tt.object.SetName("test-job")
+			tt.object.SetNamespace("test-ns")
+
+			result, err := adapter.(*genericAdapter).WorkloadKeyFor(tt.object)
+
+			// Check error cases
+			if tt.wantErr {
+				if err == nil {
+					t.Errorf("Expected error but got none")
+					return
+				}
+				if tt.expectedErr != "" && !strings.Contains(err.Error(), tt.expectedErr) {
+					t.Errorf("Expected error to contain '%s', got '%s'", tt.expectedErr, err.Error())
+				}
+				return
+			}
+
+			// Check success cases
+			if err != nil {
+				t.Errorf("Expected no error but got: %v", err)
+				return
+			}
+
+			if result != tt.want {
+				t.Errorf("Expected result %v, got %v", tt.want, result)
+			}
+		})
+	}
+}
diff --git a/pkg/controller/tas/node_failure_controller_test.go b/pkg/controller/tas/node_failure_controller_test.go
index 201ffd8a1..b820d3764 100644
--- a/pkg/controller/tas/node_failure_controller_test.go
+++ b/pkg/controller/tas/node_failure_controller_test.go
@@ -128,7 +128,7 @@ func TestNodeFailureReconciler(t *testing.T) {
 		wantUnhealthyNodes []kueue.UnhealthyNode
 		wantRequeue        time.Duration
 		wantEvictedCond    *metav1.Condition
-		featureGates       []featuregate.Feature
+		featureGates       map[featuregate.Feature]bool
 	}{
 		"Node Found and Healthy - not marked as unavailable": {
 			initObjs: []client.Object{
@@ -156,6 +156,7 @@ func TestNodeFailureReconciler(t *testing.T) {
 		},
 
 		"Node Found and Unhealthy (NotReady), delay not passed - not marked as unavailable": {
+			featureGates: map[featuregate.Feature]bool{features.TASReplaceNodeOnPodTermination: false},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -169,6 +170,7 @@ func TestNodeFailureReconciler(t *testing.T) {
 			wantRequeue:        NodeFailureDelay,
 		},
 		"Node Found and Unhealthy (NotReady), delay passed - marked as unavailable": {
+			featureGates: map[featuregate.Feature]bool{features.TASReplaceNodeOnPodTermination: false},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -181,7 +183,6 @@ func TestNodeFailureReconciler(t *testing.T) {
 			wantUnhealthyNodes: []kueue.UnhealthyNode{{Name: nodeName}},
 		},
 		"Node NotReady, pod terminating, marked as unavailable": {
-			featureGates: []featuregate.Feature{features.TASReplaceNodeOnPodTermination},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -194,7 +195,6 @@ func TestNodeFailureReconciler(t *testing.T) {
 			wantUnhealthyNodes: []kueue.UnhealthyNode{{Name: nodeName}},
 		},
 		"Node NotReady, pod failed, marked as unavailable": {
-			featureGates: []featuregate.Feature{features.TASReplaceNodeOnPodTermination},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -207,6 +207,7 @@ func TestNodeFailureReconciler(t *testing.T) {
 			wantUnhealthyNodes: []kueue.UnhealthyNode{{Name: nodeName}},
 		},
 		"Node NotReady, pod failed, ReplaceNodeOnPodTermination feature gate off, requeued": {
+			featureGates: map[featuregate.Feature]bool{features.TASReplaceNodeOnPodTermination: false},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -220,7 +221,6 @@ func TestNodeFailureReconciler(t *testing.T) {
 			wantRequeue:        NodeFailureDelay,
 		},
 		"Node NotReady, pod running, not marked as unavailable, requeued": {
-			featureGates: []featuregate.Feature{features.TASReplaceNodeOnPodTermination},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -242,7 +242,7 @@ func TestNodeFailureReconciler(t *testing.T) {
 			wantUnhealthyNodes: []kueue.UnhealthyNode{{Name: nodeName}},
 		},
 		"Two Nodes Unhealthy (NotReady), delay passed - workload evicted": {
-			featureGates: []featuregate.Feature{features.TASFailedNodeReplacement},
+			featureGates: map[featuregate.Feature]bool{features.TASReplaceNodeOnPodTermination: false},
 			initObjs: []client.Object{
 				baseNode.Clone().StatusConditions(corev1.NodeCondition{
 					Type:               corev1.NodeReady,
@@ -272,8 +272,8 @@ func TestNodeFailureReconciler(t *testing.T) {
 	for name, tc := range tests {
 		fakeClock.SetTime(testStartTime)
 		t.Run(name, func(t *testing.T) {
-			for _, fg := range tc.featureGates {
-				features.SetFeatureGateDuringTest(t, fg, true)
+			for fg, enable := range tc.featureGates {
+				features.SetFeatureGateDuringTest(t, fg, enable)
 			}
 			fakeClock.SetTime(testStartTime)
 
diff --git a/pkg/podset/podset_test.go b/pkg/podset/podset_test.go
index dc229d1c1..6a0ea3490 100644
--- a/pkg/podset/podset_test.go
+++ b/pkg/podset/podset_test.go
@@ -188,8 +188,8 @@ func TestFromAssignment(t *testing.T) {
 			wantInfo: PodSetInfo{
 				Name:  "name",
 				Count: 4,
-				Labels: map[string]string{
-					kueue.TASLabel: "true",
+				Annotations: map[string]string{
+					kueue.PodSetUnconstrainedTopologyAnnotation: "true",
 				},
 				NodeSelector: map[string]string{
 					"f1l1": "f1v1",
@@ -238,7 +238,10 @@ func TestFromAssignment(t *testing.T) {
 			features.SetFeatureGateDuringTest(t, features.TopologyAwareScheduling, tc.enableTopologyAwareScheduling)
 			client := utiltesting.NewClientBuilder().WithLists(&kueue.ResourceFlavorList{Items: tc.flavors}).Build()
 
-			gotInfo, gotError := FromAssignment(ctx, client, tc.assignment, tc.defaultCount)
+			podSet := kueue.PodSet{
+				Count: tc.defaultCount,
+			}
+			gotInfo, gotError := FromAssignment(ctx, client, tc.assignment, &podSet)
 
 			if diff := cmp.Diff(tc.wantError, gotError); diff != "" {
 				t.Errorf("Unexpected error (-want/+got):\n%s", diff)
diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go
index ffc3db9c4..9110ab8cb 100644
--- a/pkg/scheduler/scheduler_test.go
+++ b/pkg/scheduler/scheduler_test.go
@@ -1674,6 +1674,185 @@ func TestSchedule(t *testing.T) {
 				"c": {"eng-alpha/c1"},
 			},
 		},
+		// b0 is admitted, using 4 capacity.
+		// b1 and c1 are pending.
+		//
+		// Even though b1 has a higher priority, c1 is admitted
+		// as it is in a queue that is borrowing less than b1.
+		"fair sharing two queues with weight 0 schedules workload which borrows less": {
+			enableFairSharing: true,
+			cohorts: []kueue.Cohort{
+				*utiltesting.MakeCohort("A").
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("on-demand").
+							Resource(corev1.ResourceCPU, "8").Obj(),
+					).Obj(),
+			},
+			additionalClusterQueues: []kueue.ClusterQueue{
+				*utiltesting.MakeClusterQueue("b").
+					FairWeight(resource.MustParse("0")).
+					Cohort("A").
+					Preemption(kueue.ClusterQueuePreemption{
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						WithinClusterQueue:  kueue.PreemptionPolicyNever,
+					}).
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("on-demand").
+							Resource(corev1.ResourceCPU, "0").Obj(),
+					).Obj(),
+				*utiltesting.MakeClusterQueue("c").
+					FairWeight(resource.MustParse("0")).
+					Cohort("A").
+					Preemption(kueue.ClusterQueuePreemption{
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						WithinClusterQueue:  kueue.PreemptionPolicyNever,
+					}).
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("on-demand").
+							Resource(corev1.ResourceCPU, "0").Obj(),
+					).Obj(),
+			},
+			additionalLocalQueues: []kueue.LocalQueue{
+				*utiltesting.MakeLocalQueue("lq-b", "eng-alpha").ClusterQueue("b").Obj(),
+				*utiltesting.MakeLocalQueue("lq-c", "eng-alpha").ClusterQueue("c").Obj(),
+			},
+			workloads: []kueue.Workload{
+				*utiltesting.MakeWorkload("b0", "eng-alpha").
+					Queue("lq-b").
+					PodSets(*utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						Obj()).
+					ReserveQuota(utiltesting.MakeAdmission("b", "one").
+						PodSets(utiltesting.MakePodSetAssignment("one").
+							Assignment(corev1.ResourceCPU, "on-demand", "4").
+							Obj()).
+						Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("b1", "eng-alpha").
+					Queue("lq-b").
+					// high priority for tiebreak
+					Priority(9001).
+					PodSets(*utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("c1", "eng-alpha").
+					Priority(0).
+					Queue("lq-c").
+					PodSets(*utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						Obj()).
+					Obj(),
+			},
+			wantAssignments: map[workload.Reference]kueue.Admission{
+				"eng-alpha/b0": *utiltesting.MakeAdmission("b", "one").
+					PodSets(utiltesting.MakePodSetAssignment("one").
+						Assignment(corev1.ResourceCPU, "on-demand", "4").
+						Obj()).
+					Obj(),
+				"eng-alpha/c1": *utiltesting.MakeAdmission("c", "one").
+					PodSets(utiltesting.MakePodSetAssignment("one").
+						Assignment(corev1.ResourceCPU, "on-demand", "4").
+						Obj()).
+					Obj(),
+			},
+			wantScheduled: []workload.Reference{
+				"eng-alpha/c1",
+			},
+			wantLeft: map[kueue.ClusterQueueReference][]workload.Reference{
+				"b": {"eng-alpha/b1"},
+			},
+			wantInadmissibleLeft: nil,
+		},
+		// b0 is admitted, using 4 capacity.
+		// b1 and c1 are pending.
+		//
+		// Even though b1 has a higher priority, c1 is admitted
+		// as it is in a queue that is borrowing less than b1.
+		"fair sharing two queues with high weight schedules workload which borrows less": {
+			enableFairSharing: true,
+			cohorts: []kueue.Cohort{
+				*utiltesting.MakeCohort("A").
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("on-demand").
+							Resource(corev1.ResourceCPU, "8").Obj(),
+					).Obj(),
+			},
+			additionalClusterQueues: []kueue.ClusterQueue{
+				*utiltesting.MakeClusterQueue("b").
+					FairWeight(resource.MustParse("123456789")).
+					Cohort("A").
+					Preemption(kueue.ClusterQueuePreemption{
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						WithinClusterQueue:  kueue.PreemptionPolicyNever,
+					}).
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("on-demand").
+							Resource(corev1.ResourceCPU, "0").Obj(),
+					).Obj(),
+				*utiltesting.MakeClusterQueue("c").
+					FairWeight(resource.MustParse("123456789")).
+					Cohort("A").
+					Preemption(kueue.ClusterQueuePreemption{
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						WithinClusterQueue:  kueue.PreemptionPolicyNever,
+					}).
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("on-demand").
+							Resource(corev1.ResourceCPU, "0").Obj(),
+					).Obj(),
+			},
+			additionalLocalQueues: []kueue.LocalQueue{
+				*utiltesting.MakeLocalQueue("lq-b", "eng-alpha").ClusterQueue("b").Obj(),
+				*utiltesting.MakeLocalQueue("lq-c", "eng-alpha").ClusterQueue("c").Obj(),
+			},
+			workloads: []kueue.Workload{
+				*utiltesting.MakeWorkload("b0", "eng-alpha").
+					Queue("lq-b").
+					PodSets(*utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						Obj()).
+					ReserveQuota(utiltesting.MakeAdmission("b", "one").
+						PodSets(utiltesting.MakePodSetAssignment("one").
+							Assignment(corev1.ResourceCPU, "on-demand", "4").
+							Obj()).
+						Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("b1", "eng-alpha").
+					Queue("lq-b").
+					Priority(9001).
+					PodSets(*utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("c1", "eng-alpha").
+					Priority(0).
+					Queue("lq-c").
+					PodSets(*utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						Obj()).
+					Obj(),
+			},
+			wantAssignments: map[workload.Reference]kueue.Admission{
+				"eng-alpha/b0": *utiltesting.MakeAdmission("b", "one").
+					PodSets(utiltesting.MakePodSetAssignment("one").
+						Assignment(corev1.ResourceCPU, "on-demand", "4").
+						Obj()).
+					Obj(),
+				"eng-alpha/c1": *utiltesting.MakeAdmission("c", "one").
+					PodSets(utiltesting.MakePodSetAssignment("one").
+						Assignment(corev1.ResourceCPU, "on-demand", "4").
+						Obj()).
+					Obj(),
+			},
+			wantScheduled: []workload.Reference{
+				"eng-alpha/c1",
+			},
+			wantLeft: map[kueue.ClusterQueueReference][]workload.Reference{
+				"b": {"eng-alpha/b1"},
+			},
+			wantInadmissibleLeft: nil,
+		},
 		// Cohort A has Clusterqueue a, and capacity is
 		// provided by Cohort.
 		//
@@ -5173,7 +5352,7 @@ func TestScheduleForTAS(t *testing.T) {
 		// eventCmpOpts are the comparison options for the events
 		eventCmpOpts cmp.Options
 
-		featureGates []featuregate.Feature
+		featureGates map[featuregate.Feature]bool
 	}{
 		"workload with a PodSet of size zero": {
 			nodes: []corev1.Node{
@@ -5418,6 +5597,7 @@ func TestScheduleForTAS(t *testing.T) {
 			},
 		},
 		"workload with unhealthyNode annotation; second pass; preferred; no fit": {
+			featureGates:    map[featuregate.Feature]bool{features.TASFailedNodeReplacementFailFast: false},
 			nodes:           defaultNodes,
 			admissionChecks: []kueue.AdmissionCheck{defaultProvCheck},
 			topologies:      []kueue.Topology{defaultThreeLevelTopology},
@@ -5502,7 +5682,6 @@ func TestScheduleForTAS(t *testing.T) {
 					Message("Workload was evicted as there was no replacement for a failed node: x0").
 					Obj(),
 			},
-			featureGates: []featuregate.Feature{features.TASFailedNodeReplacementFailFast},
 		},
 		"workload with unhealthyNode annotation; second pass; required rack; fit": {
 			nodes:           defaultNodes,
@@ -5583,6 +5762,7 @@ func TestScheduleForTAS(t *testing.T) {
 			},
 		},
 		"workload with unhealthyNode annotation; second pass; required rack; no fit": {
+			featureGates:    map[featuregate.Feature]bool{features.TASFailedNodeReplacementFailFast: false},
 			nodes:           defaultNodes,
 			admissionChecks: []kueue.AdmissionCheck{defaultProvCheck},
 			topologies:      []kueue.Topology{defaultThreeLevelTopology},
@@ -7022,12 +7202,57 @@ func TestScheduleForTAS(t *testing.T) {
 				utiltesting.MakeEventRecord("default", "foo", "Admitted", corev1.EventTypeNormal).Obj(),
 			},
 		},
+		"workload with unhealthyNode annotation; second pass; preferred; no fit when using slices; FailFast": {
+			nodes:           defaultNodes,
+			admissionChecks: []kueue.AdmissionCheck{defaultProvCheck},
+			topologies:      []kueue.Topology{defaultThreeLevelTopology},
+			resourceFlavors: []kueue.ResourceFlavor{defaultTASThreeLevelFlavor},
+			clusterQueues:   []kueue.ClusterQueue{defaultClusterQueue},
+			workloads: []kueue.Workload{
+				*utiltesting.MakeWorkload("foo", "default").
+					UnhealthyNodes("x0").
+					Queue("tas-main").
+					PodSets(*utiltesting.MakePodSet("one", 2).
+						PreferredTopologyRequest(tasBlockLabel).
+						SliceSizeTopologyRequest(2).
+						SliceRequiredTopologyRequest(tasRackLabel).
+						Request(corev1.ResourceCPU, "1").
+						Obj()).
+					ReserveQuota(
+						utiltesting.MakeAdmission("tas-main").
+							PodSets(utiltesting.MakePodSetAssignment("one").Count(2).
+								Assignment(corev1.ResourceCPU, "tas-default", "2000m").
+								TopologyAssignment(utiltesting.MakeTopologyAssignment(utiltas.Levels(&defaultSingleLevelTopology)).
+									Domain(utiltesting.MakeTopologyDomainAssignment([]string{"x0"}, 1).Obj()).
+									Domain(utiltesting.MakeTopologyDomainAssignment([]string{"x1"}, 1).Obj()).
+									Obj()).
+								Obj()).
+							Obj(),
+					).
+					Admitted(true).
+					Obj(),
+			},
+			wantNewAssignments: map[workload.Reference]kueue.Admission{
+				"default/foo": *utiltesting.MakeAdmission("tas-main").
+					PodSets(utiltesting.MakePodSetAssignment("one").Count(2).
+						Assignment(corev1.ResourceCPU, "tas-default", "2000m").
+						TopologyAssignment(utiltesting.MakeTopologyAssignment(utiltas.Levels(&defaultSingleLevelTopology)).
+							Domain(utiltesting.MakeTopologyDomainAssignment([]string{"x0"}, 1).Obj()).
+							Domain(utiltesting.MakeTopologyDomainAssignment([]string{"x1"}, 1).Obj()).
+							Obj()).
+						Obj()).
+					Obj(),
+			},
+			wantEvents: []utiltesting.EventRecord{
+				utiltesting.MakeEventRecord("default", "foo", "EvictedDueToNodeFailures", corev1.EventTypeNormal).
+					Message("Workload was evicted as there was no replacement for a failed node: x0").Obj(),
+			},
+		},
 	}
 	for name, tc := range cases {
 		t.Run(name, func(t *testing.T) {
-			features.SetFeatureGateDuringTest(t, features.TopologyAwareScheduling, true)
-			for _, fg := range tc.featureGates {
-				features.SetFeatureGateDuringTest(t, fg, true)
+			for fg, enable := range tc.featureGates {
+				features.SetFeatureGateDuringTest(t, fg, enable)
 			}
 			ctx, log := utiltesting.ContextWithLog(t)
 
@@ -7580,7 +7805,6 @@ func TestScheduleForTASPreemption(t *testing.T) {
 	}
 	for name, tc := range cases {
 		t.Run(name, func(t *testing.T) {
-			features.SetFeatureGateDuringTest(t, features.TopologyAwareScheduling, true)
 			ctx, log := utiltesting.ContextWithLog(t)
 
 			clientBuilder := utiltesting.NewClientBuilder().
diff --git a/pkg/util/cert/cert_test.go b/pkg/util/cert/cert_test.go
new file mode 100644
index 000000000..12e447862
--- /dev/null
+++ b/pkg/util/cert/cert_test.go
@@ -0,0 +1,109 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cert
+
+import (
+	"testing"
+)
+
+func TestDeriveWebhookBaseName(t *testing.T) {
+	tests := []struct {
+		name               string
+		webhookServiceName string
+		expectedBaseName   string
+	}{
+		{
+			name:               "default kueue service name",
+			webhookServiceName: "kueue-webhook-service",
+			expectedBaseName:   "kueue",
+		},
+		{
+			name:               "custom release name",
+			webhookServiceName: "test-kueue-webhook-service",
+			expectedBaseName:   "test-kueue",
+		},
+		{
+			name:               "production release name",
+			webhookServiceName: "prod-kueue-webhook-service",
+			expectedBaseName:   "prod-kueue",
+		},
+		{
+			name:               "complex release name",
+			webhookServiceName: "my-company-staging-kueue-webhook-service",
+			expectedBaseName:   "my-company-staging-kueue",
+		},
+		{
+			name:               "service name without expected suffix",
+			webhookServiceName: "kueue-service",
+			expectedBaseName:   "kueue-service",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := deriveWebhookBaseName(tt.webhookServiceName)
+			if result != tt.expectedBaseName {
+				t.Errorf("deriveWebhookBaseName(%q) = %q, want %q",
+					tt.webhookServiceName, result, tt.expectedBaseName)
+			}
+		})
+	}
+}
+
+func TestBuildWebhookConfigurationName(t *testing.T) {
+	tests := []struct {
+		name         string
+		baseName     string
+		webhookType  string
+		expectedName string
+	}{
+		{
+			name:         "mutating webhook for kueue",
+			baseName:     "kueue",
+			webhookType:  "mutating",
+			expectedName: "kueue-mutating-webhook-configuration",
+		},
+		{
+			name:         "validating webhook for kueue",
+			baseName:     "kueue",
+			webhookType:  "validating",
+			expectedName: "kueue-validating-webhook-configuration",
+		},
+		{
+			name:         "mutating webhook for custom release",
+			baseName:     "custome-kueue-name",
+			webhookType:  "mutating",
+			expectedName: "custome-kueue-name-mutating-webhook-configuration",
+		},
+		{
+			name:         "validating webhook for custom release",
+			baseName:     "custome-kueue-name",
+			webhookType:  "validating",
+			expectedName: "custome-kueue-name-validating-webhook-configuration",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := buildWebhookConfigurationName(tt.baseName, tt.webhookType)
+			if result != tt.expectedName {
+				t.Errorf("buildWebhookConfigurationName(%q, %q) = %q, want %q",
+					tt.baseName, tt.webhookType, result, tt.expectedName)
+			}
+		})
+	}
+}
diff --git a/pkg/webhooks/clusterqueue_webhook_test.go b/pkg/webhooks/clusterqueue_webhook_test.go
index 4a84114c7..f0a1fef94 100644
--- a/pkg/webhooks/clusterqueue_webhook_test.go
+++ b/pkg/webhooks/clusterqueue_webhook_test.go
@@ -17,11 +17,13 @@ limitations under the License.
 package webhooks
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/validation/field"
 	"k8s.io/utils/ptr"
@@ -369,3 +371,142 @@ func TestValidateClusterQueueUpdate(t *testing.T) {
 		})
 	}
 }
+
+func makeFlavors(n int) []kueue.FlavorQuotas {
+	flavs := make([]kueue.FlavorQuotas, 0, n)
+	for i := range n {
+		flavs = append(flavs, kueue.FlavorQuotas{
+			Name: kueue.ResourceFlavorReference(fmt.Sprintf("f%03d", i)),
+			Resources: []kueue.ResourceQuota{{
+				Name:         corev1.ResourceCPU,
+				NominalQuota: resource.MustParse("1"),
+			}},
+		})
+	}
+	return flavs
+}
+
+func TestValidateTotalFlavors(t *testing.T) {
+	testcases := []struct {
+		name       string
+		numFlavors int
+		wantErr    bool
+	}{
+		{"within limit (10 flavors)", 10, false},
+		{"At limit (256 flavors)", 256, false},
+		{"over limit (257 flavors)", 257, true},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			cq := testingutil.MakeClusterQueue("cluster-queue").
+				ResourceGroup(makeFlavors(tc.numFlavors)...).Obj()
+
+			gotErr := ValidateClusterQueue(cq)
+
+			if diff := cmp.Diff(tc.wantErr, len(gotErr) > 0); diff != "" {
+				t.Errorf("Unexpected error (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func makeCoveredResources(n int) []kueue.ResourceGroup {
+	resources := make([]corev1.ResourceName, n)
+	quotas := make([]kueue.ResourceQuota, n)
+	for i := range n {
+		name := corev1.ResourceName(fmt.Sprintf("res%03d", i))
+		resources[i] = name
+		quotas[i] = kueue.ResourceQuota{
+			Name:         name,
+			NominalQuota: resource.MustParse("1"),
+		}
+	}
+
+	return []kueue.ResourceGroup{{
+		CoveredResources: resources,
+		Flavors: []kueue.FlavorQuotas{{
+			Name:      kueue.ResourceFlavorReference("default"),
+			Resources: quotas,
+		}},
+	}}
+}
+
+func TestValidateTotalCoveredResources(t *testing.T) {
+	testcases := []struct {
+		name          string
+		numCoveredRes int
+		wantErr       bool
+	}{
+		{"within limit (10 covered resources)", 10, false},
+		{"At limit (256 covered resources)", 256, false},
+		{"over limit (257 covered resources)", 257, true},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			cq := testingutil.MakeClusterQueue("cluster-queue").
+				Obj()
+			cq.Spec.ResourceGroups = makeCoveredResources(tc.numCoveredRes)
+
+			gotErr := ValidateClusterQueue(cq)
+
+			if diff := cmp.Diff(tc.wantErr, len(gotErr) > 0); diff != "" {
+				t.Errorf("Unexpected error (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func makeFlavorResourceCombinations(numFlavors, numResources int) []kueue.ResourceGroup {
+	flavors := make([]kueue.FlavorQuotas, 0, numFlavors)
+	resources := make([]corev1.ResourceName, numResources)
+	for j := range numResources {
+		resources[j] = corev1.ResourceName(fmt.Sprintf("res%03d", j))
+	}
+
+	for i := range numFlavors {
+		quotas := make([]kueue.ResourceQuota, numResources)
+		for j := range numResources {
+			quotas[j] = kueue.ResourceQuota{
+				Name:         resources[j],
+				NominalQuota: resource.MustParse("1"),
+			}
+		}
+		flavors = append(flavors, kueue.FlavorQuotas{
+			Name:      kueue.ResourceFlavorReference(fmt.Sprintf("f%03d", i)),
+			Resources: quotas,
+		})
+	}
+
+	return []kueue.ResourceGroup{{
+		CoveredResources: resources,
+		Flavors:          flavors,
+	}}
+}
+
+func TestValidateFlavorResourceCombinations(t *testing.T) {
+	testcases := []struct {
+		name         string
+		numFlavors   int
+		numResources int
+		wantErr      bool
+	}{
+		{"within limit (1 flavor × 10 resources = 10)", 1, 10, false},
+		{"at limit (8 flavors × 64 resources = 512)", 8, 64, false},
+		{"over limit (9 flavors × 64 resources = 576)", 9, 64, true},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			cq := testingutil.MakeClusterQueue("cluster-queue").Obj()
+			cq.Spec.ResourceGroups = makeFlavorResourceCombinations(tc.numFlavors, tc.numResources)
+
+			gotErr := ValidateClusterQueue(cq)
+
+			if diff := cmp.Diff(tc.wantErr, len(gotErr) > 0); diff != "" {
+				t.Errorf("Unexpected error (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/pkg/workload/workload_test.go b/pkg/workload/workload_test.go
index 3dda31966..e3bbeea73 100644
--- a/pkg/workload/workload_test.go
+++ b/pkg/workload/workload_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package workload
 
 import (
+	"fmt"
 	"testing"
 	"time"
 
@@ -1412,3 +1413,99 @@ func TestWithPreprocessedDRAResources(t *testing.T) {
 		})
 	}
 }
+
+func TestSetQuotaReservation(t *testing.T) {
+	// test clock and time "constants" uses in conditions.
+	testClock := testingclock.NewFakeClock(time.Now())
+	now := testClock.Now()
+	fiveMinutesAgo := now.Add(-5 * time.Minute)
+
+	// admission "constants" values used in all test cases.
+	admission := utiltesting.MakeAdmission("test-queue").Obj()
+	quotaReservedReason := "QuotaReserved"
+	quotaReservedMessage := fmt.Sprintf("Quota reserved in ClusterQueue %s", admission.ClusterQueue)
+
+	// newWorkload wrapper to reduce boilerplate in test cases.
+	newWorkload := func() *utiltesting.WorkloadWrapper {
+		return utiltesting.MakeWorkload("test", "default").Generation(1)
+	}
+
+	// newCondition helper.
+	newCondition := func(condition string, status metav1.ConditionStatus, reason, message string, ltt time.Time) metav1.Condition {
+		return metav1.Condition{
+			Type:               condition,
+			Status:             status,
+			Reason:             reason,
+			Message:            message,
+			ObservedGeneration: 1,
+			LastTransitionTime: metav1.NewTime(ltt),
+		}
+	}
+
+	// test cases.
+	type args struct {
+		workload  *kueue.Workload
+		admission *kueue.Admission
+	}
+	tests := map[string]struct {
+		args args
+		want *kueue.Workload
+	}{
+		"WorkloadWithoutConditions": {
+			args: args{
+				workload:  newWorkload().Obj(),
+				admission: admission,
+			},
+			want: newWorkload().
+				Admission(admission).
+				Condition(newCondition(kueue.WorkloadQuotaReserved, metav1.ConditionTrue, quotaReservedReason, quotaReservedMessage, now)).
+				Obj(),
+		},
+		"WorkloadWithActiveConditions": {
+			args: args{
+				workload: newWorkload().
+					Conditions(
+						newCondition(kueue.WorkloadEvicted, metav1.ConditionTrue, "TestEvictedReason", "test evicted message", fiveMinutesAgo),
+						newCondition(kueue.WorkloadPreempted, metav1.ConditionTrue, "TestPreemptedReason", "test preempted message", fiveMinutesAgo),
+						newCondition(kueue.WorkloadQuotaReserved, metav1.ConditionFalse, "TestReason", "test message", fiveMinutesAgo),
+					).Obj(),
+				admission: admission,
+			},
+			want: newWorkload().
+				Admission(admission).
+				Conditions(
+					newCondition(kueue.WorkloadEvicted, metav1.ConditionFalse, quotaReservedReason, "Previously: test evicted message", now),
+					newCondition(kueue.WorkloadPreempted, metav1.ConditionFalse, quotaReservedReason, "Previously: test preempted message", now),
+					newCondition(kueue.WorkloadQuotaReserved, metav1.ConditionTrue, quotaReservedReason, quotaReservedMessage, now),
+				).
+				Obj(),
+		},
+		"WorkloadWithInactiveConditions": {
+			args: args{
+				workload: newWorkload().
+					Conditions(
+						newCondition(kueue.WorkloadEvicted, metav1.ConditionFalse, quotaReservedReason, "Previously: test evicted message", fiveMinutesAgo),
+						newCondition(kueue.WorkloadPreempted, metav1.ConditionFalse, quotaReservedReason, "Previously: test preempted message", fiveMinutesAgo),
+						newCondition(kueue.WorkloadQuotaReserved, metav1.ConditionFalse, quotaReservedReason, quotaReservedMessage, fiveMinutesAgo),
+					).Obj(),
+				admission: admission,
+			},
+			want: newWorkload().
+				Admission(admission).
+				Conditions(
+					newCondition(kueue.WorkloadEvicted, metav1.ConditionFalse, quotaReservedReason, "Previously: test evicted message", fiveMinutesAgo),
+					newCondition(kueue.WorkloadPreempted, metav1.ConditionFalse, quotaReservedReason, "Previously: test preempted message", fiveMinutesAgo),
+					newCondition(kueue.WorkloadQuotaReserved, metav1.ConditionTrue, quotaReservedReason, quotaReservedMessage, now),
+				).
+				Obj(),
+		},
+	}
+	for name, tt := range tests {
+		t.Run(name, func(t *testing.T) {
+			SetQuotaReservation(tt.args.workload, tt.args.admission, testClock)
+			if diff := cmp.Diff(tt.want, tt.args.workload); diff != "" {
+				t.Errorf("SetQuotaReservation() (-want +got):\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/test/e2e/config/default/controller_manager_config.yaml b/test/e2e/config/default/controller_manager_config.yaml
index 4f8ca0ebb..572727d0c 100644
--- a/test/e2e/config/default/controller_manager_config.yaml
+++ b/test/e2e/config/default/controller_manager_config.yaml
@@ -37,6 +37,4 @@ integrations:
   - "leaderworkerset.x-k8s.io/leaderworkerset"
 featureGates:
   MultiKueueBatchJobWithManagedBy: true
-  TopologyAwareScheduling: true
   LocalQueueMetrics: true
-  TASFailedNodeReplacement: true
diff --git a/test/e2e/config/default/values.yaml b/test/e2e/config/default/values.yaml
index c28ad1ca4..21c32a12a 100644
--- a/test/e2e/config/default/values.yaml
+++ b/test/e2e/config/default/values.yaml
@@ -67,6 +67,4 @@ managerConfig:
         - "leaderworkerset.x-k8s.io/leaderworkerset"
     featureGates:
       MultiKueueBatchJobWithManagedBy: true
-      TopologyAwareScheduling: true
       LocalQueueMetrics: true
-      TASFailedNodeReplacement: true
diff --git a/test/e2e/singlecluster/fair_sharing_test.go b/test/e2e/singlecluster/fair_sharing_test.go
index 71001f400..5f215c7cc 100644
--- a/test/e2e/singlecluster/fair_sharing_test.go
+++ b/test/e2e/singlecluster/fair_sharing_test.go
@@ -96,7 +96,7 @@ var _ = ginkgo.Describe("Fair Sharing", ginkgo.Ordered, ginkgo.ContinueOnFailure
 			for i := range 4 {
 				job := jobtesting.MakeJob(fmt.Sprintf("j%d", i+1), ns.Name).
 					Queue(v1beta1.LocalQueueName(lq1.Name)).
-					Image(util.GetAgnHostImage(), util.BehaviorExitFast).
+					Image(util.GetAgnHostImage(), util.BehaviorWaitForDeletion).
 					Parallelism(3).
 					Completions(3).
 					RequestAndLimit(corev1.ResourceCPU, "1").
@@ -113,7 +113,7 @@ var _ = ginkgo.Describe("Fair Sharing", ginkgo.Ordered, ginkgo.ContinueOnFailure
 
 				g.Expect(cq1.Status.AdmittedWorkloads).Should(gomega.Equal(int32(4)))
 				g.Expect(cq1.Status.FairSharing).ShouldNot(gomega.BeNil())
-				g.Expect(cq1.Status.FairSharing.WeightedShare).Should(gomega.Equal(int64(111)))
+				g.Expect(cq1.Status.FairSharing.WeightedShare).Should(gomega.Equal(int64(112)))
 
 				g.Expect(cq2.Status.AdmittedWorkloads).Should(gomega.Equal(int32(0)))
 				g.Expect(cq2.Status.FairSharing).ShouldNot(gomega.BeNil())
diff --git a/test/integration/multikueue/external_job_test.go b/test/integration/multikueue/external_job_test.go
new file mode 100644
index 000000000..bc9bbcd9f
--- /dev/null
+++ b/test/integration/multikueue/external_job_test.go
@@ -0,0 +1,602 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package multikueue
+
+import (
+	"context"
+	"time"
+
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/onsi/ginkgo/v2"
+	"github.com/onsi/gomega"
+	rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
+	corev1 "k8s.io/api/core/v1"
+	apimeta "k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/utils/ptr"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+
+	config "sigs.k8s.io/kueue/apis/config/v1beta1"
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
+	qcache "sigs.k8s.io/kueue/pkg/cache/queue"
+	schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
+	"sigs.k8s.io/kueue/pkg/constants"
+	"sigs.k8s.io/kueue/pkg/controller/admissionchecks/multikueue"
+	"sigs.k8s.io/kueue/pkg/controller/core"
+	"sigs.k8s.io/kueue/pkg/controller/core/indexer"
+	"sigs.k8s.io/kueue/pkg/controller/jobframework"
+	"sigs.k8s.io/kueue/pkg/controller/jobs/generic"
+	workloadrayjob "sigs.k8s.io/kueue/pkg/controller/jobs/rayjob"
+	dispatcher "sigs.k8s.io/kueue/pkg/controller/workloaddispatcher"
+	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/util/admissioncheck"
+	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
+	testingrayjob "sigs.k8s.io/kueue/pkg/util/testingjobs/rayjob"
+	"sigs.k8s.io/kueue/pkg/webhooks"
+	"sigs.k8s.io/kueue/test/util"
+)
+
+var _ = ginkgo.Describe("MultiKueue", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+	ginkgo.When("the external RayJob adapter is enabled", func() {
+		var (
+			managerNs *corev1.Namespace
+			worker1Ns *corev1.Namespace
+			worker2Ns *corev1.Namespace
+
+			managerMultiKueueSecret1 *corev1.Secret
+			managerMultiKueueSecret2 *corev1.Secret
+			workerCluster1           *kueue.MultiKueueCluster
+			workerCluster2           *kueue.MultiKueueCluster
+			managerMultiKueueConfig  *kueue.MultiKueueConfig
+			multiKueueAC             *kueue.AdmissionCheck
+			managerCq                *kueue.ClusterQueue
+			managerLq                *kueue.LocalQueue
+
+			worker1Cq *kueue.ClusterQueue
+			worker1Lq *kueue.LocalQueue
+
+			worker2Cq *kueue.ClusterQueue
+			worker2Lq *kueue.LocalQueue
+		)
+
+		ginkgo.BeforeAll(func() {
+			managerTestCluster.fwk.StartManager(managerTestCluster.ctx, managerTestCluster.cfg, func(ctx context.Context, mgr manager.Manager) {
+				// Set up core controllers and RayJob webhook (but not MultiKueue integration)
+				err := indexer.Setup(ctx, mgr.GetFieldIndexer())
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+				cCache := schdcache.New(mgr.GetClient())
+				queues := qcache.NewManager(mgr.GetClient(), cCache)
+
+				configuration := &config.Configuration{}
+				mgr.GetScheme().Default(configuration)
+
+				failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
+				gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
+
+				failedWebhook, err := webhooks.Setup(mgr, ptr.Deref(configuration.MultiKueue.DispatcherName, config.MultiKueueDispatcherModeAllAtOnce))
+				gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
+
+				// Set up RayJob webhook (but not MultiKueue integration)
+				err = workloadrayjob.SetupIndexes(ctx, mgr.GetFieldIndexer())
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+				rayjobReconciler := workloadrayjob.NewReconciler(
+					mgr.GetClient(),
+					mgr.GetEventRecorderFor(constants.JobControllerName))
+				err = rayjobReconciler.SetupWithManager(mgr)
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+				err = workloadrayjob.SetupRayJobWebhook(mgr, jobframework.WithCache(cCache), jobframework.WithQueues(queues))
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+				// Set up multikueue with external frameworks only
+				err = multikueue.SetupIndexer(ctx, mgr.GetFieldIndexer(), managersConfigNamespace.Name)
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+				cfg := &config.Configuration{}
+				mgr.GetScheme().Default(cfg)
+				cfg.MultiKueue.ExternalFrameworks = []config.MultiKueueExternalFramework{
+					{
+						Name: "RayJob.v1.ray.io",
+					},
+				}
+
+				// Get external adapters for MultiKueue synchronization
+				gcm := generic.NewConfigManager()
+				gomega.Expect(gcm.LoadConfigurations(cfg.MultiKueue.ExternalFrameworks)).To(gomega.Succeed())
+				adapters := make(map[string]jobframework.MultiKueueAdapter)
+				for _, adapter := range gcm.GetAllAdapters() {
+					gvk := adapter.GVK()
+					adapters[gvk.String()] = adapter
+				}
+
+				err = multikueue.SetupControllers(mgr, managersConfigNamespace.Name,
+					multikueue.WithGCInterval(2*time.Second),
+					multikueue.WithWorkerLostTimeout(testingWorkerLostTimeout),
+					multikueue.WithEventsBatchPeriod(100*time.Millisecond),
+					multikueue.WithAdapters(adapters),
+					multikueue.WithDispatcherName(string(config.MultiKueueDispatcherModeAllAtOnce)),
+				)
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+				_, err = dispatcher.SetupControllers(mgr, configuration, string(config.MultiKueueDispatcherModeAllAtOnce))
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+			})
+		})
+
+		ginkgo.AfterAll(func() {
+			managerTestCluster.fwk.StopManager(managerTestCluster.ctx)
+		})
+
+		ginkgo.BeforeEach(func() {
+			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.MultiKueueAdaptersForCustomJobs, true)
+			managerNs = util.CreateNamespaceFromPrefixWithLog(managerTestCluster.ctx, managerTestCluster.client, "multikueue-")
+			worker1Ns = util.CreateNamespaceWithLog(worker1TestCluster.ctx, worker1TestCluster.client, managerNs.Name)
+			worker2Ns = util.CreateNamespaceWithLog(worker2TestCluster.ctx, worker2TestCluster.client, managerNs.Name)
+
+			w1Kubeconfig, err := worker1TestCluster.kubeConfigBytes()
+			gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+			w2Kubeconfig, err := worker2TestCluster.kubeConfigBytes()
+			gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+			managerMultiKueueSecret1 = &corev1.Secret{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "multikueue1",
+					Namespace: managersConfigNamespace.Name,
+				},
+				Data: map[string][]byte{
+					kueue.MultiKueueConfigSecretKey: w1Kubeconfig,
+				},
+			}
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, managerMultiKueueSecret1)
+
+			managerMultiKueueSecret2 = &corev1.Secret{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "multikueue2",
+					Namespace: managersConfigNamespace.Name,
+				},
+				Data: map[string][]byte{
+					kueue.MultiKueueConfigSecretKey: w2Kubeconfig,
+				},
+			}
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, managerMultiKueueSecret2)
+
+			workerCluster1 = utiltesting.MakeMultiKueueCluster("worker1").KubeConfig(kueue.SecretLocationType, managerMultiKueueSecret1.Name).Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, workerCluster1)
+
+			workerCluster2 = utiltesting.MakeMultiKueueCluster("worker2").KubeConfig(kueue.SecretLocationType, managerMultiKueueSecret2.Name).Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, workerCluster2)
+
+			managerMultiKueueConfig = utiltesting.MakeMultiKueueConfig("multikueueconfig").Clusters(workerCluster1.Name, workerCluster2.Name).Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, managerMultiKueueConfig)
+
+			multiKueueAC = utiltesting.MakeAdmissionCheck("ac1").
+				ControllerName(kueue.MultiKueueControllerName).
+				Parameters(kueue.GroupVersion.Group, "MultiKueueConfig", managerMultiKueueConfig.Name).
+				Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, multiKueueAC)
+
+			ginkgo.By("wait for check active", func() {
+				updatedAc := kueue.AdmissionCheck{}
+				acKey := client.ObjectKeyFromObject(multiKueueAC)
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, acKey, &updatedAc)).To(gomega.Succeed())
+					g.Expect(updatedAc.Status.Conditions).To(utiltesting.HaveConditionStatusTrue(kueue.AdmissionCheckActive))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			managerCq = utiltesting.MakeClusterQueue("q1").
+				AdmissionChecks(kueue.AdmissionCheckReference(multiKueueAC.Name)).
+				Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, managerCq)
+			util.ExpectClusterQueuesToBeActive(managerTestCluster.ctx, managerTestCluster.client, managerCq)
+
+			managerLq = utiltesting.MakeLocalQueue(managerCq.Name, managerNs.Name).ClusterQueue(managerCq.Name).Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, managerLq)
+			util.ExpectLocalQueuesToBeActive(managerTestCluster.ctx, managerTestCluster.client, managerLq)
+
+			worker1Cq = utiltesting.MakeClusterQueue("q1").Obj()
+			util.MustCreate(worker1TestCluster.ctx, worker1TestCluster.client, worker1Cq)
+			util.ExpectClusterQueuesToBeActive(worker1TestCluster.ctx, worker1TestCluster.client, worker1Cq)
+			worker1Lq = utiltesting.MakeLocalQueue(worker1Cq.Name, worker1Ns.Name).ClusterQueue(worker1Cq.Name).Obj()
+			util.MustCreate(worker1TestCluster.ctx, worker1TestCluster.client, worker1Lq)
+			util.ExpectLocalQueuesToBeActive(worker1TestCluster.ctx, worker1TestCluster.client, worker1Lq)
+
+			worker2Cq = utiltesting.MakeClusterQueue("q1").Obj()
+			util.MustCreate(worker2TestCluster.ctx, worker2TestCluster.client, worker2Cq)
+			util.ExpectClusterQueuesToBeActive(worker2TestCluster.ctx, worker2TestCluster.client, worker2Cq)
+			worker2Lq = utiltesting.MakeLocalQueue(worker2Cq.Name, worker2Ns.Name).ClusterQueue(worker2Cq.Name).Obj()
+			util.MustCreate(worker2TestCluster.ctx, worker2TestCluster.client, worker2Lq)
+			util.ExpectLocalQueuesToBeActive(worker2TestCluster.ctx, worker2TestCluster.client, worker2Lq)
+		})
+
+		ginkgo.AfterEach(func() {
+			gomega.Expect(util.DeleteNamespace(managerTestCluster.ctx, managerTestCluster.client, managerNs)).To(gomega.Succeed())
+			gomega.Expect(util.DeleteNamespace(worker1TestCluster.ctx, worker1TestCluster.client, worker1Ns)).To(gomega.Succeed())
+			gomega.Expect(util.DeleteNamespace(worker2TestCluster.ctx, worker2TestCluster.client, worker2Ns)).To(gomega.Succeed())
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, managerCq, true)
+			util.ExpectObjectToBeDeleted(worker1TestCluster.ctx, worker1TestCluster.client, worker1Cq, true)
+			util.ExpectObjectToBeDeleted(worker2TestCluster.ctx, worker2TestCluster.client, worker2Cq, true)
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, multiKueueAC, true)
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, managerMultiKueueConfig, true)
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, workerCluster1, true)
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, workerCluster2, true)
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, managerMultiKueueSecret1, true)
+			util.ExpectObjectToBeDeleted(managerTestCluster.ctx, managerTestCluster.client, managerMultiKueueSecret2, true)
+		})
+
+		ginkgo.It("Should run a RayJob on worker if admitted", func() {
+			admission := utiltesting.MakeAdmission(managerCq.Name).PodSets(
+				kueue.PodSetAssignment{
+					Name: "head",
+				}, kueue.PodSetAssignment{
+					Name: "workers-group-0",
+				},
+			)
+			rayjob := testingrayjob.MakeJob("rayjob1", managerNs.Name).
+				WithSubmissionMode(rayv1.InteractiveMode).
+				Queue(managerLq.Name).
+				Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, rayjob)
+			wlLookupKey := types.NamespacedName{Name: workloadrayjob.GetWorkloadNameForRayJob(rayjob.Name, rayjob.UID), Namespace: managerNs.Name}
+			util.SetQuotaReservation(managerTestCluster.ctx, managerTestCluster.client, wlLookupKey, admission.Obj())
+
+			admitWorkloadAndCheckWorkerCopies(multiKueueAC.Name, wlLookupKey, admission)
+
+			ginkgo.By("changing the status of the RayJob in the worker, updates the manager's RayJob status", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdRayJob := rayv1.RayJob{}
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, client.ObjectKeyFromObject(rayjob), &createdRayJob)).To(gomega.Succeed())
+					createdRayJob.Status.JobDeploymentStatus = rayv1.JobDeploymentStatusRunning
+					g.Expect(worker2TestCluster.client.Status().Update(worker2TestCluster.ctx, &createdRayJob)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdRayJob := rayv1.RayJob{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(rayjob), &createdRayJob)).To(gomega.Succeed())
+					g.Expect(createdRayJob.Status.JobDeploymentStatus).To(gomega.Equal(rayv1.JobDeploymentStatusRunning))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("finishing the worker RayJob, the manager's wl is marked as finished and the worker2 wl removed", func() {
+				finishJobReason := ""
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdRayJob := rayv1.RayJob{}
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, client.ObjectKeyFromObject(rayjob), &createdRayJob)).To(gomega.Succeed())
+					createdRayJob.Status.JobStatus = rayv1.JobStatusSucceeded
+					createdRayJob.Status.JobDeploymentStatus = rayv1.JobDeploymentStatusComplete
+					g.Expect(worker2TestCluster.client.Status().Update(worker2TestCluster.ctx, &createdRayJob)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				waitForWorkloadToFinishAndRemoteWorkloadToBeDeleted(wlLookupKey, finishJobReason)
+			})
+		})
+
+		ginkgo.It("Should run a RayJob on worker if admitted (ManagedBy)", func() {
+			admission := utiltesting.MakeAdmission(managerCq.Name).PodSets(
+				kueue.PodSetAssignment{
+					Name: "head",
+				}, kueue.PodSetAssignment{
+					Name: "workers-group-0",
+				},
+			)
+			rayjob := testingrayjob.MakeJob("rayjob1", managerNs.Name).
+				WithSubmissionMode(rayv1.InteractiveMode).
+				Queue(managerLq.Name).
+				ManagedBy(kueue.MultiKueueControllerName).
+				Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, rayjob)
+			wlLookupKey := types.NamespacedName{Name: workloadrayjob.GetWorkloadNameForRayJob(rayjob.Name, rayjob.UID), Namespace: managerNs.Name}
+			util.SetQuotaReservation(managerTestCluster.ctx, managerTestCluster.client, wlLookupKey, admission.Obj())
+
+			admitWorkloadAndCheckWorkerCopies(multiKueueAC.Name, wlLookupKey, admission)
+
+			ginkgo.By("changing the status of the RayJob in the worker, updates the manager's RayJob status", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdRayJob := rayv1.RayJob{}
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, client.ObjectKeyFromObject(rayjob), &createdRayJob)).To(gomega.Succeed())
+					createdRayJob.Status.JobDeploymentStatus = rayv1.JobDeploymentStatusRunning
+					g.Expect(worker2TestCluster.client.Status().Update(worker2TestCluster.ctx, &createdRayJob)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdRayJob := rayv1.RayJob{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(rayjob), &createdRayJob)).To(gomega.Succeed())
+					g.Expect(createdRayJob.Status.JobDeploymentStatus).To(gomega.Equal(rayv1.JobDeploymentStatusRunning))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("finishing the worker RayJob, the manager's wl is marked as finished and the worker2 wl removed", func() {
+				finishJobReason := ""
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdRayJob := rayv1.RayJob{}
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, client.ObjectKeyFromObject(rayjob), &createdRayJob)).To(gomega.Succeed())
+					createdRayJob.Status.JobStatus = rayv1.JobStatusSucceeded
+					createdRayJob.Status.JobDeploymentStatus = rayv1.JobDeploymentStatusComplete
+					g.Expect(worker2TestCluster.client.Status().Update(worker2TestCluster.ctx, &createdRayJob)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				waitForWorkloadToFinishAndRemoteWorkloadToBeDeleted(wlLookupKey, finishJobReason)
+			})
+		})
+
+		ginkgo.It("Should remove the worker's workload and RayJob after reconnect when the managers RayJob and workload are deleted", func() {
+			rayjob := testingrayjob.MakeJob("rayjob1", managerNs.Name).
+				WithSubmissionMode(rayv1.InteractiveMode).
+				Queue(managerLq.Name).
+				Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, rayjob)
+			rayjobLookupKey := client.ObjectKeyFromObject(rayjob)
+			createdRayJob := &rayv1.RayJob{}
+
+			createdWorkload := &kueue.Workload{}
+			wlLookupKey := types.NamespacedName{Name: workloadrayjob.GetWorkloadNameForRayJob(rayjob.Name, rayjob.UID), Namespace: managerNs.Name}
+
+			ginkgo.By("setting workload reservation in the management cluster", func() {
+				admission := utiltesting.MakeAdmission(managerCq.Name).PodSets(
+					kueue.PodSetAssignment{
+						Name: "head",
+					}, kueue.PodSetAssignment{
+						Name: "workers-group-0",
+					},
+				)
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+					util.SetQuotaReservation(managerTestCluster.ctx, managerTestCluster.client, wlLookupKey, admission.Obj())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("checking the workload creation in the worker clusters", func() {
+				managerWl := &kueue.Workload{}
+				gomega.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, wlLookupKey, managerWl)).To(gomega.Succeed())
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+					g.Expect(createdWorkload.Spec).To(gomega.BeComparableTo(managerWl.Spec))
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+					g.Expect(createdWorkload.Spec).To(gomega.BeComparableTo(managerWl.Spec))
+				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("breaking the connection to worker2", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					createdCluster.Spec.KubeConfig.Location = "bad-secret"
+					g.Expect(managerTestCluster.client.Update(managerTestCluster.ctx, createdCluster)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					activeCondition := apimeta.FindStatusCondition(createdCluster.Status.Conditions, kueue.MultiKueueClusterActive)
+					g.Expect(activeCondition).To(gomega.BeComparableTo(&metav1.Condition{
+						Type:   kueue.MultiKueueClusterActive,
+						Status: metav1.ConditionFalse,
+						Reason: "BadConfig",
+					}, util.IgnoreConditionMessage, util.IgnoreConditionTimestampsAndObservedGeneration))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("setting workload reservation in worker1, the RayJob is created in worker1", func() {
+				admission := utiltesting.MakeAdmission(managerCq.Name).PodSets(
+					kueue.PodSetAssignment{
+						Name: "head",
+					}, kueue.PodSetAssignment{
+						Name: "workers-group-0",
+					},
+				)
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+					util.SetQuotaReservation(worker1TestCluster.ctx, worker1TestCluster.client, wlLookupKey, admission.Obj())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, rayjobLookupKey, createdRayJob)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("breaking the connection to worker1", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster1), createdCluster)).To(gomega.Succeed())
+					createdCluster.Spec.KubeConfig.Location = "bad-secret"
+					g.Expect(managerTestCluster.client.Update(managerTestCluster.ctx, createdCluster)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster1), createdCluster)).To(gomega.Succeed())
+					activeCondition := apimeta.FindStatusCondition(createdCluster.Status.Conditions, kueue.MultiKueueClusterActive)
+					g.Expect(activeCondition).To(gomega.BeComparableTo(&metav1.Condition{
+						Type:   kueue.MultiKueueClusterActive,
+						Status: metav1.ConditionFalse,
+						Reason: "BadConfig",
+					}, util.IgnoreConditionMessage, util.IgnoreConditionTimestampsAndObservedGeneration))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("removing the managers RayJob and workload", func() {
+				gomega.Expect(managerTestCluster.client.Delete(managerTestCluster.ctx, rayjob)).Should(gomega.Succeed())
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+					g.Expect(managerTestCluster.client.Delete(managerTestCluster.ctx, createdWorkload)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, wlLookupKey, createdWorkload)).To(utiltesting.BeNotFoundError(), "workload not deleted")
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("the worker objects are still present", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, rayjobLookupKey, createdRayJob)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("restoring the connection to worker2", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					createdCluster.Spec.KubeConfig.Location = managerMultiKueueSecret2.Name
+					g.Expect(managerTestCluster.client.Update(managerTestCluster.ctx, createdCluster)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					activeCondition := apimeta.FindStatusCondition(createdCluster.Status.Conditions, kueue.MultiKueueClusterActive)
+					g.Expect(activeCondition).To(gomega.BeComparableTo(&metav1.Condition{
+						Type:   kueue.MultiKueueClusterActive,
+						Status: metav1.ConditionTrue,
+						Reason: "Active",
+					}, util.IgnoreConditionMessage, util.IgnoreConditionTimestampsAndObservedGeneration))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("the worker2 wl is removed by the garbage collector", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, wlLookupKey, createdWorkload)).To(utiltesting.BeNotFoundError())
+				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("restoring the connection to worker1", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster1), createdCluster)).To(gomega.Succeed())
+					createdCluster.Spec.KubeConfig.Location = managerMultiKueueSecret1.Name
+					g.Expect(managerTestCluster.client.Update(managerTestCluster.ctx, createdCluster)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster1), createdCluster)).To(gomega.Succeed())
+					activeCondition := apimeta.FindStatusCondition(createdCluster.Status.Conditions, kueue.MultiKueueClusterActive)
+					g.Expect(activeCondition).To(gomega.BeComparableTo(&metav1.Condition{
+						Type:   kueue.MultiKueueClusterActive,
+						Status: metav1.ConditionTrue,
+						Reason: "Active",
+					}, util.IgnoreConditionMessage, util.IgnoreConditionTimestampsAndObservedGeneration))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("the wl and RayJob are removed on the worker1", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, rayjobLookupKey, createdRayJob)).To(utiltesting.BeNotFoundError())
+					g.Expect(worker1TestCluster.client.Get(worker1TestCluster.ctx, wlLookupKey, createdWorkload)).To(utiltesting.BeNotFoundError())
+				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
+			})
+		})
+
+		ginkgo.It("Should requeue the workload with a delay when the connection to the admitting worker is lost", func() {
+			rayjob := testingrayjob.MakeJob("rayjob1", managerNs.Name).
+				WithSubmissionMode(rayv1.InteractiveMode).
+				Queue(managerLq.Name).
+				Obj()
+			util.MustCreate(managerTestCluster.ctx, managerTestCluster.client, rayjob)
+
+			wlLookupKey := types.NamespacedName{Name: workloadrayjob.GetWorkloadNameForRayJob(rayjob.Name, rayjob.UID), Namespace: managerNs.Name}
+
+			admission := utiltesting.MakeAdmission(managerCq.Name).PodSets(
+				kueue.PodSetAssignment{
+					Name: "head",
+				}, kueue.PodSetAssignment{
+					Name: "workers-group-0",
+				},
+			)
+
+			admitWorkloadAndCheckWorkerCopies(multiKueueAC.Name, wlLookupKey, admission)
+
+			var disconnectedTime time.Time
+			ginkgo.By("breaking the connection to worker2", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					createdCluster.Spec.KubeConfig.Location = "bad-secret"
+					g.Expect(managerTestCluster.client.Update(managerTestCluster.ctx, createdCluster)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					activeCondition := apimeta.FindStatusCondition(createdCluster.Status.Conditions, kueue.MultiKueueClusterActive)
+					g.Expect(activeCondition).To(gomega.BeComparableTo(&metav1.Condition{
+						Type:   kueue.MultiKueueClusterActive,
+						Status: metav1.ConditionFalse,
+						Reason: "BadConfig",
+					}, util.IgnoreConditionMessage, util.IgnoreConditionTimestampsAndObservedGeneration))
+					disconnectedTime = activeCondition.LastTransitionTime.Time
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("waiting for the local workload admission check state to be set to pending and quotaReservation removed", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdWorkload := &kueue.Workload{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, wlLookupKey, createdWorkload)).To(gomega.Succeed())
+					acs := admissioncheck.FindAdmissionCheck(createdWorkload.Status.AdmissionChecks, kueue.AdmissionCheckReference(multiKueueAC.Name))
+					g.Expect(acs).To(gomega.BeComparableTo(&kueue.AdmissionCheckState{
+						Name:  kueue.AdmissionCheckReference(multiKueueAC.Name),
+						State: kueue.CheckStatePending,
+					}, cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "Message")))
+
+					// The transition interval should be close to testingKeepReadyTimeout (taking into account the resolution of the LastTransitionTime field)
+					g.Expect(acs.LastTransitionTime.Time).To(gomega.BeComparableTo(disconnectedTime.Add(testingWorkerLostTimeout), cmpopts.EquateApproxTime(2*time.Second)))
+
+					g.Expect(createdWorkload.Status.Conditions).ToNot(utiltesting.HaveConditionStatusTrue(kueue.WorkloadQuotaReserved))
+				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("restoring the connection to worker2", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					createdCluster.Spec.KubeConfig.Location = managerMultiKueueSecret2.Name
+					g.Expect(managerTestCluster.client.Update(managerTestCluster.ctx, createdCluster)).To(gomega.Succeed())
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdCluster := &kueue.MultiKueueCluster{}
+					g.Expect(managerTestCluster.client.Get(managerTestCluster.ctx, client.ObjectKeyFromObject(workerCluster2), createdCluster)).To(gomega.Succeed())
+					activeCondition := apimeta.FindStatusCondition(createdCluster.Status.Conditions, kueue.MultiKueueClusterActive)
+					g.Expect(activeCondition).To(gomega.BeComparableTo(&metav1.Condition{
+						Type:   kueue.MultiKueueClusterActive,
+						Status: metav1.ConditionTrue,
+						Reason: "Active",
+					}, util.IgnoreConditionMessage, util.IgnoreConditionTimestampsAndObservedGeneration))
+					disconnectedTime = activeCondition.LastTransitionTime.Time
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("the worker2 wl is removed since the local one no longer has a reservation", func() {
+				gomega.Eventually(func(g gomega.Gomega) {
+					createdWorkload := &kueue.Workload{}
+					g.Expect(worker2TestCluster.client.Get(worker2TestCluster.ctx, wlLookupKey, createdWorkload)).To(utiltesting.BeNotFoundError())
+				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
+			})
+		})
+	})
+})
diff --git a/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go b/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go
index ba5610834..088f1dee9 100644
--- a/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go
@@ -40,7 +40,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadaw "sigs.k8s.io/kueue/pkg/controller/jobs/appwrapper"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingaw "sigs.k8s.io/kueue/pkg/util/testingjobs/appwrapper"
 	utiltestingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -835,7 +834,7 @@ var _ = ginkgo.Describe("AppWrapper controller interacting with scheduler", gink
 	})
 })
 
-var _ = ginkgo.Describe("AppWrapper controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("AppWrapper controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 		tasBlockLabel  = "cloud.com/topology-block"
@@ -859,8 +858,6 @@ var _ = ginkgo.Describe("AppWrapper controller when TopologyAwareScheduling enab
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-aw-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go b/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go
index 48eabe9d5..08f85fb39 100644
--- a/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go
@@ -35,7 +35,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadjaxjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/jaxjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjaxjob "sigs.k8s.io/kueue/pkg/util/testingjobs/jaxjob"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -561,7 +560,7 @@ var _ = ginkgo.Describe("Job controller interacting with scheduler", ginkgo.Orde
 	})
 })
 
-var _ = ginkgo.Describe("JAXJob controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("JAXJob controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -584,8 +583,6 @@ var _ = ginkgo.Describe("JAXJob controller when TopologyAwareScheduling enabled"
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-jaxjob-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/job/job_controller_test.go b/test/integration/singlecluster/controller/jobs/job/job_controller_test.go
index e3096710d..7d65d4559 100644
--- a/test/integration/singlecluster/controller/jobs/job/job_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/job/job_controller_test.go
@@ -2585,7 +2585,7 @@ var _ = ginkgo.Describe("Job controller interacting with Workload controller whe
 	})
 })
 
-var _ = ginkgo.Describe("Job controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("Job controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 		tasBlockLabel  = "cloud.com/topology-block"
@@ -2609,8 +2609,6 @@ var _ = ginkgo.Describe("Job controller when TopologyAwareScheduling enabled", g
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-job-")
 
 		nodes = []corev1.Node{
@@ -3335,6 +3333,8 @@ var _ = ginkgo.Describe("Job with elastic jobs via workload-slices support", gin
 		util.ExpectAdmissionWaitTimeMetric(clusterQueue, highPriorityClass.Name, 0)
 		util.ExpectQuotaReservedWorkloadsTotalMetric(clusterQueue, lowPriorityClass.Name, 1)
 		util.ExpectQuotaReservedWorkloadsTotalMetric(clusterQueue, highPriorityClass.Name, 0)
+		util.ExpectQuotaReservedWaitTimeMetric(clusterQueue, lowPriorityClass.Name, 1)
+		util.ExpectQuotaReservedWaitTimeMetric(clusterQueue, highPriorityClass.Name, 0)
 
 		ginkgo.By("scale-up low-priority job beyond the queue's nominal capacity")
 		gomega.Eventually(func(g gomega.Gomega) {
@@ -3369,6 +3369,8 @@ var _ = ginkgo.Describe("Job with elastic jobs via workload-slices support", gin
 		util.ExpectAdmissionWaitTimeMetric(clusterQueue, highPriorityClass.Name, 1)
 		util.ExpectQuotaReservedWorkloadsTotalMetric(clusterQueue, lowPriorityClass.Name, 1)
 		util.ExpectQuotaReservedWorkloadsTotalMetric(clusterQueue, highPriorityClass.Name, 1)
+		util.ExpectQuotaReservedWaitTimeMetric(clusterQueue, lowPriorityClass.Name, 1)
+		util.ExpectQuotaReservedWaitTimeMetric(clusterQueue, highPriorityClass.Name, 1)
 
 		ginkgo.By("the low priority old workload slice is finished")
 		util.ExpectWorkloadToFinish(ctx, k8sClient, client.ObjectKeyFromObject(lowPriorityWorkloadSlice))
diff --git a/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go b/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go
index 9b8f14db0..c9d64392b 100644
--- a/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go
@@ -39,7 +39,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadjobset "sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjobset "sigs.k8s.io/kueue/pkg/util/testingjobs/jobset"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -1137,7 +1136,7 @@ var _ = ginkgo.Describe("JobSet controller interacting with scheduler", ginkgo.O
 	})
 })
 
-var _ = ginkgo.Describe("JobSet controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("JobSet controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -1160,8 +1159,6 @@ var _ = ginkgo.Describe("JobSet controller when TopologyAwareScheduling enabled"
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-jobset-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go b/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go
index 96497b3bf..8bed0cdaa 100644
--- a/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go
@@ -38,7 +38,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadmpijob "sigs.k8s.io/kueue/pkg/controller/jobs/mpijob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
 	testingmpijob "sigs.k8s.io/kueue/pkg/util/testingjobs/mpijob"
@@ -893,7 +892,7 @@ var _ = ginkgo.Describe("Job controller interacting with scheduler", ginkgo.Orde
 	})
 })
 
-var _ = ginkgo.Describe("MPIJob controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("MPIJob controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -916,8 +915,6 @@ var _ = ginkgo.Describe("MPIJob controller when TopologyAwareScheduling enabled"
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-mpijob-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go b/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go
index 8db7d518a..35b7ee67a 100644
--- a/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go
@@ -32,7 +32,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadpaddlejob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/paddlejob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
 	testingpaddlejob "sigs.k8s.io/kueue/pkg/util/testingjobs/paddlejob"
@@ -283,7 +282,7 @@ var _ = ginkgo.Describe("Job controller interacting with scheduler", framework.R
 	})
 })
 
-var _ = ginkgo.Describe("PaddleJob controller when TopologyAwareScheduling enabled", framework.RedundantSpec, ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("PaddleJob controller with TopologyAwareScheduling", framework.RedundantSpec, ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -306,8 +305,6 @@ var _ = ginkgo.Describe("PaddleJob controller when TopologyAwareScheduling enabl
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-paddlejob-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go b/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go
index 0093a82e2..e075b6e69 100644
--- a/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go
@@ -39,7 +39,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	podcontroller "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	podconstants "sigs.k8s.io/kueue/pkg/controller/jobs/pod/constants"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -2128,7 +2127,7 @@ var _ = ginkgo.Describe("Pod controller interacting with Workload controller whe
 	})
 })
 
-var _ = ginkgo.Describe("Pod controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("Pod controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 		tasBlockLabel  = "cloud.com/topology-block"
@@ -2165,8 +2164,6 @@ var _ = ginkgo.Describe("Pod controller when TopologyAwareScheduling enabled", g
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-pod-")
 
 		nodes = []corev1.Node{
@@ -2315,10 +2312,9 @@ var _ = ginkgo.Describe("Pod controller when TopologyAwareScheduling enabled", g
 	})
 })
 
-var _ = ginkgo.Describe("Pod controller when TASReplaceNodeOnPodTermination is enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("Pod controller with TASReplaceNodeOnPodTermination", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
-		tasBlockLabel  = "cloud.com/topology-block"
 	)
 
 	var (
@@ -2341,7 +2337,6 @@ var _ = ginkgo.Describe("Pod controller when TASReplaceNodeOnPodTermination is e
 	mjnsSelector, err := metav1.LabelSelectorAsSelector(nsSelector)
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 	ginkgo.BeforeAll(func() {
-		_ = features.SetEnable(features.TASFailedNodeReplacement, true)
 		fwk.StartManager(ctx, cfg, managerSetup(true, true, nil,
 			jobframework.WithManagedJobsNamespaceSelector(mjnsSelector),
 			jobframework.WithEnabledFrameworks([]string{"pod"}),
@@ -2350,13 +2345,9 @@ var _ = ginkgo.Describe("Pod controller when TASReplaceNodeOnPodTermination is e
 
 	ginkgo.AfterAll(func() {
 		fwk.StopManager(ctx)
-		_ = features.SetEnable(features.TASFailedNodeReplacement, false)
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TASReplaceNodeOnPodTermination, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-pod-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go b/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go
index 7188bffb8..782eadd6f 100644
--- a/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go
@@ -35,7 +35,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadpytorchjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/pytorchjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
 	testingpytorchjob "sigs.k8s.io/kueue/pkg/util/testingjobs/pytorchjob"
@@ -582,7 +581,7 @@ var _ = ginkgo.Describe("Job controller interacting with scheduler", ginkgo.Orde
 	})
 })
 
-var _ = ginkgo.Describe("PyTorchJob controller when TopologyAwareScheduling enabled", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("PyTorchJob controller with TopologyAwareScheduling", ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -605,8 +604,6 @@ var _ = ginkgo.Describe("PyTorchJob controller when TopologyAwareScheduling enab
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-pytorchjob-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go b/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go
index 9e29e8dd7..602a983ab 100644
--- a/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go
@@ -32,7 +32,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadtfjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/tfjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
 	testingtfjob "sigs.k8s.io/kueue/pkg/util/testingjobs/tfjob"
@@ -297,7 +296,7 @@ var _ = ginkgo.Describe("Job controller interacting with scheduler", framework.R
 	})
 })
 
-var _ = ginkgo.Describe("TFJob controller when TopologyAwareScheduling enabled", framework.RedundantSpec, ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("TFJob controller with TopologyAwareScheduling", framework.RedundantSpec, ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -320,8 +319,6 @@ var _ = ginkgo.Describe("TFJob controller when TopologyAwareScheduling enabled",
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-tfjob-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go b/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go
index e07d4feb5..09e8abf49 100644
--- a/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go
@@ -32,7 +32,6 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadxgboostjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/xgboostjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
 	testingxgboostjob "sigs.k8s.io/kueue/pkg/util/testingjobs/xgboostjob"
@@ -280,7 +279,7 @@ var _ = ginkgo.Describe("Job controller interacting with scheduler", framework.R
 	})
 })
 
-var _ = ginkgo.Describe("XGBoostJob controller when TopologyAwareScheduling enabled", framework.RedundantSpec, ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
+var _ = ginkgo.Describe("XGBoostJob controller with TopologyAwareScheduling", framework.RedundantSpec, ginkgo.Ordered, ginkgo.ContinueOnFailure, func() {
 	const (
 		nodeGroupLabel = "node-group"
 	)
@@ -303,8 +302,6 @@ var _ = ginkgo.Describe("XGBoostJob controller when TopologyAwareScheduling enab
 	})
 
 	ginkgo.BeforeEach(func() {
-		features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-xgboostjob-")
 
 		nodes = []corev1.Node{
diff --git a/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go b/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
index b635843dc..86facd242 100644
--- a/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
+++ b/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
@@ -258,7 +258,7 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 			}
 			util.ExpectReservingActiveWorkloadsMetric(cqA, 9)
 			util.ExpectPendingWorkloadsMetric(cqA, 0, 1)
-			util.ExpectClusterQueueWeightedShareMetric(cqA, 666)
+			util.ExpectClusterQueueWeightedShareMetric(cqA, 667)
 			util.ExpectClusterQueueWeightedShareMetric(cqB, 0)
 			util.ExpectClusterQueueWeightedShareMetric(cqC, 0)
 
@@ -271,23 +271,23 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 			ginkgo.By("Finishing eviction of 4 running workloads in cqA: shared quota is fair-shared")
 			util.FinishEvictionOfWorkloadsInCQ(ctx, k8sClient, cqA, 4)
 			util.ExpectReservingActiveWorkloadsMetric(cqB, 4)
-			util.ExpectClusterQueueWeightedShareMetric(cqA, 222)
-			util.ExpectClusterQueueWeightedShareMetric(cqB, 111)
+			util.ExpectClusterQueueWeightedShareMetric(cqA, 223)
+			util.ExpectClusterQueueWeightedShareMetric(cqB, 112)
 			util.ExpectClusterQueueWeightedShareMetric(cqC, 0)
 
 			ginkgo.By("cq-c reclaims one unit, preemption happens in cq-a")
 			cWorkload := testing.MakeWorkload("c0", ns.Name).Queue("c").Request(corev1.ResourceCPU, "1").Obj()
 			util.MustCreate(ctx, k8sClient, cWorkload)
 			util.ExpectPendingWorkloadsMetric(cqC, 1, 0)
-			util.ExpectClusterQueueWeightedShareMetric(cqA, 222)
-			util.ExpectClusterQueueWeightedShareMetric(cqB, 111)
+			util.ExpectClusterQueueWeightedShareMetric(cqA, 223)
+			util.ExpectClusterQueueWeightedShareMetric(cqB, 112)
 			util.ExpectClusterQueueWeightedShareMetric(cqC, 0)
 
 			ginkgo.By("Finishing eviction of 1 running workloads in the CQ with highest usage: cqA")
 			util.FinishEvictionOfWorkloadsInCQ(ctx, k8sClient, cqA, 1)
 			util.ExpectReservingActiveWorkloadsMetric(cqC, 1)
-			util.ExpectClusterQueueWeightedShareMetric(cqA, 111)
-			util.ExpectClusterQueueWeightedShareMetric(cqB, 111)
+			util.ExpectClusterQueueWeightedShareMetric(cqA, 112)
+			util.ExpectClusterQueueWeightedShareMetric(cqB, 112)
 			util.ExpectClusterQueueWeightedShareMetric(cqC, 0)
 
 			ginkgo.By("Checking that weight share status changed")
@@ -296,11 +296,79 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 			gomega.Eventually(func(g gomega.Gomega) {
 				g.Expect(k8sClient.Get(ctx, cqAKey, createdCqA)).Should(gomega.Succeed())
 				g.Expect(createdCqA.Status.FairSharing).ShouldNot(gomega.BeNil())
-				g.Expect(createdCqA.Status.FairSharing).Should(gomega.BeComparableTo(&kueue.FairSharingStatus{WeightedShare: 111}))
+				g.Expect(createdCqA.Status.FairSharing).Should(gomega.BeComparableTo(&kueue.FairSharingStatus{WeightedShare: 112}))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
 	})
 
+	ginkgo.When("Preemption is enabled and CQs have 0 weight", func() {
+		var (
+			cqA *kueue.ClusterQueue
+			cqB *kueue.ClusterQueue
+		)
+		ginkgo.BeforeEach(func() {
+			createCohort(testing.MakeCohort("top-cohort").
+				ResourceGroup(
+					*testing.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "8").Obj(),
+				).Obj())
+
+			cqA = createQueue(testing.MakeClusterQueue("best-effort-cq-a").
+				Cohort("top-cohort").
+				FairWeight(resource.MustParse("0")).
+				Preemption(kueue.ClusterQueuePreemption{
+					ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+				}).
+				ResourceGroup(
+					*testing.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "0").Obj(),
+				).Obj())
+
+			cqB = createQueue(testing.MakeClusterQueue("best-effort-cq-b").
+				Cohort("top-cohort").
+				FairWeight(resource.MustParse("0")).
+				Preemption(kueue.ClusterQueuePreemption{
+					ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+				}).
+				ResourceGroup(
+					*testing.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "0").Obj(),
+				).Obj())
+		})
+
+		ginkgo.It("should not cause an infinite preemption cycle", func() {
+			ginkgo.By("Creating two workloads in cqA")
+			wlA1 := createWorkloadWithPriority("best-effort-cq-a", "4", 9001)
+			wlA2 := createWorkloadWithPriority("best-effort-cq-a", "4", 100)
+			util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wlA1, wlA2)
+			util.ExpectReservingActiveWorkloadsMetric(cqA, 2)
+			util.ExpectReservingActiveWorkloadsMetric(cqB, 0)
+
+			ginkgo.By("Creating a workload in cqB that should preempt one from cqA")
+			wlB1 := createWorkloadWithPriority("best-effort-cq-b", "4", 100)
+
+			ginkgo.By("Check Preemptions")
+			util.ExpectPreemptedWorkloadsTotalMetric(cqA.Name, "InCohortFairSharing", 0)
+			util.ExpectPreemptedWorkloadsTotalMetric(cqB.Name, "InCohortFairSharing", 1)
+
+			ginkgo.By("Waiting for preemption and eviction")
+			// wlA2 will be preempted as it is lower priority than wlA1
+			util.ExpectWorkloadsToBePreempted(ctx, k8sClient, wlA2)
+			util.FinishEvictionForWorkloads(ctx, k8sClient, wlA2)
+			util.ExpectEvictedWorkloadsTotalMetric(cqA.Name, kueue.WorkloadEvictedByPreemption, "", "", 1)
+
+			ginkgo.By("Check Admission")
+			util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wlB1)
+
+			ginkgo.By("Verify both workloads running")
+			util.ExpectReservingActiveWorkloadsMetric(cqA, 1)
+			util.ExpectReservingActiveWorkloadsMetric(cqB, 1)
+
+			ginkgo.By("Checking that there are no more preemptions")
+			util.ExpectPreemptedWorkloadsTotalMetric(cqA.Name, "InCohortFairSharing", 0)
+			util.ExpectPreemptedWorkloadsTotalMetric(cqB.Name, "InCohortFairSharing", 1)
+			util.ExpectEvictedWorkloadsTotalMetric(cqA.Name, kueue.WorkloadEvictedByPreemption, "", "", 1)
+			util.ExpectEvictedWorkloadsTotalMetric(cqB.Name, kueue.WorkloadEvictedByPreemption, "", "", 0)
+		})
+	})
+
 	ginkgo.When("using hierarchical cohorts", func() {
 		ginkgo.It("admits workloads respecting fair share", func() {
 			//         root
@@ -342,10 +410,10 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 
 			util.ExpectReservingActiveWorkloadsMetric(cqSecondLeft, 5)
 			util.ExpectReservingActiveWorkloadsMetric(cqSecondRight, 5)
-			expectCohortWeightedShare(cohortFirstLeft.Name, 428)
+			expectCohortWeightedShare(cohortFirstLeft.Name, 429)
 			expectCohortWeightedShare(cohortFirstRight.Name, 0)
-			expectCohortWeightedShare(cohortSecondLeft.Name, 214)
-			expectCohortWeightedShare(cohortSecondRight.Name, 214)
+			expectCohortWeightedShare(cohortSecondLeft.Name, 215)
+			expectCohortWeightedShare(cohortSecondRight.Name, 215)
 			expectCohortWeightedShare(cohortBank.Name, 0)
 		})
 		ginkgo.It("preempts workloads to enforce fair share", func() {
@@ -399,12 +467,12 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 
 			ginkgo.By("share is fair with respect to each parent")
 			// parent root
-			expectCohortWeightedShare("best-effort", 666)
-			expectCohortWeightedShare("research", 666)
+			expectCohortWeightedShare("best-effort", 667)
+			expectCohortWeightedShare("research", 667)
 			// parent research
-			expectCohortWeightedShare("chemistry", 166)
-			expectCohortWeightedShare("physics", 166)
-			expectCohortWeightedShare("llm", 166)
+			expectCohortWeightedShare("chemistry", 167)
+			expectCohortWeightedShare("physics", 167)
+			expectCohortWeightedShare("llm", 167)
 
 			ginkgo.By("number workloads admitted proportional to share at each level")
 			util.ExpectReservingActiveWorkloadsMetric(bestEffortQueue, 4)
diff --git a/test/integration/singlecluster/scheduler/preemption_test.go b/test/integration/singlecluster/scheduler/preemption_test.go
index a25607c5d..9a2905d4f 100644
--- a/test/integration/singlecluster/scheduler/preemption_test.go
+++ b/test/integration/singlecluster/scheduler/preemption_test.go
@@ -314,20 +314,22 @@ var _ = ginkgo.Describe("Preemption", func() {
 				gomega.Eventually(func(g gomega.Gomega) {
 					g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(alphaLowWl), alphaLowWl)).To(gomega.Succeed())
 					g.Expect(apimeta.FindStatusCondition(alphaLowWl.Status.Conditions, kueue.WorkloadPreempted)).To(gomega.BeComparableTo(&metav1.Condition{
-						Type:    kueue.WorkloadPreempted,
-						Status:  metav1.ConditionFalse,
-						Reason:  "QuotaReserved",
-						Message: fmt.Sprintf("Previously: Preempted to accommodate a workload (UID: %s, JobUID: UNKNOWN) due to %s", alphaMidWl.UID, preemption.HumanReadablePreemptionReasons[kueue.InClusterQueueReason]),
+						Type:               kueue.WorkloadPreempted,
+						Status:             metav1.ConditionFalse,
+						ObservedGeneration: alphaLowWl.Generation,
+						Reason:             "QuotaReserved",
+						Message:            fmt.Sprintf("Previously: Preempted to accommodate a workload (UID: %s, JobUID: UNKNOWN) due to %s", alphaMidWl.UID, preemption.HumanReadablePreemptionReasons[kueue.InClusterQueueReason]),
 					}, conditionCmpOpts))
 				}, util.Timeout, util.Interval).Should(gomega.Succeed())
 
 				gomega.Eventually(func(g gomega.Gomega) {
 					g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(betaMidWl), betaMidWl)).To(gomega.Succeed())
 					g.Expect(apimeta.FindStatusCondition(betaMidWl.Status.Conditions, kueue.WorkloadPreempted)).To(gomega.BeComparableTo(&metav1.Condition{
-						Type:    kueue.WorkloadPreempted,
-						Status:  metav1.ConditionFalse,
-						Reason:  "QuotaReserved",
-						Message: fmt.Sprintf("Previously: Preempted to accommodate a workload (UID: %s, JobUID: UNKNOWN) due to %s", alphaMidWl.UID, preemption.HumanReadablePreemptionReasons[kueue.InCohortReclamationReason]),
+						Type:               kueue.WorkloadPreempted,
+						Status:             metav1.ConditionFalse,
+						ObservedGeneration: betaMidWl.Generation,
+						Reason:             "QuotaReserved",
+						Message:            fmt.Sprintf("Previously: Preempted to accommodate a workload (UID: %s, JobUID: UNKNOWN) due to %s", alphaMidWl.UID, preemption.HumanReadablePreemptionReasons[kueue.InCohortReclamationReason]),
 					}, conditionCmpOpts))
 				}, util.Timeout, util.Interval).Should(gomega.Succeed())
 			})
diff --git a/test/integration/singlecluster/tas/tas_test.go b/test/integration/singlecluster/tas/tas_test.go
index ee944814e..b87cd71d1 100644
--- a/test/integration/singlecluster/tas/tas_test.go
+++ b/test/integration/singlecluster/tas/tas_test.go
@@ -37,7 +37,6 @@ import (
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/admissionchecks/provisioning"
 	"sigs.k8s.io/kueue/pkg/controller/tas"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/admissioncheck"
 	utiltas "sigs.k8s.io/kueue/pkg/util/tas"
 	"sigs.k8s.io/kueue/pkg/util/testing"
@@ -52,23 +51,19 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 	)
 
 	ginkgo.BeforeAll(func() {
-		_ = features.SetEnable(features.TASFailedNodeReplacement, true)
 		fwk.StartManager(ctx, cfg, managerSetup)
 	})
 
 	ginkgo.AfterAll(func() {
 		fwk.StopManager(ctx)
-		_ = features.SetEnable(features.TASFailedNodeReplacement, false)
 	})
 
 	ginkgo.BeforeEach(func() {
-		_ = features.SetEnable(features.TopologyAwareScheduling, true)
 		ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "tas-")
 	})
 
 	ginkgo.AfterEach(func() {
 		gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
-		_ = features.SetEnable(features.TopologyAwareScheduling, false)
 	})
 
 	ginkgo.When("Delete Topology", func() {
@@ -1230,8 +1225,8 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 							&kueue.TopologyAssignment{
 								Levels: []string{corev1.LabelHostname},
 								Domains: []kueue.TopologyDomainAssignment{
+									{Count: 1, Values: []string{"x1"}},
 									{Count: 1, Values: []string{"x2"}},
-									{Count: 1, Values: []string{"x3"}},
 								},
 							},
 						))
@@ -1422,8 +1417,6 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 
 				localQueue = testing.MakeLocalQueue("local-queue", ns.Name).ClusterQueue(clusterQueue.Name).Obj()
 				util.MustCreate(ctx, k8sClient, localQueue)
-
-				_ = features.SetEnable(features.TASFailedNodeReplacementFailFast, true)
 			})
 
 			ginkgo.AfterEach(func() {
@@ -1436,8 +1429,6 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				for _, node := range nodes {
 					util.ExpectObjectToBeDeleted(ctx, k8sClient, &node, true)
 				}
-
-				_ = features.SetEnable(features.TASFailedNodeReplacementFailFast, false)
 			})
 
 			ginkgo.It("should evict the Workload if no replacement is possible and place it on another rack", func() {
diff --git a/test/integration/singlecluster/webhook/core/clusterqueue_test.go b/test/integration/singlecluster/webhook/core/clusterqueue_test.go
index 0edcfb631..b84b62e0a 100644
--- a/test/integration/singlecluster/webhook/core/clusterqueue_test.go
+++ b/test/integration/singlecluster/webhook/core/clusterqueue_test.go
@@ -39,7 +39,7 @@ import (
 
 const (
 	resourcesMaxItems = 16
-	flavorsMaxItems   = 16
+	flavorsMaxItems   = 64
 )
 
 var _ = ginkgo.Describe("ClusterQueue Webhook", ginkgo.Ordered, func() {
diff --git a/test/integration/singlecluster/webhook/core/cohort_test.go b/test/integration/singlecluster/webhook/core/cohort_test.go
index cce83c3a6..841957e6a 100644
--- a/test/integration/singlecluster/webhook/core/cohort_test.go
+++ b/test/integration/singlecluster/webhook/core/cohort_test.go
@@ -18,6 +18,7 @@ package core
 
 import (
 	"context"
+	"fmt"
 
 	"github.com/onsi/ginkgo/v2"
 	"github.com/onsi/gomega"
@@ -84,45 +85,30 @@ var _ = ginkgo.Describe("Cohort Webhook", ginkgo.Ordered, func() {
 					Obj(),
 				testing.BeForbiddenError()),
 			ginkgo.Entry("Should reject too many flavors in resource group",
-				testing.MakeCohort("cohort").ResourceGroup(
-					*testing.MakeFlavorQuotas("f0").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f1").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f2").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f3").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f4").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f5").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f6").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f7").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f8").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f9").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f10").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f11").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f12").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f13").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f14").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f15").Resource(corev1.ResourceCPU).Obj(),
-					*testing.MakeFlavorQuotas("f16").Resource(corev1.ResourceCPU).Obj()).Obj(),
+				func() *kueue.Cohort {
+					var flavors []kueue.FlavorQuotas
+					for i := range 65 {
+						flavors = append(flavors,
+							*testing.MakeFlavorQuotas(fmt.Sprintf("f%d", i)).
+								Resource(corev1.ResourceCPU).
+								Obj(),
+						)
+					}
+					return testing.MakeCohort("cohort").
+						ResourceGroup(flavors...).
+						Obj()
+				}(),
 				testing.BeInvalidError()),
 			ginkgo.Entry("Should reject too many resources in resource group",
-				testing.MakeCohort("cohort").ResourceGroup(
-					*testing.MakeFlavorQuotas("flavor").
-						Resource("cpu0").
-						Resource("cpu1").
-						Resource("cpu2").
-						Resource("cpu3").
-						Resource("cpu4").
-						Resource("cpu5").
-						Resource("cpu6").
-						Resource("cpu7").
-						Resource("cpu8").
-						Resource("cpu9").
-						Resource("cpu10").
-						Resource("cpu11").
-						Resource("cpu12").
-						Resource("cpu13").
-						Resource("cpu14").
-						Resource("cpu15").
-						Resource("cpu16").Obj()).Obj(),
+				func() *kueue.Cohort {
+					fq := testing.MakeFlavorQuotas("flavor")
+					for i := range 17 {
+						fq = fq.Resource(corev1.ResourceName(fmt.Sprintf("cpu%d", i)))
+					}
+					return testing.MakeCohort("cohort").
+						ResourceGroup(*fq.Obj()).
+						Obj()
+				}(),
 				testing.BeInvalidError()),
 			ginkgo.Entry("Should allow resource with valid name",
 				testing.MakeCohort("cohort").
diff --git a/test/integration/singlecluster/webhook/core/workload_test.go b/test/integration/singlecluster/webhook/core/workload_test.go
index 0f0d4c7c7..c2f2ae2e5 100644
--- a/test/integration/singlecluster/webhook/core/workload_test.go
+++ b/test/integration/singlecluster/webhook/core/workload_test.go
@@ -807,23 +807,21 @@ var _ = ginkgo.Describe("Workload validating webhook", ginkgo.Ordered, func() {
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
 
-		ginkgo.It("Should forbid the change of spec.admission", func() {
+		ginkgo.It("Should forbid the change of status.admission", func() {
 			ginkgo.By("Creating a new Workload")
 			workload := testing.MakeWorkload(workloadName, ns.Name).Obj()
 			util.MustCreate(ctx, k8sClient, workload)
 
-			ginkgo.By("Admitting the Workload")
-			gomega.Eventually(func(g gomega.Gomega) {
-				var newWL kueue.Workload
-				g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(workload), &newWL)).To(gomega.Succeed())
-				newWL.Status.Admission = testing.MakeAdmission("cluster-queue").Obj()
-				g.Expect(k8sClient.Status().Update(ctx, &newWL)).Should(gomega.Succeed())
-			}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			ginkgo.By("Admitting the Workload", func() {
+				util.SetQuotaReservation(ctx, k8sClient, client.ObjectKeyFromObject(workload), testing.MakeAdmission("cluster-queue").Obj())
+				util.SyncAdmittedConditionForWorkloads(ctx, k8sClient, workload)
+			})
 
 			ginkgo.By("Updating queueName")
 			gomega.Eventually(func(g gomega.Gomega) {
 				var newWL kueue.Workload
 				g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(workload), &newWL)).To(gomega.Succeed())
+				g.Expect(newWL.Status.Admission).NotTo(gomega.BeNil())
 				newWL.Status.Admission.ClusterQueue = "foo-cluster-queue"
 				g.Expect(k8sClient.Status().Update(ctx, &newWL)).Should(testing.BeForbiddenError())
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
diff --git a/test/integration/singlecluster/webhook/jobs/jobset_webhook_test.go b/test/integration/singlecluster/webhook/jobs/jobset_webhook_test.go
index 9a419d2e0..d0614ca42 100644
--- a/test/integration/singlecluster/webhook/jobs/jobset_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/jobset_webhook_test.go
@@ -23,7 +23,6 @@ import (
 
 	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/jobset"
 	"sigs.k8s.io/kueue/test/util"
@@ -59,11 +58,7 @@ var _ = ginkgo.Describe("JobSet Webhook", ginkgo.Ordered, ginkgo.ContinueOnFailu
 		})
 	})
 
-	ginkgo.When("with TopologyAwareScheduling enabled", func() {
-		ginkgo.BeforeEach(func() {
-			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-		})
-
+	ginkgo.When("with TopologyAwareScheduling", func() {
 		ginkgo.It("shouldn't create JobSet if both kueue.x-k8s.io/podset-required-topology and kueue.x-k8s.io/podset-preferred-topology annotations are set", func() {
 			job := testingjob.MakeJobSet("jobset", ns.Name).
 				Queue("indexed_job").
diff --git a/test/integration/singlecluster/webhook/jobs/paddlejob_webhook_test.go b/test/integration/singlecluster/webhook/jobs/paddlejob_webhook_test.go
index 5deb49426..bde566b3f 100644
--- a/test/integration/singlecluster/webhook/jobs/paddlejob_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/paddlejob_webhook_test.go
@@ -24,7 +24,6 @@ import (
 
 	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/paddlejob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjobspaddlejob "sigs.k8s.io/kueue/pkg/util/testingjobs/paddlejob"
 	"sigs.k8s.io/kueue/test/util"
@@ -47,11 +46,7 @@ var _ = ginkgo.Describe("PaddleJob Webhook", ginkgo.Ordered, func() {
 		gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
 	})
 
-	ginkgo.When("with TopologyAwareScheduling enabled", func() {
-		ginkgo.BeforeEach(func() {
-			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-		})
-
+	ginkgo.When("with TopologyAwareScheduling", func() {
 		ginkgo.It("the creation doesn't succeed if job contain both kueue.x-k8s.io/podset-required-topology and kueue.x-k8s.io/podset-preferred-topology annotations", func() {
 			job := testingjobspaddlejob.MakePaddleJob("job", ns.Name).
 				PaddleReplicaSpecs(
diff --git a/test/integration/singlecluster/webhook/jobs/pytorchjob_webhook_test.go b/test/integration/singlecluster/webhook/jobs/pytorchjob_webhook_test.go
index e60d64ff8..93378ab1c 100644
--- a/test/integration/singlecluster/webhook/jobs/pytorchjob_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/pytorchjob_webhook_test.go
@@ -24,7 +24,6 @@ import (
 
 	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/pytorchjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjobspytorchjob "sigs.k8s.io/kueue/pkg/util/testingjobs/pytorchjob"
 	"sigs.k8s.io/kueue/test/util"
@@ -47,11 +46,7 @@ var _ = ginkgo.Describe("PyTorchJob Webhook", ginkgo.Ordered, func() {
 		gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
 	})
 
-	ginkgo.When("with TopologyAwareScheduling enabled", func() {
-		ginkgo.BeforeEach(func() {
-			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-		})
-
+	ginkgo.When("with TopologyAwareScheduling", func() {
 		ginkgo.It("the creation doesn't succeed if job contain both kueue.x-k8s.io/podset-required-topology and kueue.x-k8s.io/podset-preferred-topology annotations", func() {
 			job := testingjobspytorchjob.MakePyTorchJob("job", ns.Name).
 				PyTorchReplicaSpecs(
diff --git a/test/integration/singlecluster/webhook/jobs/tfjob_webhook_test.go b/test/integration/singlecluster/webhook/jobs/tfjob_webhook_test.go
index 6a7facb69..b67c0b672 100644
--- a/test/integration/singlecluster/webhook/jobs/tfjob_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/tfjob_webhook_test.go
@@ -24,7 +24,6 @@ import (
 
 	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/tfjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjobstfjob "sigs.k8s.io/kueue/pkg/util/testingjobs/tfjob"
 	"sigs.k8s.io/kueue/test/util"
@@ -47,11 +46,7 @@ var _ = ginkgo.Describe("TFJob Webhook", ginkgo.Ordered, func() {
 		gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
 	})
 
-	ginkgo.When("with TopologyAwareScheduling enabled", func() {
-		ginkgo.BeforeEach(func() {
-			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-		})
-
+	ginkgo.When("with TopologyAwareScheduling", func() {
 		ginkgo.It("the creation doesn't succeed if job contain both kueue.x-k8s.io/podset-required-topology and kueue.x-k8s.io/podset-preferred-topology annotations", func() {
 			job := testingjobstfjob.MakeTFJob("job", ns.Name).
 				TFReplicaSpecs(
diff --git a/test/integration/singlecluster/webhook/jobs/xgboostjob_webhook_test.go b/test/integration/singlecluster/webhook/jobs/xgboostjob_webhook_test.go
index 42a7455f6..e672a3f19 100644
--- a/test/integration/singlecluster/webhook/jobs/xgboostjob_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/xgboostjob_webhook_test.go
@@ -24,7 +24,6 @@ import (
 
 	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/xgboostjob"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjobsxgboostjob "sigs.k8s.io/kueue/pkg/util/testingjobs/xgboostjob"
 	"sigs.k8s.io/kueue/test/util"
@@ -47,11 +46,7 @@ var _ = ginkgo.Describe("XGBoostJob Webhook", ginkgo.Ordered, func() {
 		gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
 	})
 
-	ginkgo.When("with TopologyAwareScheduling enabled", func() {
-		ginkgo.BeforeEach(func() {
-			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.TopologyAwareScheduling, true)
-		})
-
+	ginkgo.When("with TopologyAwareScheduling", func() {
 		ginkgo.It("the creation doesn't succeed if job contain both kueue.x-k8s.io/podset-required-topology and kueue.x-k8s.io/podset-preferred-topology annotations", func() {
 			job := testingjobsxgboostjob.MakeXGBoostJob("job", ns.Name).
 				XGBReplicaSpecs(
diff --git a/test/util/util.go b/test/util/util.go
index 04dc61fee..059303e4d 100644
--- a/test/util/util.go
+++ b/test/util/util.go
@@ -607,6 +607,15 @@ func ExpectQuotaReservedWorkloadsTotalMetric(cq *kueue.ClusterQueue, workloadPri
 	expectCounterMetric(metric, v)
 }
 
+func ExpectQuotaReservedWaitTimeMetric(cq *kueue.ClusterQueue, workloadPriorityClass string, count int) {
+	gomega.EventuallyWithOffset(1, func(g gomega.Gomega) {
+		metric := metrics.QuotaReservedWaitTime.WithLabelValues(cq.Name, workloadPriorityClass)
+		v, err := testutil.GetHistogramMetricCount(metric)
+		g.Expect(err).ToNot(gomega.HaveOccurred())
+		g.Expect(int(v)).Should(gomega.Equal(count))
+	}, Timeout, Interval).Should(gomega.Succeed())
+}
+
 func expectCounterMetric(metric prometheus.Counter, count int) {
 	gomega.EventuallyWithOffset(2, func(g gomega.Gomega) {
 		v, err := testutil.GetCounterMetricValue(metric)